Created
March 2, 2014 06:50
-
-
Save philbritton/9302904 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Styles for the query builder container: query builder + results. */ | |
#queryBuilderContainer { | |
position: relative; | |
font-family: "Segoe UI", Helvetica, Verdana; | |
width: auto; | |
} | |
#queryBuilderContainer * { | |
margin: 0; | |
padding: 0; | |
box-sizing: border-box; | |
} | |
#queryBuilderContainer #queryBusy { | |
background: #606060 url(images/msls-loader-light.gif) no-repeat center center; | |
cursor: wait; | |
display: none; | |
position: fixed; | |
top: 0; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
z-index: 5000; | |
-moz-opacity: .35; | |
opacity: .35; | |
} | |
#queryBuilderContainer h1 { | |
font-weight: normal; | |
font-size: 2em; | |
line-height: 1em; | |
padding-bottom: 10px; | |
} | |
#queryBuilderContainer button { | |
border: 0; | |
} | |
#queryBuilderContainer .buttonQuery { | |
cursor: pointer; | |
margin-top: 15px; | |
padding: 10px; | |
width: 80px; | |
height: 35px; | |
} | |
#queryBuilderContainer #submitQuery { | |
background: #29ABE0; | |
color: #FFFFFF; | |
margin-right: 15px; | |
} | |
#queryBuilderContainer #clearQuery { | |
color: #000000; | |
background-color: #E6E6E6; | |
} | |
#queryBuilderContainer .wrongInput { | |
background-color: #f8a4a4; | |
} | |
#queryBuilderContainer .error { | |
color: #ff0000; | |
} | |
#queryBuilderContainer .navigationDropdown { | |
background-color: #d0ebfa; | |
} | |
/* Styles for the query builder. */ | |
#queryBuilder { | |
margin-bottom: 20px; | |
} | |
#queryBuilder .addCondition, #queryBuilder .removeCondition { | |
cursor: pointer; | |
width: 20px; | |
color: transparent; | |
height: 20px; | |
margin: 7px 3px 3px 9px; | |
padding: 0; | |
} | |
#queryBuilder .addCondition { | |
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7CAAAOwgEVKEqAAAAAB3RJTUUH3QsJATErA49PDwAAAgBJREFUOMut1T1IVWEYB/DfPfdKXxTRkFFD0KUhDUIotEYHl0KkoKlF+qBIoqGGloZoq6EPpaAiCGmIDDVaa4sgSYSyaIiiEEkSK4REr7flOXA63NsH9MCBw/s+z//5/r+Fjo4ONWQrdmMvmlGN8wJe4QEe4WXesJADXImTOI05PMczTMf9GrRhB5bgAi7hewpQyoCtQy/24V4oj6gt28PpOWxDDyazgKvRhz04hluYV19GcABPcDlKcRAzKWBP1Oswbvo7mcd1LOAGRnG+WC6Xm3EH93E204BUVmEX1mMqALIyhs04hKEEneHtIio1ItmCAdzG2hr3lbCdR2eCLjyNkOulVox6/6ijMxoYXQmaYjSyqa7AJjRGqgUk2IgN8TVk9KuB0VQKxS85j+3RnG9YHg6WRepVfEI33mZsppEkoVDxn6QUgI2588fYidnYiruRxX5MhM7nnM0aLJYwjtaoU1rHWbyL/4k4X8SHGkDpjrdhPMFgzFlLnSwaoiQLWFpHpyUwBhMMh9GpGI+8vI797q4TXTFsGzBcLJfLU9HpE/iIFzmDObyPu4UagEdwJohiIN3l3gi7Lxr1J3JIS3E0yGEAV0VkMIPjeIhr6A+K8hv66seV4IAefM3z4WTUaSy4rj1HsJXY5dYYpWKQyS8EW/iHJ6AaGaVPwBDe5A1/AgojgwuRXC6BAAAAAElFTkSuQmCC) no-repeat; | |
} | |
#queryBuilder .removeCondition { | |
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7CAAAOwgEVKEqAAAAAB3RJTUUH3QsJATEJ1u8O6wAAAbBJREFUOMut1V+LTVEYBvDf2bOPUmp8h9MwF0oSytVEdkfNRLmYK00ulFwpJWNEHYYo5gO40rggKRkX/tYkSaNc00l8hCEiMw43767V7nTmODy1L9ba63ne9b7rfdaqFUWhC0ZxEOPYhRy/sYolPMR9vKsShxqNRjpeh1O4hf34gdsxfoZP2IJJHIkgS/hVCuSJ2EbMx66e4yqe6I4iAl/CbhzGMmSxoI6bIXYOzR5i4l8z1o4Ht56mfBrHcR4X0LE2OngRaR/Dd7ysFUUxgrdRiyZW/B3qeITt2JbhEDZgdgAxwZnFMCYzTKCNRYNjMTQmcuzAjUrdhrE+6tMNGb7gW1LPpziaRw3aFcJFHOhxOEOYw/Vkro162Tar/hPyENtUmT8bhV4r5RQjWMmTdsmSFD/H1y8y7MObDAuhPvYPmY6FxkKGe/iKmdI+AzT2TGR0J4vTuYw9mB5AcDq4V/Cx9PJrbA1PdvCqDz/nsbMWHuAEOllin6m4OFvhzaKHWBFrWsGZKm2b3ofL4euTEfkx3sfd2E5aYy82R9ucwTX8LEVqfTwBO5PA6RNwFx+qxD9+2m672NnhxQAAAABJRU5ErkJggg==) no-repeat; | |
} | |
#queryBuilder #queryUrl { | |
font-size: 80%; | |
} | |
#queryBuilderForm select { | |
padding: 2px; | |
margin: 5px; | |
} | |
#queryBuilderForm input { | |
padding: 3px; | |
margin: 5px; | |
width: inherit; | |
} | |
#queryBuilderForm input[type=text] { | |
min-width: 250px; | |
} | |
#queryBuilder label { | |
display: inline-block; | |
padding-top: 4px; | |
min-width: 100px; | |
/* Without this one FF and Chrome wil put the label on top of the buttons and they will make them not clickable. */ | |
z-index: -1; | |
} | |
#queryBuilderForm #orderByFiltersList, | |
#queryBuilderForm #selectFiltersList, | |
#queryBuilderForm #expandFiltersList { | |
display: none; | |
} | |
#queryBuilderForm #orderByConditions.listVisible span, | |
#queryBuilderForm #selectConditions.listVisible span, | |
#queryBuilderForm #expandConditions.listVisible span { | |
overflow-y: auto; | |
overflow-x: hidden; | |
max-height: 200px; | |
width: auto; | |
display: inline-block; | |
padding-bottom: 10px; | |
padding-right: 10px; | |
} | |
#queryBuilderForm .listVisible button { | |
vertical-align: top; | |
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7CAAAOwgEVKEqAAAAAB3RJTUUH3QsJATEJ1u8O6wAAAbBJREFUOMut1V+LTVEYBvDf2bOPUmp8h9MwF0oSytVEdkfNRLmYK00ulFwpJWNEHYYo5gO40rggKRkX/tYkSaNc00l8hCEiMw43767V7nTmODy1L9ba63ne9b7rfdaqFUWhC0ZxEOPYhRy/sYolPMR9vKsShxqNRjpeh1O4hf34gdsxfoZP2IJJHIkgS/hVCuSJ2EbMx66e4yqe6I4iAl/CbhzGMmSxoI6bIXYOzR5i4l8z1o4Ht56mfBrHcR4X0LE2OngRaR/Dd7ysFUUxgrdRiyZW/B3qeITt2JbhEDZgdgAxwZnFMCYzTKCNRYNjMTQmcuzAjUrdhrE+6tMNGb7gW1LPpziaRw3aFcJFHOhxOEOYw/Vkro162Tar/hPyENtUmT8bhV4r5RQjWMmTdsmSFD/H1y8y7MObDAuhPvYPmY6FxkKGe/iKmdI+AzT2TGR0J4vTuYw9mB5AcDq4V/Cx9PJrbA1PdvCqDz/nsbMWHuAEOllin6m4OFvhzaKHWBFrWsGZKm2b3ofL4euTEfkx3sfd2E5aYy82R9ucwTX8LEVqfTwBO5PA6RNwFx+qxD9+2m672NnhxQAAAABJRU5ErkJggg==) no-repeat; | |
} | |
#queryBuilderForm .filterList > label { | |
display: block; | |
margin-right: 20px; | |
white-space: nowrap; | |
} | |
#queryBuilderForm .filterContainer { | |
padding-left: 68px; | |
position: relative; | |
} | |
#queryBuilderForm .filterLabel { | |
display: inline-block; | |
float: left; | |
position: absolute; | |
left: 0; | |
vertical-align: top; | |
} | |
/* Styles for the results. */ | |
#results { | |
margin-top: 10px; | |
} | |
#results table.defaultResultsFormatting { | |
border-collapse: collapse; | |
} | |
#results table.defaultResultsFormatting tbody tr:not(.expandedChild):hover { | |
background-color: #DAECEF; | |
} | |
#results table.defaultResultsFormatting th { | |
background: #E6E6E6; | |
color: #000000; | |
} | |
#results table.defaultResultsFormatting th, #results table.defaultResultsFormatting td { | |
border: 0; | |
padding: 2px 3px; | |
text-align: left; | |
} | |
#results td table.defaultResultsFormatting th, #results td table.defaultResultsFormatting td { | |
padding: 1px 2px; | |
} | |
#results table.defaultResultsFormatting tr:not(.expandedChild) > td { | |
max-width: 300px; | |
max-height: 50px; | |
overflow: hidden; | |
text-overflow: ellipsis; | |
white-space: nowrap; | |
} | |
#results table.defaultResultsFormatting .expandedChild > td { | |
padding: 0 0 0 50px; | |
position: relative; | |
min-height: 30px; | |
} | |
#results table.defaultResultsFormatting .expandedChild > td table { | |
margin-bottom: 8px; | |
} | |
#results .expandChild { | |
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJCAYAAADgkQYQAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7CAAAOwgEVKEqAAAAAB3RJTUUH3QsJATIB8xnVGgAAADZJREFUGNONzLENACAMxECzwY+Y/ZcwBSUixPXJ0FRVAk7AQepqgL/DjR7ACXAC/AOQJC1I4gauvkEq2U4JQwAAAABJRU5ErkJggg==) no-repeat; | |
display: inline-block; | |
width: 12px; | |
height: 14px; | |
} | |
#results .expandChild.collapsed { | |
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAMCAYAAACulacQAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7CAAAOwgEVKEqAAAAAB3RJTUUH3QsJATIPFKH4HQAAAGpJREFUGNN9kNENwCAIRA/DQG6gIziKTiIruJETlX6ZgrW9P/JeDgJyzlprVVUlbOGUEkopaK1dAJzAABBjXLOKCDn4JbCt2QXej7BCwE9ecM6JMQZ67yGcgIgQET21FrhaW+V2rPedDroBb3kzlWXCo2UAAAAASUVORK5CYII=) no-repeat; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html> | |
<meta http-equiv="X-UA-Compatible" content="IE=10" /> | |
<title>OData Query Builder</title> | |
<style type="text/css"> | |
body | |
{ | |
font-family: "Segoe UI", Helvetica, Verdana; | |
padding: 20px; | |
} | |
#results { | |
white-space: pre; | |
} | |
</style> | |
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-2.0.0.min.js"></script> | |
<!-- The following two libraries are included in the JavaScript and CSS section of the jsbin. | |
<script src="Scripts/datajs-1.1.1.min.js"></script> | |
<script src="Scripts/odata-query-builder.js"></script> | |
<link type="text/css" href="Content/odata-query-builder.css" rel="stylesheet" />--> | |
<script> | |
// Document ready event handler. | |
$(document).ready(function () { | |
var createdQueryBuilder = new OData.explorer.DataExplorer( | |
{ | |
// An array containing the different endpoints. | |
endpoints: [ | |
{ | |
name: 'OData', | |
url: 'http://dacgrouptools.cloudapp.net:8181/mscrmconnector/odata.rsc?%40authtoken=1b1A2c1p8E6y3r5F2r0x&%24callback=parseFunction&%24format=json', | |
provider: function () { return ODataMetadata; } | |
} | |
], | |
encodeUrlComponents: true, | |
hideOrderbyFilters: true, | |
hideColumnFilters: true, | |
hideExpandFilters: true, | |
// Overwrite the default onUrlChange callback. | |
onUrlChange: function (url) { | |
console.log(url); | |
}, | |
// Overwrite the default onSubmit callback. | |
onSubmit: function (url) { | |
// If nothing or false is returned we will | |
// not execute any query to fetch the data for you. | |
return url; | |
}, | |
// Overwrite the default onResults callback. | |
onResults: function (data) { | |
// If nothing or false is returned we will | |
// not display the results in the #reulst container for you. | |
return (JSON.stringify(data, null, 4)); | |
}, | |
// Overwrite the default onError callback. | |
onError: function (error, url) { | |
console.log(url + '\t' + JSON.stringify(error)); | |
} | |
}); | |
}); | |
</script> | |
<body> | |
<h1>OData Query Builder</h1> | |
<p>press "RUN WITH JS"</p> | |
<div id="queryBuilderContainer"> | |
</div> | |
</body> | |
</html> | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright (c) Microsoft. All rights reserved. | |
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation | |
// files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, | |
// modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the | |
// Software is furnished to do so, subject to the following conditions: | |
// | |
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. | |
// | |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE | |
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR | |
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
(function(n,t){var yt=n.datajs||{},r=n.OData||{},lo,ao,ei,ay,rp;typeof define=="function"&&define.amd?(define("datajs",yt),define("OData",r)):(n.datajs=yt,n.OData=r),yt.version={major:1,minor:1,build:1};var ko=function(t){return n.ActiveXObject?new n.ActiveXObject(t):null},ot=function(n){return n!==null&&n!==t},er=function(n,t){for(var i=0,r=n.length;i<r;i++)if(n[i]===t)return!0;return!1},it=function(n,i){return n!==t?n:i},o=function(t){if(arguments.length===1){n.setTimeout(t,0);return}var i=Array.prototype.slice.call(arguments,1);n.setTimeout(function(){t.apply(this,i)},0)},g=function(n,t){for(var i in t)n[i]=t[i];return n},or=function(n,t){if(n)for(var i=0,r=n.length;i<r;i++)if(t(n[i]))return n[i];return null},e=function(n){return Object.prototype.toString.call(n)==="[object Array]"},cf=function(n){return Object.prototype.toString.call(n)==="[object Date]"},lf=function(n){return typeof n=="object"},s=function(n){return parseInt(n,10)},ru=function(n,t,i){n.hasOwnProperty(t)&&(n[i]=n[t],delete n[t])},uu=function(n){throw n;},go=function(n){return n.trim?n.trim():n.replace(/^\s+|\s+$/g,"")},af=function(n,i){return n!==t?n:i},up=/^([^:\/?#]+:)?(\/\/[^\/?#]*)?([^?#:]+)?(\?[^#]*)?(#.*)?/,ns=["scheme","authority","path","query","fragment"],vf=function(n){var i={isAbsolute:!1},r,t,u;if(n){if(r=up.exec(n),r)for(t=0,u=ns.length;t<u;t++)r[t+1]&&(i[ns[t]]=r[t+1]);i.scheme&&(i.isAbsolute=!0)}return i},ts=function(n){return"".concat(n.scheme||"",n.authority||"",n.path||"",n.query||"",n.fragment||"")},fp=/^\/{0,2}(?:([^@]*)@)?([^:]+)(?::{1}(\d+))?/,ep=/%[0-9A-F]{2}/ig,op=function(n){var i=vf(n),r=i.scheme,u=i.authority,t;return r&&(i.scheme=r.toLowerCase(),u&&(t=fp.exec(u),t&&(i.authority="//"+(t[1]?t[1]+"@":"")+t[2].toLowerCase()+(t[3]?":"+t[3]:"")))),n=ts(i),n.replace(ep,function(n){return n.toLowerCase()})},c=function(n,t){var i,u,r,f;return t?(i=vf(n),i.isAbsolute)?n:(u=vf(t),r={},i.authority?(r.authority=i.authority,f=i.path,r.query=i.query):(i.path?(f=i.path.charAt(0)==="/"?i.path:sp(i.path,u.path),r.query=i.query):(f=u.path,r.query=i.query||u.query),r.authority=u.authority),r.path=hp(f),r.scheme=u.scheme,r.fragment=i.fragment,ts(r)):n},sp=function(n,t){var i="/",r;return t&&(r=t.lastIndexOf("/"),i=t.substring(0,r),i.charAt(i.length-1)!=="/"&&(i=i+"/")),i+n},hp=function(n){for(var t="",r="",i;n;)n.indexOf("..")===0||n.indexOf(".")===0?n=n.replace(/^\.\.?\/?/g,""):n.indexOf("/..")===0?(n=n.replace(/^\/\..\/?/g,"/"),i=t.lastIndexOf("/"),t=i===-1?"":t.substring(0,i)):n.indexOf("/.")===0?n=n.replace(/^\/\.\/?/g,"/"):(r=n,i=n.indexOf("/",1),i!==-1&&(r=n.substring(0,i)),t=t+r,n=n.replace(r,""));return t},cp=function(i){var r=[],o,u,f,s,e,h;if(n.atob===t)r=lp(i);else for(o=n.atob(i),u=0;u<o.length;u++)r.push(o.charCodeAt(u));for(f="",s="0123456789ABCDEF",e=0;e<r.length;e++)h=r[e],f+=s[h>>4],f+=s[h&15];return f},lp=function(n){for(var i="",r,u,f,e,o,t=0;t<n.length;t++)r=ap(n[t]),u="",r!==null&&(u=r.toString(2),i+=vp(u));for(f=[],e=parseInt(i.length/8,10),t=0;t<e;t++)o=parseInt(i.substring(t*8,(t+1)*8),2),f.push(o);return f},ap=function(n){var t=n.charCodeAt(0),i=65,r=6;return t>=65&&t<=90?t-i:t>=97&&t<=122?t-i-r:t>=48&&t<=57?t+4:n=="+"?62:n=="/"?63:null},vp=function(n){while(n.length<6)n="0"+n;return n},fu="http://",sr=fu+"www.w3.org/",is=sr+"1999/xhtml",hr=sr+"2000/xmlns/",pi=sr+"XML/1998/namespace",rs=fu+"www.mozilla.org/newlayout/xml/parsererror.xml",yp=function(n){var t=/(^\s)|(\s$)/;return t.test(n)},pp=function(n){var t=/^\s*$/;return n===null||t.test(n)},wp=function(n){while(n!==null&&n.nodeType===1){var t=st(n,"space",pi);if(t==="preserve")return!0;if(t==="default")break;else n=n.parentNode}return!1},bp=function(n){var t=n.nodeName;return t=="xmlns"||t.indexOf("xmlns:")===0},eu=function(n,t,i){try{n.setProperty(t,i)}catch(r){}},kp=function(){var n=ko("Msxml2.DOMDocument.3.0");return n&&(eu(n,"ProhibitDTD",!0),eu(n,"MaxElementDepth",256),eu(n,"AllowDocumentFunction",!1),eu(n,"AllowXsltScript",!1)),n},us=function(){try{var n=ko("Msxml2.DOMDocument.6.0");return n&&(n.async=!0),n}catch(t){return kp()}},dp=function(n){var t=us(),i;return t?(t.loadXML(n),i=t.parseError,i.errorCode!==0&&cr(i.reason,i.srcText,n),t):null},cr=function(n,t,i){typeof n=="string"&&(n={message:n});throw g(n,{srcText:t||"",errorXmlText:i||""});},ou=function(t){var s=n.DOMParser&&new n.DOMParser,r,e,l;if(!s)return r=dp(t),r||cr("XML DOM parser not supported"),r;try{r=s.parseFromString(t,"text/xml")}catch(v){cr(v,"",t)}var i=r.documentElement,h=i.namespaceURI,c=f(i);if(c==="parsererror"&&h===rs&&(e=b(i,rs,"sourcetext"),l=e?ki(e):"",cr(k(i)||"",l,t)),c==="h3"&&h===is||tw(i,is,"h3")){for(var o="",a=[],u=i.firstChild;u;)u.nodeType===1&&(o+=k(u)||""),a.push(u.nextSibling),u=u.firstChild||a.shift();o+=k(i)||"",cr(o,"",t)}return r},pt=function(n,t){return n?n+":"+t:t},gp=function(n,t){if(yp(t.data)){var i=bi(n,pi,"space");i||(i=si(n.ownerDocument,pi,pt("xml","space")),l(n,i)),i.value="preserve"}return n.appendChild(t),n},wi=function(n,t){for(var r=n.attributes,i=0,u=r.length;i<u;i++)t(r.item(i))},st=function(n,t,i){var r=bi(n,t,i);return r?ki(r):null},bi=function(n,t,i){var r=n.attributes;return r.getNamedItemNS?r.getNamedItemNS(i||null,t):r.getQualifiedItem(t,i)||null},rt=function(n,t){var i=bi(n,"base",pi);return(i?c(i.value,t):t)||null},p=function(n,t){yf(n,!1,function(n){return n.nodeType===1&&t(n),!0})},fs=function(n,t,i){for(var u=i.split("/"),r=0,f=u.length;r<f;r++)n=n&&b(n,t,u[r]);return n||null},nw=function(n,t,i){var f=i.lastIndexOf("/"),r=i.substring(f+1),e=i.substring(0,f),u=e?fs(n,t,e):n;return u?r.charAt(0)==="@"?bi(u,r.substring(1),t):b(u,t,r):null},b=function(n,t,i){return es(n,t,i,!1)},tw=function(n,t,i){if(n.getElementsByTagNameNS){var r=n.getElementsByTagNameNS(t,i);return r.length>0?r[0]:null}return es(n,t,i,!0)},es=function(n,t,i,r){var e=null;return yf(n,r,function(n){if(n.nodeType===1){var r=!t||u(n)===t,o=!i||f(n)===i;r&&o&&(e=n)}return e===null}),e},k=function(n){var i=null,r=n.nodeType===9&&n.documentElement?n.documentElement:n,f=r.ownerDocument.preserveWhiteSpace===!1,u;return yf(r,!1,function(n){if(n.nodeType===3||n.nodeType===4){var e=ki(n),o=f||!pp(e);o||(u===t&&(u=wp(r)),o=u),o&&(i?i+=e:i=e)}return!0}),i},f=function(n){return n.localName||n.baseName},u=function(n){return n.namespaceURI||null},ki=function(n){return n.nodeType===1?k(n):n.nodeValue},yf=function(n,t,i){for(var f=[],r=n.firstChild,u=!0;r&&u;)u=i(r),u&&(t&&r.firstChild&&f.push(r.firstChild),r=r.nextSibling||f.shift())},iw=function(n,t,i){for(var r=n.nextSibling,e,o;r;){if(r.nodeType===1&&(e=!t||u(r)===t,o=!i||f(r)===i,e&&o))return r;r=r.nextSibling}return null},os=function(){var t=n.document.implementation;return t&&t.createDocument?t.createDocument(null,null,null):us()},su=function(n,t){if(!e(t))return l(n,t);for(var i=0,r=t.length;i<r;i++)t[i]&&l(n,t[i]);return n},l=function(n,t){if(t){if(typeof t=="string")return gp(n,uw(n.ownerDocument,t));t.nodeType===2?n.setAttributeNodeNS?n.setAttributeNodeNS(t):n.setAttributeNode(t):n.appendChild(t)}return n},si=function(n,i,r,u){var f=n.createAttributeNS&&n.createAttributeNS(i,r)||n.createNode(2,r,i||t);return f.value=u||"",f},lr=function(n,i,r,u){var f=n.createElementNS&&n.createElementNS(i,r)||n.createNode(1,r,i||t);return su(f,u||[])},ss=function(n,t,i){return si(n,hr,pt("xmlns",i),t)},rw=function(n,t){for(var f="<c>"+t+"<\/c>",e=ou(f),r=e.documentElement,o=("importNode"in n)?n.importNode(r,!0):r,u=n.createDocumentFragment(),i=o.firstChild;i;)u.appendChild(i),i=i.nextSibling;return u},uw=function(n,t){return n.createTextNode(t)},fw=function(n,t,i,r,u){for(var f="",h=u.split("/"),c=b,a=lr,o=t,e,s=0,v=h.length;s<v;s++)f=h[s],f.charAt(0)==="@"&&(f=f.substring(1),c=bi,a=si),e=c(o,i,f),e||(e=a(n,i,pt(r,f)),l(o,e)),o=e;return o},pf=function(t){var i=n.XMLSerializer,r;if(i)return r=new i,r.serializeToString(t);if(t.xml)return t.xml;throw{message:"XML serialization unsupported"};},ew=function(n){var f=n.childNodes,t,r=f.length,i;if(r===0)return"";var e=n.ownerDocument,o=e.createDocumentFragment(),u=e.createElement("c");for(o.appendChild(u),t=0;t<r;t++)u.appendChild(f[t]);for(i=pf(o),i=i.substr(3,i.length-7),t=0;t<r;t++)n.appendChild(u.childNodes[t]);return i},kit=function(i){var r=i.xml,u;if(r!==t)return r;if(n.XMLSerializer)return u=new n.XMLSerializer,u.serializeToString(i);throw{message:"XML serialization unsupported"};},ow=function(n,t,i){return function(){return n[t].apply(n,arguments),i}},di=function(){this._arguments=t,this._done=t,this._fail=t,this._resolved=!1,this._rejected=!1};di.prototype={then:function(n,t){return n&&(this._done?this._done.push(n):this._done=[n]),t&&(this._fail?this._fail.push(t):this._fail=[t]),this._resolved?this.resolve.apply(this,this._arguments):this._rejected&&this.reject.apply(this,this._arguments),this},resolve:function(){if(this._done){for(var n=0,i=this._done.length;n<i;n++)this._done[n].apply(null,arguments);this._done=t,this._resolved=!1,this._arguments=t}else this._resolved=!0,this._arguments=arguments},reject:function(){if(this._fail){for(var n=0,i=this._fail.length;n<i;n++)this._fail[n].apply(null,arguments);this._fail=t,this._rejected=!1,this._arguments=t}else this._rejected=!0,this._arguments=arguments},promise:function(){var n={};return n.then=ow(this,"then",n),n}};var hu=function(){return n.jQuery&&n.jQuery.Deferred?new n.jQuery.Deferred:new di},hs=function(n,t){var i=(n&&n.__metadata||{}).type;return i||(t?t.type:null)},v="Edm.",cs=v+"Binary",ls=v+"Boolean",as=v+"Byte",cu=v+"DateTime",lu=v+"DateTimeOffset",vs=v+"Decimal",ys=v+"Double",ps=v+"Guid",ws=v+"Int16",bs=v+"Int32",ks=v+"Int64",ds=v+"SByte",gs=v+"Single",ar=v+"String",au=v+"Time",ht=v+"Geography",nh=ht+"Point",th=ht+"LineString",ih=ht+"Polygon",rh=ht+"Collection",uh=ht+"MultiPolygon",fh=ht+"MultiLineString",eh=ht+"MultiPoint",et=v+"Geometry",oh=et+"Point",sh=et+"LineString",hh=et+"Polygon",ch=et+"Collection",lh=et+"MultiPolygon",ah=et+"MultiLineString",vh=et+"MultiPoint",wf="Point",bf="LineString",kf="Polygon",df="MultiPoint",gf="MultiLineString",ne="MultiPolygon",te="GeometryCollection",sw=[ar,bs,ks,ls,ys,gs,cu,lu,au,vs,ps,as,ws,ds,cs],hw=[et,oh,sh,hh,ch,lh,ah,vh],cw=[ht,nh,th,ih,rh,uh,fh,eh],hi=function(n,t){if(!n)return null;if(e(n)){for(var r,i=0,u=n.length;i<u;i++)if(r=hi(n[i],t),r)return r;return null}return n.dataServices?hi(n.dataServices.schema,t):t(n)},yh=function(n,t){return n=n===0?"":"."+a(n.toString(),3),t>0&&(n===""&&(n=".000"),n+=a(t.toString(),4)),n},ph=function(n){var u,t,e;if(typeof n=="string")return n;if(u=yw(n),t=bh(n.__offset),u&&t!=="Z"){n=new Date(n.valueOf());var i=oc(t),o=n.getUTCHours()+i.d*i.h,s=n.getUTCMinutes()+i.d*i.m;n.setUTCHours(o,s)}else u||(t="");var r=n.getUTCFullYear(),h=n.getUTCMonth()+1,f="";return r<=0&&(r=-(r-1),f="-"),e=yh(n.getUTCMilliseconds(),n.__ns),f+a(r,4)+"-"+a(h,2)+"-"+a(n.getUTCDate(),2)+"T"+a(n.getUTCHours(),2)+":"+a(n.getUTCMinutes(),2)+":"+a(n.getUTCSeconds(),2)+e+t},wh=function(n){var t=n.ms,e="",i,r,u,f;return t<0&&(e="-",t=-t),i=Math.floor(t/864e5),t-=864e5*i,r=Math.floor(t/36e5),t-=36e5*r,u=Math.floor(t/6e4),t-=6e4*u,f=Math.floor(t/1e3),t-=f*1e3,e+"P"+a(i,2)+"DT"+a(r,2)+"H"+a(u,2)+"M"+a(f,2)+yh(t,n.ns)+"S"},a=function(n,t,i){for(var r=n.toString(10);r.length<t;)i?r+="0":r="0"+r;return r},bh=function(n){return!n||n==="Z"||n==="+00:00"||n==="-00:00"?"Z":n},vu=function(n){if(typeof n=="string"){var t=n.indexOf(")",10);if(n.indexOf("Collection(")===0&&t>0)return n.substring(11,t)}return null},lw=function(n,i,r,u,f,e){return f.request(n,function(f){try{f.headers&&ee(f.headers),f.data===t&&f.statusCode!==204&&u.read(f,e)}catch(o){o.request===t&&(o.request=n),o.response===t&&(o.response=f),r(o);return}i(f.data,f)},r)},aw=function(n){return d(n)&&e(n.__batchRequests)},vw=/Collection\((.*)\)/,kh=function(n,t){var i=n&&n.results||n;return!!i&&yu(t)||!t&&e(i)&&!d(i[0])},yu=function(n){return vw.test(n)},d=function(n){return!!n&&lf(n)&&!e(n)&&!cf(n)},yw=function(n){return n.__edmType==="Edm.DateTimeOffset"||!n.__edmType&&n.__offset},dh=function(n){if(!n&&!d(n))return!1;var t=n.__metadata||{},i=n.__deferred||{};return!t.type&&!!i.uri},gh=function(n){return d(n)&&n.__metadata&&"uri"in n.__metadata},vr=function(n,t){var i=n&&n.results||n;return e(i)&&!yu(t)&&d(i[0])},ie=function(n){return er(cw,n)},re=function(n){return er(hw,n)},nc=function(n){if(!n&&!d(n))return!1;var i=n.__metadata,t=n.__mediaresource;return!i&&!!t&&!!t.media_src},pu=function(n){return cf(n)||typeof n=="string"||typeof n=="number"||typeof n=="boolean"},ue=function(n){return er(sw,n)},tc=function(n,i){return dh(n)?"deferred":gh(n)?"entry":vr(n)?"feed":i&&i.relationship?n===null||n===t||!vr(n)?"entry":"feed":null},wt=function(n,t){return or(n,function(n){return n.name===t})},fe=function(n,t,i){return n?hi(t,function(t){return gw(n,t,i)}):null},pw=function(n,t){return or(n,function(n){return n.name===t})},gi=function(n,t){return fe(n,t,"complexType")},bt=function(n,t){return fe(n,t,"entityType")},ic=function(n){return hi(n,function(n){return or(n.entityContainer,function(n){return oe(n.isDefaultEntityContainer)})})},rc=function(n,t){return fe(n,t,"entityContainer")},ww=function(n,t){return or(n,function(n){return n.name===t})},bw=function(n,t){var u=null,f,i,r;return n&&(f=n.relationship,i=hi(t,function(n){var r=uc(n.namespace,f),i=n.association,t,u;if(r&&i)for(t=0,u=i.length;t<u;t++)if(i[t].name===r)return i[t];return null}),i&&(r=i.end[0],r.role!==n.toRole&&(r=i.end[1]),u=r.type)),u},kw=function(n,t,i){if(n){var u=n.relationship,r=hi(i,function(n){for(var f=n.entityContainer,t,i,r=0;r<f.length;r++)if(t=f[r].associationSet,t)for(i=0;i<t.length;i++)if(t[i].association==u)return t[i];return null});if(r&&r.end[0]&&r.end[1])return r.end[0].entitySet==t?r.end[1].entitySet:r.end[0].entitySet}return null},dw=function(n,t){return hi(t,function(t){for(var f=t.entityContainer,r,u,i=0;i<f.length;i++)if(r=f[i].entitySet,r)for(u=0;u<r.length;u++)if(r[u].name==n)return{entitySet:r[u],containerName:f[i].name,functionImport:f[i].functionImport};return null})},uc=function(n,t){return t.indexOf(n)===0&&t.charAt(n.length)==="."?t.substr(n.length+1):null},gw=function(n,t,i){if(n&&t){var r=uc(t.namespace,n);if(r)return or(t[i],function(n){return n.name===r})}return null},ct=function(n,t){var i,f,e;if(n===t)return n;var r=n.split("."),u=t.split("."),o=r.length>=u.length?r.length:u.length;for(i=0;i<o;i++){if(f=r[i]&&s(r[i]),e=u[i]&&s(u[i]),f>e)return n;if(f<e)return t}},nb={accept:"Accept","content-type":"Content-Type",dataserviceversion:"DataServiceVersion",maxdataserviceversion:"MaxDataServiceVersion"},ee=function(n){var t,r,i,u;for(t in n)r=t.toLowerCase(),i=nb[r],i&&t!==i&&(u=n[t],delete n[t],n[i]=u)},oe=function(n){return typeof n=="boolean"?n:typeof n=="string"&&n.toLowerCase()==="true"},tb=/^(-?\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?(?:\.(\d+))?(.*)$/,fc=function(n,t,i){var r=tb.exec(n),o=r?bh(r[8]):null,h,u,e,c,l,f;if(!r||!t&&o!=="Z"){if(i)return null;throw{message:"Invalid date/time value"};}if(h=s(r[1]),h<=0&&h++,u=r[7],e=0,u){if(u.length>7){if(i)return null;throw{message:"Cannot parse date/time value to given precision."};}e=a(u.substring(3),4,!0),u=a(u.substring(0,3),3,!0),u=s(u),e=s(e)}else u=0;var v=s(r[4]),y=s(r[5]),p=s(r[6])||0;if(o!=="Z"&&(c=oc(o),l=-c.d,v+=c.h*l,y+=c.m*l),f=new Date,f.setUTCFullYear(h,s(r[2])-1,s(r[3])),f.setUTCHours(v,y,p,u),isNaN(f.valueOf())){if(i)return null;throw{message:"Invalid date/time value"};}return t&&(f.__edmType="Edm.DateTimeOffset",f.__offset=o),e&&(f.__ns=e),f},wu=function(n,t){return fc(n,!1,t)},se=function(n,t){return fc(n,!0,t)},ec=/^([+-])?P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)(?:\.(\d+))?S)?)?/,dit=function(n){ec.test(n)},he=function(n){var i=ec.exec(n),t,r,u;if(i===null)throw{message:"Invalid duration value."};var f=i[2]||"0",e=i[3]||"0",o=s(i[4]||0),h=s(i[5]||0),c=s(i[6]||0),l=parseFloat(i[7]||0);if(f!=="0"||e!=="0")throw{message:"Unsupported duration value."};if(t=i[8],r=0,t){if(t.length>7)throw{message:"Cannot parse duration value to given precision."};r=a(t.substring(3),4,!0),t=a(t.substring(0,3),3,!0),t=s(t),r=s(r)}else t=0;return t+=l*1e3+c*6e4+h*36e5+o*864e5,i[1]==="-"&&(t=-t),u={ms:t,__edmType:"Edm.Time"},r&&(u.ns=r),u},oc=function(n){var t=n.substring(0,1),i,r;return t=t==="+"?1:-1,i=s(n.substring(1)),r=s(n.substring(n.indexOf(":")+1)),{d:t,h:i,m:r}},sc=function(n,i,r){n.method||(n.method="GET"),n.headers?ee(n.headers):n.headers={},n.headers.Accept===t&&(n.headers.Accept=i.accept),ot(n.data)&&n.body===t&&i.write(n,r),ot(n.headers.MaxDataServiceVersion)||(n.headers.MaxDataServiceVersion=i.maxDataServiceVersion||"1.0")},hc=function(n,i,r){var u,e,f;if(n&&typeof n=="object")for(u in n)e=n[u],f=hc(e,u,r),f=r(u,f,i),f!==e&&(e===t?delete n[u]:n[u]=f);return n},ib=function(n,t){return t("",hc(n,"",t))},bu=0,rb=function(n){return n.method&&n.method!=="GET"?!1:!0},ub=function(t){var i=n.document.createElement("IFRAME");i.style.display="none";var r=t.replace(/&/g,"&").replace(/"/g,""").replace(/\</g,"<"),u='<html><head><script type="text/javascript" src="'+r+'"><\/script><\/head><body><\/body><\/html>',f=n.document.getElementsByTagName("BODY")[0];return f.appendChild(i),cc(i,u),i},fb=function(){if(n.XMLHttpRequest)return new n.XMLHttpRequest;var t;if(n.ActiveXObject)try{return new n.ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(i){try{return new n.ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(r){t=r}}else t={message:"XMLHttpRequest not supported"};throw t;},eb=function(n){return n.indexOf("http://")===0||n.indexOf("https://")===0||n.indexOf("file://")===0},ob=function(t){if(!eb(t))return!0;var i=n.location,r=i.protocol+"//"+i.host+"/";return t.indexOf(r)===0},sb=function(i,r){try{delete n[i]}catch(u){n[i]=t,r===bu-1&&(bu-=1)}},ce=function(n){return n&&(cc(n,""),n.parentNode.removeChild(n)),null},hb=function(n,t){for(var r=n.getAllResponseHeaders().split(/\r?\n/),u,i=0,f=r.length;i<f;i++)r[i]&&(u=r[i].split(": "),t[u[0]]=u[1])},cc=function(n,t){var i=n.contentWindow?n.contentWindow.document:n.contentDocument.document;i.open(),i.write(t),i.close()};r.defaultHttpClient={callbackParameterName:"$callback",formatQueryString:"$format=json",enableJsonpCallback:!1,request:function(i,r,u){var y={},f=null,s=!1,h,a,w,b,k,d,l,v;y.abort=function(){(h=ce(h),s)||(s=!0,f&&(f.abort(),f=null),u({message:"Request aborted"}))};var p=function(){h=ce(h),s||(s=!0,f=null,u({message:"Request timed out"}))},c,e=i.requestUri,g=it(i.enableJsonpCallback,this.enableJsonpCallback),nt=it(i.callbackParameterName,this.callbackParameterName),tt=it(i.formatQueryString,this.formatQueryString);if(!g||ob(e)){if(f=fb(),f.onreadystatechange=function(){var t,n,o,h;s||f===null||f.readyState!==4||(t=f.statusText,n=f.status,n===1223&&(n=204,t="No Content"),o=[],hb(f,o),h={requestUri:e,statusCode:n,statusText:t,headers:o,body:f.responseText},s=!0,f=null,n>=200&&n<=299?r(h):u({message:"HTTP request failed",request:i,response:h}))},f.open(i.method||"GET",e,!0,i.user,i.password),i.headers)for(c in i.headers)f.setRequestHeader(c,i.headers[c]);i.timeoutMS&&(f.timeout=i.timeoutMS,f.ontimeout=p),f.send(i.body)}else{if(!rb(i))throw{message:"Request is not local and cannot be done through JSONP."};a=bu,bu+=1,w=a.toString(),b=!1,c="handleJSONP_"+w,n[c]=function(i){if(h=ce(h),!s){b=!0,n.clearTimeout(k),sb(c,a),n.ActiveXObject&&!n.DOMParser&&(i=n.JSON.parse(n.JSON.stringify(i)));var u;u=i.d===t?{"Content-Type":"application/json;odata=minimalmetadata",dataServiceVersion:"3.0"}:{"Content-Type":"application/json"},o(r,{body:i,statusCode:200,headers:u})}},d=i.timeoutMS?i.timeoutMS:12e4,k=n.setTimeout(p,d),l=nt+"=parent."+c,this.formatQueryString&&(l+="&"+tt),v=e.indexOf("?"),e=v===-1?e+"?"+l:v===e.length-1?e+l:e+"&"+l,h=ub(e)}return y}};var ci="3.0",nr=function(n){var t,r,i,f,u;if(!n)return null;for(t=n.split(";"),r={},i=1,f=t.length;i<f;i++)u=t[i].split("="),r[go(u[0])]=u[1];return{mediaType:go(t[0]),properties:r}},cb=function(n){if(!n)return t;var r=n.mediaType,i;for(i in n.properties)r+=";"+i+"="+n.properties[i];return r},lc=function(n,t,i,r){var u={};return g(u,i),g(u,{contentType:n,dataServiceVersion:t,handler:r}),u},ac=function(n,t,i){if(n){var r=n.headers;r[t]||(r[t]=i)}},lb=function(n,t){if(n){var i=n.headers,r=i.DataServiceVersion;i.DataServiceVersion=r?ct(r,t):t}},vc=function(n,i){var r=n.headers;return r&&r[i]||t},yc=function(n){return nr(vc(n,"Content-Type"))},ab=/^\s?(\d+\.\d+);?.*$/,pc=function(n){var i=vc(n,"DataServiceVersion"),t;if(i&&(t=ab.exec(i),t&&t.length))return t[1]},wc=function(n,t){return n.accept.indexOf(t.mediaType)>=0},vb=function(n,i,r,u){var f;if(!r||!r.headers)return!1;var e=yc(r),s=pc(r)||"",o=r.body;return ot(o)?wc(n,e)?(f=lc(e,s,u,n),f.response=r,r.data=i(n,o,f),r.data!==t):!1:!1},yb=function(n,i,r,u){var e,o,f;return!r||!r.headers?!1:(e=yc(r),o=pc(r),(!e||wc(n,e))&&(f=lc(e,o,u,n),f.request=r,r.body=i(n,r.data,f),r.body!==t))?(lb(r,f.dataServiceVersion||"1.0"),ac(r,"Content-Type",cb(f.contentType)),ac(r,"MaxDataServiceVersion",n.maxDataServiceVersion),!0):!1},li=function(n,t,i,r){return{accept:i,maxDataServiceVersion:r,read:function(t,i){return vb(this,n,t,i)},write:function(n,i){return yb(this,t,n,i)}}},pb=function(n,t){return t},wb=function(n,i){return ot(i)?i.toString():t};r.textHandler=li(pb,wb,"text/plain",ci);var bc=fu+"www.opengis.net",ut=bc+"/gml",kc=bc+"/def/crs/EPSG/0/",dc="gml",yr=function(n,t,i){var r={type:n};return r[t]=i,r},le=function(n){if(e(n)&&n.length>=2){var t=n[0];n[0]=n[1],n[1]=t}return n},ae=function(n,t,i,r,u,f){var e=gc(n,i,r,u,f);return yr(t,"coordinates",e)},gc=function(n,t,i,r,e){var o=[];return p(n,function(n){var s,h,c;if(u(n)===ut){if(s=f(n),s===t){h=b(n,ut),h&&(c=r(h,e),c&&o.push(c));return}s===i&&p(n,function(n){if(u(n)===ut){var t=r(n,e);t&&o.push(t)}})}}),o},bb=function(n,t){var i=gc(n,"geometryMember","geometryMembers",il,t);return yr(te,"geometries",i)},kb=function(n,t){return yr(bf,"coordinates",ve(n,t))},db=function(n,t){return ae(n,gf,"curveMember","curveMembers",ve,t)},gb=function(n,t){return ae(n,df,"pointMember","pointMembers",ye,t)},nk=function(n,t){return ae(n,ne,"surfaceMember","surfaceMembers",nl,t)},tk=function(n,t){return yr(wf,"coordinates",ye(n,t))},ik=function(n,t){return yr(kf,"coordinates",nl(n,t))},ve=function(n,t){var i=[];return p(n,function(n){var e=u(n),r;if(e===ut){if(r=f(n),r==="posList"){i=uk(n,t);return}if(r==="pointProperty"){i.push(rk(n,t));return}if(r==="pos"){i.push(pe(n,t));return}}}),i},ye=function(n,t){var i=b(n,ut,"pos");return i?pe(i,t):[]},rk=function(n,t){var i=b(n,ut,"Point");return i?ye(i,t):[]},nl=function(n,t){var i=[],r=!1;return p(n,function(n){if(u(n)===ut){var e=f(n);if(e==="exterior"){r=!0,i.unshift(tl(n,t));return}if(e==="interior"){i.push(tl(n,t));return}}}),!r&&i.length>0&&i.unshift([[]]),i},tl=function(n,t){var i=[];return p(n,function(n){u(n)===ut&&f(n)==="LinearRing"&&(i=ve(n,t))}),i},uk=function(n,t){var f=pe(n,!1),e=f.length,r,i,u;if(e%2!=0)throw{message:"GML posList element has an uneven number of numeric values"};for(r=[],i=0;i<e;i+=2)u=f.slice(i,i+2),r.push(t?le(u):u);return r},pe=function(n,t){var u=[],o=" \t\r\n",r=k(n),f;if(r)for(var s=r.length,e=0,i=0;i<=s;)o.indexOf(r.charAt(i))!==-1&&(f=r.substring(e,i),f&&u.push(parseFloat(f)),e=i+1),i++;return t?le(u):u},il=function(n,t){var o=f(n),i,u,r,e;switch(o){case"Point":i=tk;break;case"Polygon":i=ik;break;case"LineString":i=kb;break;case"MultiPoint":i=gb;break;case"MultiCurve":i=db;break;case"MultiSurface":i=nk;break;case"MultiGeometry":i=bb;break;default:throw{message:"Unsupported element: "+o,element:n};}if(u=i(n,t),r=st(n,"srsName",ut)||st(n,"srsName"),r){if(r.indexOf(kc)!==0)throw{message:"Unsupported srs name: "+r,element:n};e=r.substring(kc.length),e&&(u.crs={type:"name",properties:{name:"EPSG:"+e}})}return u},rl=function(n,t,i,r){var u,o,e,s,f,h,c;switch(i){case wf:u=fk;break;case bf:u=ek;break;case kf:u=ok;break;case df:u=sk;break;case gf:u=hk;break;case ne:u=ck;break;case te:u=ak;break;default:return null}return o=u(n,t,r),e=t.crs,e&&e.type==="name"&&(s=e.properties,f=s&&s.name,f&&f.indexOf("ESPG:")===0&&f.length>5&&(h=f.substring(5),c=si(n,null,"srsName",dc+h),l(o,c))),o},kt=function(n,t,i){return lr(n,ut,pt(dc,t),i)},ul=function(n,t,i){var r=e(t)?t:[];return r=i?le(r):r,kt(n,"pos",r.join(" "))},fl=function(n,t,i,r){var f=kt(n,t),u,o;if(e(i)){for(u=0,o=i.length;u<o;u++)l(f,ul(n,i[u],r));o===0&&l(f,kt(n,"posList"))}return f},el=function(n,t,i){return kt(n,"Point",ul(n,t,i))},ol=function(n,t,i){return fl(n,"LineString",t,i)},sl=function(n,t,i,r){var u=kt(n,t),f;return e(i)&&i.length>0&&(f=fl(n,"LinearRing",i,r),l(u,f)),u},hl=function(n,t,i){var f=t&&t.length,u=kt(n,"Polygon"),r;if(e(t)&&f>0)for(l(u,sl(n,"exterior",t[0],i)),r=1;r<f;r++)l(u,sl(n,"interior",t[r],i));return u},fk=function(n,t,i){return el(n,t.coordinates,i)},ek=function(n,t,i){return ol(n,t.coordinates,i)},ok=function(n,t,i){return hl(n,t.coordinates,i)},ku=function(n,t,i,r,u,f){var h=r&&r.length,c=kt(n,t),s,o;if(e(r)&&h>0){for(s=kt(n,i),o=0;o<h;o++)l(s,u(n,r[o],f));l(c,s)}return c},sk=function(n,t,i){return ku(n,"MultiPoint","pointMembers",t.coordinates,el,i)},hk=function(n,t,i){return ku(n,"MultiCurve","curveMembers",t.coordinates,ol,i)},ck=function(n,t,i){return ku(n,"MultiSurface","surfaceMembers",t.coordinates,hl,i)},lk=function(n,t,i){return rl(n,t,t.type,i)},ak=function(n,t,i){return ku(n,"MultiGeometry","geometryMembers",t.geometries,lk,i)},du="application/xml",dt=fu+"schemas.microsoft.com/ado/",ai=dt+"2007/08/dataservices",gu=dt+"2007/06/edmx",vk=dt+"2006/04/edm",yk=dt+"2007/05/edm",pk=dt+"2008/01/edm",wk=dt+"2008/09/edm",bk=dt+"2009/08/edm",kk=dt+"2009/11/edm",lt=ai,nt=ai+"/metadata",we=ai+"/related/",cl=ai+"/scheme",ll="d",be="m",gt=function(n,t){var i={name:f(n),value:n.value};return i[t?"namespaceURI":"namespace"]=u(n),i},pr=function(n,t){for(var s=[],h=[],c=n.attributes,e,i,o,r=0,l=c.length;r<l;r++)e=c[r],u(e)!==hr&&s.push(gt(e,t));for(i=n.firstChild;i!=null;)i.nodeType===1&&h.push(pr(i,t)),i=i.nextSibling;return o={name:f(n),value:k(n),attributes:s,children:h},o[t?"namespaceURI":"namespace"]=u(n),o},ke=function(n){return u(n)===lt&&f(n)==="element"},al=function(n,t){return{type:n,extensions:t}},dk=function(n){var t,i;return b(n,ut)?et:(t=b(n,lt),!t)?ar:ke(t)&&(i=iw(t,lt),i&&ke(i))?"Collection()":null},vl=function(n){var t=null,i=!1,r=[];return wi(n,function(n){var e=u(n),o=f(n),s=ki(n);if(e===nt){if(o==="null"){i=s.toLowerCase()==="true";return}if(o==="type"){t=s;return}}if(e!==pi&&e!==hr){r.push(gt(n,!0));return}}),{type:!t&&i?ar:t,isNull:i,extensions:r}},yl=function(n){if(u(n)!==lt)return null;var e=f(n),t=vl(n),o=t.isNull,i=t.type,r=al(i,t.extensions),s=o?null:pl(n,i,r);return{name:e,value:s,metadata:r}},pl=function(n,t,i){t||(t=dk(n),i.type=t);var r=ie(t);return r||re(t)?gk(n,t,r):ue(t)?wl(n,t):yu(t)?td(n,t,i):nd(n,t,i)},gk=function(n,t,i){var u=b(n,ut),r=il(u,i);return r.__metadata={type:t},r},wl=function(n,t){var i=ki(n)||"";switch(t){case ls:return oe(i);case cs:case vs:case ps:case ks:case ar:return i;case as:case ws:case bs:case ds:return s(i);case ys:case gs:return parseFloat(i);case au:return he(i);case cu:return wu(i);case lu:return se(i)}return i},nd=function(n,t,i){var r={__metadata:{type:t}};return p(n,function(n){var t=yl(n),u=t.name;i.properties=i.properties||{},i.properties[u]=t.metadata,r[u]=t.value}),r},td=function(n,t,i){var r=[],u=i.elements=[],f=vu(t);return p(n,function(n){if(ke(n)){var t=vl(n),o=t.extensions,i=t.type||f,e=al(i,o),s=pl(n,i,e);r.push(s),u.push(e)}}),{__metadata:{type:t==="Collection()"?null:t},results:r}},id=function(n,i){if(u(n)===lt){i=rt(n,i);var r=f(n);if(r==="links")return rd(n,i);if(r==="uri")return bl(n,i)}return t},rd=function(n,t){var i=[];return p(n,function(n){f(n)==="uri"&&u(n)===lt&&i.push(bl(n,t))}),{results:i}},bl=function(n,t){var i=k(n)||"";return{uri:c(i,t)}},ud=function(n,t){return t===et||t===ht?n&&n.type:t===oh||t===nh?wf:t===sh||t===th?bf:t===hh||t===ih?kf:t===ch||t===rh?te:t===lh||t===uh?ne:t===ah||t===fh?gf:t===vh||t===eh?df:null},kl=function(n,t,i){return lr(n,nt,pt(be,t),i)},de=function(n,t,i){return si(n,nt,pt(be,t),i)},dl=function(n,t,i){return lr(n,lt,pt(ll,t),i)},fd=function(n,t){return t===cu||t===lu||cf(n)?ph(n):t===au?wh(n):n.toString()},at=function(n,t){return{element:n,dsv:t}},wr=function(n,t,i,r){var u=i?de(n,"type",i):null,f=dl(n,t,u);return su(f,r)},ed=function(n,t,i,r){var u=fd(i,r),f=wr(n,t,r,u);return at(f,"1.0")},od=function(n,t,i,r){var u=de(n,"null","true"),f=wr(n,t,i,u),e=gi(i,r)?"2.0":"1.0";return at(f,e)},sd=function(n,t,i,r,u,f,o){var c=vu(r),a=e(i)?i:i.results,v=r?{type:c}:{},h,s,y,p,w;for(v.properties=u.properties,h=wr(n,t,c?r:null),s=0,y=a.length;s<y;s++)p=a[s],w=ge(n,"element",p,v,f,o),l(h,w.element);return at(h,"3.0")},hd=function(n,t,i,r,u,f,e){var h=wr(n,t,r),a=u.properties||{},v=gi(r,e)||{},s="1.0",o;for(o in i)if(o!=="__metadata"){var y=i[o],p=wt(v.property,o),w=a[o]||{},c=ge(n,o,y,w,p,e);s=ct(s,c.dsv),l(h,c.element)}return at(h,s)},cd=function(n,t,i,r,u){var f=ud(i,r),e=rl(n,i,f,u),o=wr(n,t,r,e);return at(o,"3.0")},ge=function(n,t,i,r,u,f){var e=hs(i,r,u),o,s;return pu(i)?ed(n,t,i,e||ar):(o=ie(e),o||re(e))?cd(n,t,i,e,o):kh(i,e)?sd(n,t,i,e,r,u,f):nc(i)?null:(s=tc(i,u),s!==null)?null:i===null?od(n,t,e):hd(n,t,i,e,r,u,f)},ld=function(n){if(n&&lf(n)){var t=os();return l(t,dl(t,"uri",n.uri))}},ad=function(n,t){if(t){var r=ou(t),i=b(r);if(i)return id(i)}},vd=function(n,i,r){var u=r.contentType=r.contentType||nr(du);return u&&u.mediaType===du?pf(ld(i)):t};r.xmlHandler=li(ad,vd,du,ci);var gl="a",ni=sr+"2005/Atom",br=sr+"2007/app",na=ai+"/edit-media/",ta=ai+"/mediaresource/",ia=ai+"/relatedlinks/",ra=["application/atom+xml","application/atomsvc+xml","application/xml"],ua=ra[0],yd=[ni,br,pi,hr],pd={SyndicationAuthorEmail:"author/email",SyndicationAuthorName:"author/name",SyndicationAuthorUri:"author/uri",SyndicationContributorEmail:"contributor/email",SyndicationContributorName:"contributor/name",SyndicationContributorUri:"contributor/uri",SyndicationPublished:"published",SyndicationRights:"rights",SyndicationSummary:"summary",SyndicationTitle:"title",SyndicationUpdated:"updated"},wd=function(n){return pd[n]||n},kr=function(n){return!er(yd,n)},fa=function(n,t,i,r,u){var f;if(u=u||"",f=n["FC_TargetPath"+u],!f)return null;var e=n["FC_SourcePath"+u],s=wd(f),o=r?r+(e?"/"+e:""):e,l=o&&tg(i,t,o),h=n["FC_NsUri"+u]||null,c=n["FC_NsPrefix"+u]||null,a=n["FC_KeepInContent"+u]||"";return f!==s&&(h=ni,c=gl),{contentKind:n["FC_ContentKind"+u],keepInContent:a.toLowerCase()==="true",nsPrefix:c,nsURI:h,propertyPath:o,propertyType:l,entryPath:s}},ea=function(n,t,i){for(var c=[],l,r,f,u,e;n;){for(l=n.FC_SourcePath,r=fa(n,n,t),r&&i(r),f=n.property||[],u=0,e=f.length;u<e;u++)for(var o=f[u],s=0,h="";r=fa(o,n,t,o.name,h);)i(r),s++,h="_"+s;n=bt(n.baseType,t)}return c},nf=function(n){var t=[];return wi(n,function(n){var i=u(n);kr(i)&&t.push(gt(n,!0))}),t},oa=function(n){return pr(n,!0)},sa=function(n,t,i){var e=u(n),r=f(n);if(e===br&&r==="service")return cg(n,t);if(e===ni){if(r==="feed")return bd(n,t,i);if(r==="entry")return va(n,t,i)}},ha=function(n,t){var r=[],i={extensions:r};return wi(n,function(e){var o=f(e),s=u(e),h=ki(e);if(s===null){if(o==="title"||o==="metadata"){i[o]=h;return}if(o==="target"){i.target=c(h,rt(n,t));return}}kr(s)&&r.push(gt(e,!0))}),i},ca=function(n,t,i){var r=i.actions=i.actions||[];r.push(ha(n,t))},la=function(n,t,i){var r=i.functions=i.functions||[];r.push(ha(n,t))},bd=function(n,t,i){var o=nf(n),r={feed_extensions:o},s=[],e={__metadata:r,results:s};return t=rt(n,t),p(n,function(n){var l=u(n),h=f(n);if(l===nt){if(h==="count"){e.__count=parseInt(k(n),10);return}if(h==="action"){ca(n,t,r);return}if(h==="function"){la(n,t,r);return}}if(kr(l)){o.push(pr(n));return}if(h==="entry"){s.push(va(n,t,i));return}if(h==="link"){kd(n,e,t);return}if(h==="id"){r.uri=c(k(n),t),r.uri_extensions=nf(n);return}if(h==="title"){r.title=k(n)||"",r.title_extensions=nf(n);return}}),e},kd=function(n,t,i){var r=aa(n,i),f=r.href,e=r.rel,o=r.extensions,u=t.__metadata;if(e==="next"){t.__next=f,u.next_extensions=o;return}if(e==="self"){u.self=f,u.self_extensions=o;return}},aa=function(n,t){t=rt(n,t);var r=[],i={extensions:r,baseURI:t};if(wi(n,function(n){var s=u(n),e=f(n),o=n.value;if(e==="href"){i.href=c(o,t);return}if(e==="type"||e==="rel"){i[e]=o;return}kr(s)&&r.push(gt(n,!0))}),!i.href)throw{error:"href attribute missing on link element",element:n};return i},dd=function(n,i){if(n.indexOf("/")===-1)return i[n];for(var u=n.split("/"),r=0,f=u.length;r<f;r++){if(i===null)return t;if(i=i[u[r]],i===t)return i}return i},gd=function(n,i,r,u){var o,s,f,h,e;if(n.indexOf("/")===-1)i[n]=r,o=n;else{for(s=n.split("/"),f=0,h=s.length-1;f<h;f++){if(e=i[s[f]],e===t)e={},i[s[f]]=e;else if(e===null)return;i=e}o=s[f],i[o]=r}if(u){var c=i.__metadata=i.__metadata||{},l=c.properties=c.properties||{},a=l[o]=l[o]||{};a.type=u}},ng=function(n,t,i){var e=n.propertyPath,r,u,f;n.keepInContent||dd(e,i)===null||(r=nw(t,n.nsURI,n.entryPath),r)&&(u=n.propertyType,f=n.contentKind==="xhtml"?ew(r):wl(r,u||"Edm.String"),gd(e,i,f,u))},tg=function(n,t,i){for(var s=i.split("/"),u,h,f,e,o,r;t;){for(f=t,u=0,h=s.length;u<h;u++){if(e=f.property,!e)break;if(o=wt(e,s[u]),!o)break;if(r=o.type,!r||ue(r))return r||null;if(f=gi(r,n),!f)return null}t=bt(t.baseType,n)}return null},va=function(n,t,i){var r={},e={__metadata:r},o=st(n,"etag",nt),s;return o&&(r.etag=o),t=rt(n,t),p(n,function(n){var s=u(n),o=f(n);if(s===ni){if(o==="id"){ig(n,r,t);return}if(o==="category"){rg(n,r);return}if(o==="content"){ug(n,e,r,t);return}if(o==="link"){fg(n,e,r,t,i);return}return}if(s===nt){if(o==="properties"){wa(n,e,r);return}if(o==="action"){ca(n,t,r);return}if(o==="function"){la(n,t,r);return}}}),s=bt(r.type,i),ea(s,i,function(t){ng(t,n,e)}),e},ig=function(n,t,i){t.uri=c(k(n),rt(n,i)),t.uri_extensions=nf(n)},rg=function(n,t){if(st(n,"scheme")===cl){if(t.type)throw{message:"Invalid AtomPub document: multiple category elements defining the entry type were encounterd withing an entry",element:n};var i=[];wi(n,function(n){var t=u(n),r=f(n);if(!t){r!=="scheme"&&r!=="term"&&i.push(gt(n,!0));return}kr(t)&&i.push(gt(n,!0))}),t.type=st(n,"term"),t.type_extensions=i}},ug=function(n,t,i,r){var e=st(n,"src"),o=st(n,"type");if(e){if(!o)throw{message:"Invalid AtomPub document: content element must specify the type attribute if the src attribute is also specified",element:n};i.media_src=c(e,rt(n,r)),i.content_type=o}p(n,function(r){if(e)throw{message:"Invalid AtomPub document: content element must not have child elements if the src attribute is specified",element:n};u(r)===nt&&f(r)==="properties"&&wa(r,t,i)})},fg=function(n,t,i,r,u){var f=aa(n,r),e=f.rel,s=f.href,o=f.extensions;if(e==="self"){i.self=s,i.self_link_extensions=o;return}if(e==="edit"){i.edit=s,i.edit_link_extensions=o;return}if(e==="edit-media"){i.edit_media=f.href,i.edit_media_extensions=o,pa(f,i);return}if(e.indexOf(na)===0){sg(f,t,i);return}if(e.indexOf(ta)===0){hg(f,t,i);return}if(e.indexOf(we)===0){og(n,f,t,i,u);return}if(e.indexOf(ia)===0){eg(f,i);return}},eg=function(n,t){var r=n.rel.substring(ia.length),i;t.properties=t.properties||{},i=t.properties[r]=t.properties[r]||{},i.associationuri=n.href,i.associationuri_extensions=n.extensions},og=function(n,t,i,r,u){var e,o=b(n,nt,"inline"),s,h,f,c;o?(s=b(o),h=rt(o,t.baseURI),e=s?sa(s,h,u):null):e={__deferred:{uri:t.href}},f=t.rel.substring(we.length),i[f]=e,r.properties=r.properties||{},c=r.properties[f]=r.properties[f]||{},c.extensions=t.extensions},sg=function(n,t,i){var o=n.rel.substring(na.length),f=ya(o,t,i),r=f.value,u=f.metadata,e=n.href;r.edit_media=e,r.content_type=n.type,u.edit_media_extensions=n.extensions,r.media_src=r.media_src||e,u.media_src_extensions=u.media_src_extensions||[],pa(n,r)},hg=function(n,t,i){var f=n.rel.substring(ta.length),r=ya(f,t,i),u=r.value,e=r.metadata;u.media_src=n.href,e.media_src_extensions=n.extensions,u.content_type=n.type},ya=function(n,t,i){i.properties=i.properties||{};var u=i.properties[n],r=t[n]&&t[n].__mediaresource;return r||(r={},t[n]={__mediaresource:r},i.properties[n]=u={}),{value:r,metadata:u}},pa=function(n,t){for(var r=n.extensions,i=0,u=r.length;i<u;i++)if(r[i].namespaceURI===nt&&r[i].name==="etag"){t.media_etag=r[i].value,r.splice(i,1);return}},wa=function(n,t,i){p(n,function(n){var r=yl(n),u,f;r&&(u=r.name,f=i.properties=i.properties||{},f[u]=r.metadata,t[u]=r.value)})},cg=function(n,t){var i=[],r=[];if(t=rt(n,t),p(n,function(n){if(u(n)===br&&f(n)==="workspace"){i.push(lg(n,t));return}r.push(pr(n))}),i.length===0)throw{message:"Invalid AtomPub service document: No workspace element found.",element:n};return{workspaces:i,extensions:r}},lg=function(n,i){var e=[],o=[],r;return i=rt(n,i),p(n,function(n){var s=u(n),h=f(n);if(s===ni&&h==="title"){if(r!==t)throw{message:"Invalid AtomPub service document: workspace has more than one child title element",element:n};r=k(n);return}if(s===br){h==="collection"&&e.push(ag(n,i));return}o.push(oa(n))}),{title:r||"",collections:e,extensions:o}},ag=function(n,i){var r=st(n,"href"),o,e;if(!r)throw{message:"Invalid AtomPub service document: collection has no href attribute",element:n};if(i=rt(n,i),r=c(r,rt(n,i)),o=[],p(n,function(i){var r=u(i),s=f(i);if(r===ni){if(s==="title"){if(e!==t)throw{message:"Invalid AtomPub service document: collection has more than one child title element",element:i};e=k(i)}return}r!==br&&o.push(oa(n))}),!e)throw{message:"Invalid AtomPub service document: collection has no title element",element:n};return{title:e,href:r,extensions:o}},ti=function(n,t,i){return lr(n,ni,pt(gl,t),i)},tr=function(n,t,i){return si(n,null,t,i)},vg=function(n){var t,e,i,o,r;if(n.childNodes.length>0)return!1;for(t=!0,e=n.attributes,i=0,o=e.length;i<o&&t;i++)r=e[i],t=t&&bp(r)||u(r)==nt&&f(r)==="type";return t},yg=function(n,t,i,r,u){var s=null,e=null,f=null,o="",h;return i!=="deferred"?(s=tr(n,"type","application/atom+xml;type="+i),e=kl(n,"inline"),r&&(o=r.__metadata&&r.__metadata.uri||"",f=ba(n,r,u)||no(n,r,u),l(e,f.element))):o=r.__deferred.uri,h=ti(n,"link",[tr(n,"href",o),tr(n,"rel",c(t,we)),s,e]),at(h,f?f.dsv:"1.0")},pg=function(n,t,i,r,u,f){var e,o;return nc(i)?null:(e=ge(n,t,i,r,u,f),e||(o=tc(i,u),e=yg(n,t,o,i,f)),e)},wg=function(n,t,i,r){var u=fs(i,lt,r.propertyPath),l=u&&bi(u,"null",nt),o,s="1.0",f,e,h,c;if(l&&l.value==="true")return s;if(u&&(o=k(u)||"",!r.keepInContent))for(s="2.0",f=u.parentNode,e=f,f.removeChild(u);e!==i&&vg(e);)f=e.parentNode,f.removeChild(e),e=f;return(h=fw(n,t,r.nsURI,r.nsPrefix,r.entryPath),h.nodeType===2)?(h.value=o,s):(c=r.contentKind,su(h,[c&&si(n,null,"type",c),c==="xhtml"?rw(n,o):o]),s)},no=function(n,t,i){var e=t.__metadata||{},b=e.properties||{},y=e.etag,p=e.uri,s=e.type,o=bt(s,i),h=kl(n,"properties"),c=ti(n,"entry",[ti(n,"author",ti(n,"name")),y&&de(n,"etag",y),p&&ti(n,"id",p),s&&ti(n,"category",[tr(n,"term",s),tr(n,"scheme",cl)]),ti(n,"content",[tr(n,"type","application/xml"),h])]),f="1.0",r,v,w;for(r in t)if(r!=="__metadata"){var k=b[r]||{},d=o&&(wt(o.property,r)||wt(o.navigationProperty,r)),a=pg(n,r,t[r],k,d,i);a&&(v=a.element,w=u(v)===ni?c:h,l(w,v),f=ct(f,a.dsv))}return ea(o,i,function(t){var i=wg(n,c,h,t);f=ct(f,i)}),at(c,f)},ba=function(n,t,i){var f=e(t)?t:t.results,r,o,u,h,s;if(!f)return null;for(r="1.0",o=ti(n,"feed"),u=0,h=f.length;u<h;u++)s=no(n,f[u],i),l(o,s.element),r=ct(r,s.dsv);return at(o,r)},bg=function(n,t){var u,i,r,f;return n&&(u=vr(n)&&ba||lf(n)&&no,u&&(i=os(),r=u(i,n,t),r))?(f=r.element,su(f,[ss(i,nt,be),ss(i,lt,ll)]),at(l(i,f),r.dsv)):null},kg=function(n,t,i){if(t){var u=ou(t),r=b(u);if(r)return sa(r,null,i.metadata)}},dg=function(n,t,i){var u=i.contentType=i.contentType||nr(ua),r;if(u&&u.mediaType===ua&&(r=bg(t,i.metadata),r))return i.dataServiceVersion=ct(i.dataServiceVersion||"1.0",r.dsv),pf(r.element)};r.atomHandler=li(kg,dg,ra.join(","),ci);var i=function(n,t,i,r){return{attributes:n,elements:t,text:i||!1,ns:r}},tt={elements:{Annotations:i(["Target","Qualifier"],["TypeAnnotation*","ValueAnnotation*"]),Association:i(["Name"],["End*","ReferentialConstraint","TypeAnnotation*","ValueAnnotation*"]),AssociationSet:i(["Name","Association"],["End*","TypeAnnotation*","ValueAnnotation*"]),Binary:i(null,null,!0),Bool:i(null,null,!0),Collection:i(null,["String*","Int*","Float*","Decimal*","Bool*","DateTime*","DateTimeOffset*","Guid*","Binary*","Time*","Collection*","Record*"]),CollectionType:i(["ElementType","Nullable","DefaultValue","MaxLength","FixedLength","Precision","Scale","Unicode","Collation","SRID"],["CollectionType","ReferenceType","RowType","TypeRef"]),ComplexType:i(["Name","BaseType","Abstract"],["Property*","TypeAnnotation*","ValueAnnotation*"]),DateTime:i(null,null,!0),DateTimeOffset:i(null,null,!0),Decimal:i(null,null,!0),DefiningExpression:i(null,null,!0),Dependent:i(["Role"],["PropertyRef*"]),Documentation:i(null,null,!0),End:i(["Type","Role","Multiplicity","EntitySet"],["OnDelete"]),EntityContainer:i(["Name","Extends"],["EntitySet*","AssociationSet*","FunctionImport*","TypeAnnotation*","ValueAnnotation*"]),EntitySet:i(["Name","EntityType"],["TypeAnnotation*","ValueAnnotation*"]),EntityType:i(["Name","BaseType","Abstract","OpenType"],["Key","Property*","NavigationProperty*","TypeAnnotation*","ValueAnnotation*"]),EnumType:i(["Name","UnderlyingType","IsFlags"],["Member*"]),Float:i(null,null,!0),Function:i(["Name","ReturnType"],["Parameter*","DefiningExpression","ReturnType","TypeAnnotation*","ValueAnnotation*"]),FunctionImport:i(["Name","ReturnType","EntitySet","IsSideEffecting","IsComposable","IsBindable","EntitySetPath"],["Parameter*","ReturnType","TypeAnnotation*","ValueAnnotation*"]),Guid:i(null,null,!0),Int:i(null,null,!0),Key:i(null,["PropertyRef*"]),LabeledElement:i(["Name"],["Path","String","Int","Float","Decimal","Bool","DateTime","DateTimeOffset","Guid","Binary","Time","Collection","Record","LabeledElement","Null"]),Member:i(["Name","Value"]),NavigationProperty:i(["Name","Relationship","ToRole","FromRole","ContainsTarget"],["TypeAnnotation*","ValueAnnotation*"]),Null:i(null,null),OnDelete:i(["Action"]),Path:i(null,null,!0),Parameter:i(["Name","Type","Mode","Nullable","DefaultValue","MaxLength","FixedLength","Precision","Scale","Unicode","Collation","ConcurrencyMode","SRID"],["CollectionType","ReferenceType","RowType","TypeRef","TypeAnnotation*","ValueAnnotation*"]),Principal:i(["Role"],["PropertyRef*"]),Property:i(["Name","Type","Nullable","DefaultValue","MaxLength","FixedLength","Precision","Scale","Unicode","Collation","ConcurrencyMode","CollectionKind","SRID"],["CollectionType","ReferenceType","RowType","TypeAnnotation*","ValueAnnotation*"]),PropertyRef:i(["Name"]),PropertyValue:i(["Property","Path","String","Int","Float","Decimal","Bool","DateTime","DateTimeOffset","Guid","Binary","Time"],["Path","String","Int","Float","Decimal","Bool","DateTime","DateTimeOffset","Guid","Binary","Time","Collection","Record","LabeledElement","Null"]),ReferenceType:i(["Type"]),ReferentialConstraint:i(null,["Principal","Dependent"]),ReturnType:i(["ReturnType","Type","EntitySet"],["CollectionType","ReferenceType","RowType"]),RowType:i(["Property*"]),String:i(null,null,!0),Schema:i(["Namespace","Alias"],["Using*","EntityContainer*","EntityType*","Association*","ComplexType*","Function*","ValueTerm*","Annotations*"]),Time:i(null,null,!0),TypeAnnotation:i(["Term","Qualifier"],["PropertyValue*"]),TypeRef:i(["Type","Nullable","DefaultValue","MaxLength","FixedLength","Precision","Scale","Unicode","Collation","SRID"]),Using:i(["Namespace","Alias"]),ValueAnnotation:i(["Term","Qualifier","Path","String","Int","Float","Decimal","Bool","DateTime","DateTimeOffset","Guid","Binary","Time"],["Path","String","Int","Float","Decimal","Bool","DateTime","DateTimeOffset","Guid","Binary","Time","Collection","Record","LabeledElement","Null"]),ValueTerm:i(["Name","Type"],["TypeAnnotation*","ValueAnnotation*"]),Edmx:i(["Version"],["DataServices","Reference*","AnnotationsReference*"],!1,gu),DataServices:i(null,["Schema*"],!1,gu)}},ka=["m:FC_ContentKind","m:FC_KeepInContent","m:FC_NsPrefix","m:FC_NsUri","m:FC_SourcePath","m:FC_TargetPath"];tt.elements.Property.attributes=tt.elements.Property.attributes.concat(ka),tt.elements.EntityType.attributes=tt.elements.EntityType.attributes.concat(ka),tt.elements.Edmx={attributes:["Version"],elements:["DataServices"],ns:gu},tt.elements.DataServices={elements:["Schema*"],ns:gu},tt.elements.EntityContainer.attributes.push("m:IsDefaultEntityContainer"),tt.elements.Property.attributes.push("m:MimeType"),tt.elements.FunctionImport.attributes.push("m:HttpMethod"),tt.elements.FunctionImport.attributes.push("m:IsAlwaysBindable"),tt.elements.EntityType.attributes.push("m:HasStream"),tt.elements.DataServices.attributes=["m:DataServiceVersion","m:MaxDataServiceVersion"];var da=function(n){if(!n)return n;if(n.length>1){var t=n.substr(0,2);return t===t.toUpperCase()?n:n.charAt(0).toLowerCase()+n.substr(1)}return n.charAt(0).toLowerCase()},gg=function(n,t){var r,u,e,i,f,o;if(t==="Documentation")return{isArray:!0,propertyName:"documentation"};if(r=n.elements,!r)return null;for(u=0,e=r.length;u<e;u++)if(i=r[u],f=!1,i.charAt(i.length-1)==="*"&&(f=!0,i=i.substr(0,i.length-1)),t===i)return o=da(i),{isArray:f,propertyName:o};return null},nn=/^(m:FC_.*)_[0-9]+$/,ga=function(n){return n===vk||n===yk||n===pk||n===wk||n===bk||n===kk},to=function(n){var o=f(n),e=u(n),i=tt.elements[o];if(!i)return null;if(i.ns){if(e!==i.ns)return null}else if(!ga(e))return null;var t={},r=[],s=i.attributes||[];return wi(n,function(n){var c=f(n),e=u(n),l=n.value,i,o,h;e!==hr&&(i=null,o=!1,ga(e)||e===null?i="":e===nt&&(i="m:"),i!==null&&(i+=c,h=nn.exec(i),h&&(i=h[1]),er(s,i)&&(o=!0,t[da(c)]=l)),o||r.push(gt(n)))}),p(n,function(n){var o=f(n),u=gg(i,o),e;u?u.isArray?(e=t[u.propertyName],e||(e=[],t[u.propertyName]=e),e.push(to(n))):t[u.propertyName]=to(n):r.push(pr(n))}),i.text&&(t.text=k(n)),r.length&&(t.extensions=r),t},nv=function(n,i){var r=ou(i),u=b(r);return to(u)||t};r.metadataHandler=li(nv,null,du,ci);var tv="o",io="f",iv="p",rv="c",uv="s",fv="l",tn="odata",ii=tn+".",rn="@"+ii+"bind",ro=ii+"metadata",ev=ii+"navigationLinkUrl",ir=ii+"type",tf={readLink:"self",editLink:"edit",nextLink:"__next",mediaReadLink:"media_src",mediaEditLink:"edit_media",mediaContentType:"content_type",mediaETag:"media_etag",count:"__count",media_src:"mediaReadLink",edit_media:"mediaEditLink",content_type:"mediaContentType",media_etag:"mediaETag",url:"uri"},h={metadata:"odata.metadata",count:"odata.count",next:"odata.nextLink",id:"odata.id",etag:"odata.etag",read:"odata.readLink",edit:"odata.editLink",mediaRead:"odata.mediaReadLink",mediaEdit:"odata.mediaEditLink",mediaEtag:"odata.mediaETag",mediaContentType:"odata.mediaContentType",actions:"odata.actions",functions:"odata.functions",navigationUrl:"odata.navigationLinkUrl",associationUrl:"odata.associationLinkUrl",type:"odata.type"},un=function(n){if(n.indexOf(".")>0){var t=n.indexOf("@"),r=t>-1?n.substring(0,t):null,i=n.substring(t+1);return{target:r,name:i,isOData:i.indexOf(ii)===0}}return null},uo=function(n,t,i,r,u){return d(t)&&t[ir]||i&&i[n+"@"+ir]||r&&r.type||bw(r,u)||null},fo=function(n,t){return t?wt(t.property,n)||wt(t.navigationProperty,n):null},ov=function(n){return d(n)&&ii+"id"in n},fn=function(n,t,i){if(!!t[n+"@"+ev]||i&&i.relationship)return!0;var r=e(t[n])?t[n][0]:t[n];return ov(r)},eo=function(n){return ue(n)||ie(n)||re(n)},ri=function(n,t,i,r,u){var f,e;for(f in n)if(f.indexOf(".")>0&&f.charAt(0)!=="#"&&(e=un(f),e)){var c=e.name,o=e.target,s=null,h=null;o&&(s=fo(o,r),h=uo(o,n[o],n,s,u)),e.isOData?en(c,o,h,n[f],n,t,i):t[f]=n[f]}return t},en=function(n,t,i,r,u,f,e){var o=n.substring(ii.length);switch(o){case"navigationLinkUrl":cn(o,t,i,r,u,f,e);return;case"nextLink":case"count":sn(o,t,r,f,e);return;case"mediaReadLink":case"mediaEditLink":case"mediaContentType":case"mediaETag":hn(o,t,i,r,f,e);return;default:on(o,t,r,f,e);return}},on=function(n,t,i,r,u){var f=r.__metadata=r.__metadata||{},e=tf[n]||n,s,o;if(n==="editLink"){f.uri=c(i,u),f[e]=f.uri;return}if((n==="readLink"||n==="associationLinkUrl")&&(i=c(i,u)),t){if(s=f.properties=f.properties||{},o=s[t]=s[t]||{},n==="type"){o[e]=o[e]||i;return}o[e]=i;return}f[e]=i},sn=function(n,t,i,r,u){var f=tf[n],e=t?r[t]:r;e[f]=n==="nextLink"?c(i,u):i},hn=function(n,t,i,r,u,f){var e=u.__metadata=u.__metadata||{},h=tf[n],o,s;if((n==="mediaReadLink"||n==="mediaEditLink")&&(r=c(r,f)),t){o=e.properties=e.properties||{},s=o[t]=o[t]||{},s.type=s.type||i,u.__metadata=e,u[t]=u[t]||{__mediaresource:{}},u[t].__mediaresource[h]=r;return}e[h]=r},cn=function(n,t,i,r,u,f,e){var s=f.__metadata=f.__metadata||{},h=s.properties=s.properties||{},o=h[t]=h[t]||{},l=c(r,e);if(u.hasOwnProperty(t)){o.navigationLinkUrl=l;return}f[t]={__deferred:{uri:l}},o.type=o.type||i},oo=function(n,t,i,r,u,f,o){if(typeof n=="string")return ln(n,t,o);if(!eo(t)){if(e(n))return sv(n,t,i,r,f,o);if(d(n))return an(n,t,i,r,f,o)}return n},ln=function(n,t,i){switch(t){case au:return he(n);case cu:return wu(n,!1);case lu:return se(n,!1)}return i?wu(n,!0)||se(n,!0)||n:n},sv=function(n,t,i,r,u,f){for(var a=vu(t),o=[],h=[],e=0,c=n.length;e<c;e++){var s=uo(null,n[e])||a,l={type:s},v=oo(n[e],s,l,r,null,u,f);eo(s)||pu(n[e])||o.push(l),h.push(v)}return o.length>0&&(i.elements=o),{__metadata:{type:t},results:h}},an=function(n,t,i,r,u,f){var e=rf(n,{type:t},r,u,f),o=e.__metadata,s=o.properties;return s&&(i.properties=s,delete o.properties),e},vn=function(n,t,i,r,u){return e(n)?cv(n,t,i,r,u):d(n)?rf(n,t,i,r,u):null},rf=function(n,i,r,u,f){var d,v,g,o,y,h,c,nt;i=i||{};var l=n[ir]||i.type||null,s=bt(l,u),k=!0;s||(k=!1,s=gi(l,u));var p={type:l},a={__metadata:p},w={},e;if(k&&s&&i.entitySet&&i.contentTypeOdata=="minimalmetadata"){for(d=r.substring(0,r.lastIndexOf("$metadata")),e=null,s.key||(e=s);!!e&&!e.key&&e.baseType;)e=bt(e.baseType,u);(s.key||!!e&&e.key)&&(v=s.key?lv(n,s):lv(n,e),v&&(g={key:v,entitySet:i.entitySet,functionImport:i.functionImport,containerName:i.containerName},yn(n,g,l,d,s,e)))}for(o in n)if(o.indexOf("#")===0)hv(o.substring(1),n[o],a,r,u);else if(o.indexOf(".")===-1){for(p.properties||(p.properties=w),y=n[o],h=h=fo(o,s),e=s;!!s&&h===null&&e.baseType;)e=bt(e.baseType,u),h=h=fo(o,e);var tt=fn(o,n,h),b=uo(o,y,n,h,u),it=w[o]=w[o]||{type:b};tt?(c={},i.entitySet!==t&&(nt=kw(h,i.entitySet.name,u),c=dw(nt,u)),c.contentTypeOdata=i.contentTypeOdata,c.kind=i.kind,c.type=b,a[o]=vn(y,c,r,u,f)):a[o]=oo(y,b,it,r,h,u,f)}return ri(n,a,r,s,u)},hv=function(n,t,i,r,u){var f,s,g,nt;if(n&&(e(t)||d(t))){var l=!1,h=n.lastIndexOf("."),a=n.substring(h+1),v=h>-1?n.substring(0,h):"",y=a===n||v.indexOf(".")===-1?ic(u):rc(v,u);y&&(f=ww(y.functionImport,a),f&&!!f.isSideEffecting&&(l=!oe(f.isSideEffecting)));for(var p=i.__metadata,w=l?"functions":"actions",tt=c(n,r),b=e(t)?t:[t],o=0,k=b.length;o<k;o++)s=b[o],s&&(g=p[w]=p[w]||[],nt={metadata:tt,title:s.title,target:c(s.target,r)},g.push(nt))}},cv=function(n,t,i,r,u){for(var h=e(n)?n:n.value,c=[],l,f,s,o=0,a=h.length;o<a;o++)l=rf(h[o],t,i,r,u),c.push(l);if(f={results:c},d(n)){for(s in n)s.indexOf("#")===0&&(f.__metadata=f.__metadata||{},hv(s.substring(1),n[s],f,i,r));f=ri(n,f,i)}return f},lv=function(n,t){var r,i=t.key.propertyRef,f,e,u;if(r="(",i.length==1)f=wt(t.property,i[0].name).type,r+=so(n[i[0].name],f);else for(e=!0,u=0;u<i.length;u++)e?e=!1:r+=",",f=wt(t.property,i[u].name).type,r+=i[u].name+"="+so(n[i[u].name],f);return r+=")"},yn=function(n,t,i,r,u,f){var o=n[h.id]||n[h.read]||n[h.edit]||t.entitySet.name+t.key,e;n[h.id]=r+o,n[h.edit]||(n[h.edit]=t.entitySet.name+t.key,t.entitySet.entityType!=i&&(n[h.edit]+="/"+i)),n[h.read]=n[h.read]||n[h.edit],n[h.etag]||(e=pn(n,u,f),!e||(n[h.etag]=e)),dn(n,u,f),wn(n,u,f),kn(n,t)},pn=function(n,t,i){for(var u="",f,r=0;t.property&&r<t.property.length;r++)f=t.property[r],u=av(n,u,f);if(i)for(r=0;i.property&&r<i.property.length;r++)f=i.property[r],u=av(n,u,f);return u.length>0?u+'"':null},av=function(n,t,i){return i.concurrencyMode=="Fixed"&&(t+=t.length>0?",":'W/"',t+=n[i.name]!==null?so(n[i.name],i.type):"null"),t},wn=function(n,i,r){for(var s="@odata.navigationLinkUrl",c="@odata.associationLinkUrl",u,e,o,f=0;i.navigationProperty&&f<i.navigationProperty.length;f++)u=i.navigationProperty[f].name,e=u+s,n[e]===t&&(n[e]=n[h.edit]+"/"+encodeURIComponent(u)),o=u+c,n[o]===t&&(n[o]=n[h.edit]+"/$links/"+encodeURIComponent(u));if(r&&r.navigationProperty)for(f=0;f<r.navigationProperty.length;f++)u=r.navigationProperty[f].name,e=u+s,n[e]===t&&(n[e]=n[h.edit]+"/"+encodeURIComponent(u)),o=u+c,n[o]===t&&(n[o]=n[h.edit]+"/$links/"+encodeURIComponent(u))},so=function(n,t){n=""+bn(n,t),n=encodeURIComponent(n.replace("'","''"));switch(t){case"Edm.Binary":return"X'"+n+"'";case"Edm.DateTime":return"datetime'"+n+"'";case"Edm.DateTimeOffset":return"datetimeoffset'"+n+"'";case"Edm.Decimal":return n+"M";case"Edm.Guid":return"guid'"+n+"'";case"Edm.Int64":return n+"L";case"Edm.Float":return n+"f";case"Edm.Double":return n+"D";case"Edm.Geography":return"geography'"+n+"'";case"Edm.Geometry":return"geometry'"+n+"'";case"Edm.Time":return"time'"+n+"'";case"Edm.String":return"'"+n+"'";default:return n}},bn=function(n,t){switch(t){case"Edm.Binary":return cp(n);default:return n}},kn=function(n,i){for(var u=i.functionImport||[],f,r=0;r<u.length;r++)u[r].isBindable&&u[r].parameter[0]&&u[r].parameter[0].type==i.entitySet.entityType&&(f="#"+i.containerName+"."+u[r].name,n[f]==t&&(n[f]={title:u[r].name,target:n[h.edit]+"/"+u[r].name}))},dn=function(n,t,i){(t.hasStream||i&&i.hasStream)&&(n[h.mediaEdit]=n[h.mediaEdit]||n[h.mediaEdit]+"/$value",n[h.mediaRead]=n[h.mediaRead]||n[h.mediaEdit])},gn=function(n,t,i,r){var u={type:t},f=oo(n.value,t,u,i,null,null,r);return ri(n,{__metadata:u,value:f},i)},ntt=function(n,t,i,r,u){var f={},e=sv(n.value,t,f,i,r,u);return g(e.__metadata,f),ri(n,e,i)},ttt=function(n,t){var r=n.value,u,i,f,o;if(!e(r))return vv(n,t);for(u=[],i=0,f=r.length;i<f;i++)u.push(vv(r[i],t));return o={results:u},ri(n,o,t)},vv=function(n,t){var i={uri:c(n.url,t)},u,r;return i=ri(n,i,t),u=i.__metadata||{},r=u.properties||{},uf(r.url),ru(r,"url","uri"),i},uf=function(n){n&&delete n.type},itt=function(n,t){var o=n.value,s=[],h=ri(n,{collections:s},t),e=h.__metadata||{},i=e.properties||{},u,l,f,r;for(uf(i.value),ru(i,"value","collections"),u=0,l=o.length;u<l;u++)f=o[u],r={title:f.name,href:c(f.url,t)},r=ri(f,r,t),e=r.__metadata||{},i=e.properties||{},uf(i.name),uf(i.url),ru(i,"name","title"),ru(i,"url","href"),s.push(r);return{workspaces:[h]}},ui=function(n,t){return{kind:n,type:t||null}},rtt=function(n,t,i){var f=n[ro],s,v,o,y,l,r,p,h,c,w,b,u,k;if(!f||typeof f!="string")return null;if(s=f.lastIndexOf("#"),s===-1)return ui(uv);if(v=f.indexOf("@Element",s),o=v-1,o<0&&(o=f.indexOf("?",s),o===-1&&(o=f.length)),y=f.substring(s+1,o),y.indexOf("/$links/")>0)return ui(fv);if(l=y.split("/"),l.length>=0){if(r=l[0],p=l[1],eo(r))return ui(iv,r);if(yu(r))return ui(rv,r);if(h=p,!p){var d=r.lastIndexOf("."),g=r.substring(d+1),a=g===r?ic(t):rc(r.substring(0,d),t);a&&(c=pw(a.entitySet,g),w=a.functionImport,b=a.name,h=!c?null:c.entityType)}return v>0?(u=ui(tv,h),u.entitySet=c,u.functionImport=w,u.containerName=b,u):h?(u=ui(io,h),u.entitySet=c,u.functionImport=w,u.containerName=b,u):e(n.value)&&!gi(r,t)&&(k=n.value[0],!pu(k)&&(ov(k)||!i))?ui(io,null):ui(tv,r)}return null},utt=function(n,t,i,r,u){var e,f,o;if(!d(n))return n;if(u=u||"minimalmetadata",e=n[ro],f=rtt(n,t,r),ot(f)&&(f.contentTypeOdata=u),o=null,f){delete n[ro],o=f.type;switch(f.kind){case io:return cv(n,f,e,t,i);case rv:return ntt(n,o,e,t,i);case iv:return gn(n,o,e,i);case uv:return itt(n,e);case fv:return ttt(n,e)}}return rf(n,f,e,t,i)},yv=["type","etag","media_src","edit_media","content_type","media_etag"],pv=function(n,t){var u=/\/\$links\//,i={},r=n.__metadata,f=t&&u.test(t.request.requestUri);return ho(n,r&&r.properties,i,f),i},ftt=function(n,t){var i,u,r,f;if(n)for(i=0,u=yv.length;i<u;i++)r=yv[i],f=ii+(tf[r]||r),dr(f,null,n[r],t)},ho=function(n,t,i,r){var u,f;for(u in n)f=n[u],u==="__metadata"?ftt(f,i):u.indexOf(".")===-1?r&&u==="uri"?ott(f,i):ett(u,f,t,i,r):i[u]=f},ett=function(n,i,r,u){var e=r&&r[n]||{properties:t,type:t},f=hs(i,e);if(pu(i)||!i){dr(ir,n,f,u),u[n]=i;return}if(vr(i,f)||gh(i)){ctt(n,i,u);return}if(!f&&dh(i)){stt(n,i,u);return}if(kh(i,f)){vu(f)&&dr(ir,n,f,u),htt(n,i,u);return}u[n]={},dr(ir,null,f,u[n]),ho(i,e.properties,u[n])},ott=function(n,t){t.url=n},stt=function(n,t,i){dr(ev,n,t.__deferred.uri,i)},htt=function(n,t,i){i[n]=[];var r=e(t)?t:t.results;ho(r,null,i[n])},ctt=function(n,t,i){if(vr(t)){i[n]=[];for(var u=e(t)?t:t.results,r=0,f=u.length;r<f;r++)wv(n,u[r],!0,i);return}wv(n,t,!1,i)},wv=function(n,t,i,r){var f=t.__metadata&&t.__metadata.uri,u;if(f){ltt(n,f,i,r);return}if(u=pv(t),i){r[n].push(u);return}r[n]=u},ltt=function(n,t,i,r){var u=n+rn;if(i){r[u]=r[u]||[],r[u].push(t);return}r[u]=t},dr=function(n,i,r,u){r!==t&&(i?u[i+"@"+n]=r:u[n]=r)},bv="application/json",kv=nr(bv),dv=function(n){var r=[],t,i,u;for(t in n)for(i=0,u=n[t].length;i<u;i++)r.push(g({metadata:t},n[t][i]));return r},att=function(n,t,i,r){var c,f,l,u,o,s,v,e,h,a;if(n&&typeof n=="object")if(f=n.__metadata,f&&(f.actions&&(f.actions=dv(f.actions)),f.functions&&(f.functions=dv(f.functions)),c=f&&f.type),l=bt(c,t)||gi(c,t),l){if(o=l.property,o)for(s=0,v=o.length;s<v;s++)if(e=o[s],h=e.name,u=n[h],e.type==="Edm.DateTime"||e.type==="Edm.DateTimeOffset"){if(u){if(u=i(u),!u)throw{message:"Invalid date/time value"};n[h]=u}}else e.type==="Edm.Time"&&(n[h]=he(u))}else if(r)for(a in n)u=n[a],typeof u=="string"&&(n[a]=i(u)||u);return n},gv=function(n){if(n){var t=n.properties.odata;return t==="nometadata"||t==="minimalmetadata"||t==="fullmetadata"}return!1},vtt=function(n,t){for(var u={collections:[]},r,e,i=0,f=n.EntitySets.length;i<f;i++)r=n.EntitySets[i],e={title:r,href:c(r,t)},u.collections.push(e);return{workspaces:[u]}},ytt=/^\/Date\((-?\d+)(\+|-)?(\d+)?\)\/$/,ptt=function(n){var t,i;return n<0?(t="-",n=-n):t="+",i=Math.floor(n/60),n=n-60*i,t+a(i,2)+":"+a(n,2)},wtt=function(n){var i=n&&ytt.exec(n),t,r,u;if(i&&(t=new Date(s(i[1])),i[2]&&(r=s(i[3]),i[2]==="-"&&(r=-r),u=t.getUTCMinutes(),t.setUTCMinutes(u-r),t.__edmType="Edm.DateTimeOffset",t.__offset=ptt(r)),!isNaN(t.valueOf())))return t},btt=function(t,i,r){var f=it(r.recognizeDates,t.recognizeDates),h=it(r.inferJsonLightFeedAsObject,t.inferJsonLightFeedAsObject),e=r.metadata,o=r.dataServiceVersion,s=wtt,u=typeof i=="string"?n.JSON.parse(i):i;if(ct("3.0",o)===o){if(gv(r.contentType))return utt(u,e,f,h,r.contentType.properties.odata);s=wu}return u=ib(u.d,function(n,t){return att(t,e,s,f)}),u=nit(u,r.dataServiceVersion),gtt(u,r.response.requestUri)},ny=function(t){var i,r=Date.prototype.toJSON;try{Date.prototype.toJSON=function(){return ph(this)},i=n.JSON.stringify(t,dtt)}finally{Date.prototype.toJSON=r}return i},ktt=function(n,i,r){var e=r.dataServiceVersion||"1.0",o=it(r.useJsonLight,n.useJsonLight),u=r.contentType=r.contentType||kv,f;return u&&u.mediaType===kv.mediaType?(f=i,o||gv(u))?(r.dataServiceVersion=ct(e,"3.0"),f=pv(i,r),ny(f)):(ct("3.0",e)===e&&(u.properties.odata="verbose",r.contentType=u),ny(f)):t},dtt=function(n,t){return t&&t.__edmType==="Edm.Time"?wh(t):t},gtt=function(n,t){var i=d(n)&&!n.__metadata&&e(n.EntitySets);return i?vtt(n,t):n},nit=function(n,t){return t&&t.lastIndexOf(";")===t.length-1&&(t=t.substr(0,t.length-1)),t&&t!=="1.0"||e(n)&&(n={results:n}),n},ff=li(btt,ktt,bv,ci);ff.recognizeDates=!1,ff.useJsonLight=!1,ff.inferJsonLightFeedAsObject=!1,r.jsonHandler=ff;var gr="multipart/mixed",tit=/^HTTP\/1\.\d (\d{3}) (.*)$/i,iit=/^([^()<>@,;:\\"\/[\]?={} \t]+)\s?:\s?(.*)/,co=function(){return Math.floor((1+Math.random())*65536).toString(16).substr(1)},ty=function(n){return n+co()+"-"+co()+"-"+co()},iy=function(n){return n.handler.partHandler},ry=function(n){var t=n.boundaries;return t[t.length-1]},rit=function(n,t,i){var r=i.contentType.properties.boundary;return{__batchResponses:uy(t,{boundaries:[r],handlerContext:i})}},uit=function(n,t,i){var r=i.contentType=i.contentType||nr(gr);if(r.mediaType===gr)return fit(t,i)},uy=function(n,t){var f="--"+ry(t),u,o,s,r,e,i;for(ef(n,t,f),rr(n,t),u=[];o!=="--"&&t.position<n.length;){if(s=fy(n,t),r=nr(s["Content-Type"]),r&&r.mediaType===gr){t.boundaries.push(r.properties.boundary);try{e=uy(n,t)}catch(h){h.response=ey(n,t,f),e=[h]}u.push({__changeResponses:e}),t.boundaries.pop(),ef(n,t,"--"+ry(t))}else{if(!r||r.mediaType!=="application/http")throw{message:"invalid MIME part type "};rr(n,t),i=ey(n,t,f);try{i.statusCode>=200&&i.statusCode<=299?iy(t.handlerContext).read(i,t.handlerContext):i={message:"HTTP request failed",response:i}}catch(h){i=h}u.push(i)}o=n.substr(t.position,2),rr(n,t)}return u},fy=function(n,t){var r={},i,u,f;do f=t.position,u=rr(n,t),i=iit.exec(u),i!==null?r[i[1]]=i[2]:t.position=f;while(u&&i);return ee(r),r},ey=function(n,t,i){var o=t.position,r=tit.exec(rr(n,t)),u,f,e;return r?(u=r[1],f=r[2],e=fy(n,t),rr(n,t)):t.position=o,{statusCode:u,statusText:f,headers:e,body:ef(n,t,"\r\n"+i)}},rr=function(n,t){return ef(n,t,"\r\n")},ef=function(n,t,i){var u=t.position||0,r=n.length;if(i){if(r=n.indexOf(i,u),r===-1)return null;t.position=r+i.length}else t.position=r;return n.substring(u,r)},fit=function(n,t){var o;if(!aw(n))throw{message:"Data is not a batch object."};for(var r=ty("batch_"),f=n.__batchRequests,u="",i=0,e=f.length;i<e;i++)u+=of(r,!1)+oy(f[i],t);return u+=of(r,!0),o=t.contentType.properties,o.boundary=r,u},of=function(n,t){var i="\r\n--"+n;return t&&(i+="--"),i+"\r\n"},oy=function(n,t,i){var s=n.__changeRequests,r,f,o,h,u;if(e(s)){if(i)throw{message:"Not Supported: change set nested in other change set"};for(f=ty("changeset_"),r="Content-Type: "+gr+"; boundary="+f+"\r\n",o=0,h=s.length;o<h;o++)r+=of(f,!1)+oy(s[o],t,!0);r+=of(f,!0)}else r="Content-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\n",u=g({},t),u.handler=li,u.request=n,u.contentType=null,sc(n,iy(t),u),r+=eit(n);return r},eit=function(n){var t=(n.method?n.method:"GET")+" "+n.requestUri+" HTTP/1.1\r\n",i;for(i in n.headers)n.headers[i]&&(t=t+i+": "+n.headers[i]+"\r\n");return t+="\r\n",n.body&&(t+=n.body),t};r.batchHandler=li(rit,uit,gr,ci),lo=[r.jsonHandler,r.atomHandler,r.xmlHandler,r.textHandler],ao=function(n,t,i){for(var r=0,u=lo.length;r<u&&!lo[r][n](t,i);r++);if(r===u)throw{message:"no handler for data"};},r.defaultSuccess=function(t){n.alert(n.JSON.stringify(t))},r.defaultError=uu,r.defaultHandler={read:function(n,t){n&&ot(n.body)&&n.headers["Content-Type"]&&ao("read",n,t)},write:function(n,t){ao("write",n,t)},maxDataServiceVersion:ci,accept:"application/atomsvc+xml;q=0.8, application/json;odata=fullmetadata;q=0.7, application/json;q=0.5, */*;q=0.1"},r.defaultMetadata=[],r.read=function(n,t,i,u,f,e){var o;return o=n instanceof String||typeof n=="string"?{requestUri:n}:n,r.request(o,t,i,u,f,e)},r.request=function(n,t,i,u,f,e){t=t||r.defaultSuccess,i=i||r.defaultError,u=u||r.defaultHandler,f=f||r.defaultHttpClient,e=e||r.defaultMetadata,n.recognizeDates=it(n.recognizeDates,r.jsonHandler.recognizeDates),n.callbackParameterName=it(n.callbackParameterName,r.defaultHttpClient.callbackParameterName),n.formatQueryString=it(n.formatQueryString,r.defaultHttpClient.formatQueryString),n.enableJsonpCallback=it(n.enableJsonpCallback,r.defaultHttpClient.enableJsonpCallback),n.useJsonLight=it(n.useJsonLight,r.jsonHandler.enableJsonpCallback),n.inferJsonLightFeedAsObject=it(n.inferJsonLightFeedAsObject,r.jsonHandler.inferJsonLightFeedAsObject);var o={metadata:e,recognizeDates:n.recognizeDates,callbackParameterName:n.callbackParameterName,formatQueryString:n.formatQueryString,enableJsonpCallback:n.enableJsonpCallback,useJsonLight:n.useJsonLight,inferJsonLightFeedAsObject:n.inferJsonLightFeedAsObject};try{return sc(n,u,o),lw(n,t,i,u,f,o)}catch(s){i(s)}},r.parseMetadata=function(n){return nv(null,n)},r.batchHandler.partHandler=r.defaultHandler;var ft=null,oit=function(){var t={v:this.valueOf(),t:"[object Date]"},n;for(n in this)t[n]=this[n];return t},sit=function(n,t){var r,i;if(t&&t.t==="[object Date]"){r=new Date(t.v);for(i in t)i!=="t"&&i!=="v"&&(r[i]=t[i]);t=r}return t},sf=function(n,t){return n.name+"#!#"+t},sy=function(n,t){return t.replace(n.name+"#!#","")},y=function(n){this.name=n};y.create=function(t){if(y.isSupported())return ft=ft||n.localStorage,new y(t);throw{message:"Web Storage not supported by the browser"};},y.isSupported=function(){return!!n.localStorage},y.prototype.add=function(n,t,i,r){r=r||this.defaultError;var u=this;this.contains(n,function(f){f?o(r,{message:"key already exists",key:n}):u.addOrUpdate(n,t,i,r)},r)},y.prototype.addOrUpdate=function(i,r,u,f){var s,h,e;if(f=f||this.defaultError,i instanceof Array)f({message:"Array of keys not supported"});else{s=sf(this,i),h=Date.prototype.toJSON;try{e=r,e!==t&&(Date.prototype.toJSON=oit,e=n.JSON.stringify(r)),ft.setItem(s,e),o(u,i,r)}catch(c){c.code===22||c.number===2147942414?o(f,{name:"QUOTA_EXCEEDED_ERR",error:c}):o(f,c)}finally{Date.prototype.toJSON=h}}},y.prototype.clear=function(n,t){var i,r,u,f;t=t||this.defaultError;try{for(i=0,r=ft.length;r>0&&i<r;)u=ft.key(i),f=sy(this,u),u!==f?(ft.removeItem(u),r=ft.length):i++;o(n)}catch(e){o(t,e)}},y.prototype.close=function(){},y.prototype.contains=function(n,t,i){i=i||this.defaultError;try{var r=sf(this,n),u=ft.getItem(r);o(t,u!==null)}catch(f){o(i,f)}},y.prototype.defaultError=uu,y.prototype.getAllKeys=function(n,t){var r,i,e,u,f;t=t||this.defaultError,r=[];try{for(i=0,e=ft.length;i<e;i++)u=ft.key(i),f=sy(this,u),u!==f&&r.push(f);o(n,r)}catch(s){o(t,s)}},y.prototype.mechanism="dom",y.prototype.read=function(i,r,u){if(u=u||this.defaultError,i instanceof Array)u({message:"Array of keys not supported"});else try{var e=sf(this,i),f=ft.getItem(e);f=f!==null&&f!=="undefined"?n.JSON.parse(f,sit):t,o(r,i,f)}catch(s){o(u,s)}},y.prototype.remove=function(n,t,i){if(i=i||this.defaultError,n instanceof Array)i({message:"Batches not supported"});else try{var r=sf(this,n);ft.removeItem(r),o(t)}catch(u){o(i,u)}},y.prototype.update=function(n,t,i,r){r=r||this.defaultError;var u=this;this.contains(n,function(f){f?u.addOrUpdate(n,t,i,r):o(r,{message:"key not found",key:n})},r)};var hy=n.mozIndexedDB||n.webkitIndexedDB||n.msIndexedDB||n.indexedDB,hit=n.IDBKeyRange||n.webkitIDBKeyRange,cy=n.IDBTransaction||n.webkitIDBTransaction||{},ly=cy.READ_ONLY||"readonly",ur=cy.READ_WRITE||"readwrite",vt=function(n,t){return function(i){var r=n||t,u,f;if(r){if(Object.prototype.toString.call(i)==="[object IDBDatabaseException]"){if(i.code===11){r({name:"QuotaExceededError",error:i});return}r(i);return}try{f=i.target.error||i,u=f.name}catch(e){u=i.type==="blocked"?"IndexedDBBlocked":"UnknownError"}r({name:u,error:i})}}},cit=function(n,t,i){var u=n.name,f="_datajs_"+u,r=hy.open(f);r.onblocked=i,r.onerror=i,r.onupgradeneeded=function(){var n=r.result;n.objectStoreNames.contains(u)||n.createObjectStore(u)},r.onsuccess=function(n){var f=r.result,e;if(!f.objectStoreNames.contains(u)){if("setVersion"in f){e=f.setVersion("1.0"),e.onsuccess=function(){var n=e.transaction;n.oncomplete=function(){t(f)},f.createObjectStore(u,null,!1)},e.onerror=i,e.onblocked=i;return}n.target.error={name:"DBSchemaMismatch"},i(n);return}f.onversionchange=function(n){n.target.close()},t(f)}},fi=function(n,t,i,r){var u=n.name,f=n.db,e=vt(r,n.defaultError);if(f){i(f.transaction(u,t));return}cit(n,function(r){n.db=r,i(r.transaction(u,t))},e)},w=function(n){this.name=n};w.create=function(n){if(w.isSupported())return new w(n);throw{message:"IndexedDB is not supported on this browser"};},w.isSupported=function(){return!!hy},w.prototype.add=function(n,t,i,r){var e=this.name,o=this.defaultError,u=[],f=[];n instanceof Array?(u=n,f=t):(u=[n],f=[t]),fi(this,ur,function(s){s.onabort=vt(r,o,n,"add"),s.oncomplete=function(){n instanceof Array?i(u,f):i(n,t)};for(var h=0;h<u.length&&h<f.length;h++)s.objectStore(e).add({v:f[h]},u[h])},r)},w.prototype.addOrUpdate=function(n,t,i,r){var e=this.name,o=this.defaultError,u=[],f=[];n instanceof Array?(u=n,f=t):(u=[n],f=[t]),fi(this,ur,function(s){var h,c;for(s.onabort=vt(r,o),s.oncomplete=function(){n instanceof Array?i(u,f):i(n,t)},h=0;h<u.length&&h<f.length;h++)c={v:f[h]},s.objectStore(e).put(c,u[h])},r)},w.prototype.clear=function(n,t){var i=this.name,r=this.defaultError;fi(this,ur,function(u){u.onerror=vt(t,r),u.oncomplete=function(){n()},u.objectStore(i).clear()},t)},w.prototype.close=function(){this.db&&(this.db.close(),this.db=null)},w.prototype.contains=function(n,t,i){var r=this.name,u=this.defaultError;fi(this,ly,function(f){var e=f.objectStore(r),o=e.get(n);f.oncomplete=function(){t(!!o.result)},f.onerror=vt(i,u)},i)},w.prototype.defaultError=uu,w.prototype.getAllKeys=function(n,t){var i=this.name,r=this.defaultError;fi(this,ur,function(u){var e=[],f;u.oncomplete=function(){n(e)},f=u.objectStore(i).openCursor(),f.onerror=vt(t,r),f.onsuccess=function(n){var t=n.target.result;t&&(e.push(t.key),t["continue"].call(t))}},t)},w.prototype.mechanism="indexeddb",w.prototype.read=function(n,i,r){var f=this.name,e=this.defaultError,u=n instanceof Array?n:[n];fi(this,ly,function(o){var h=[],s,c,l;for(o.onerror=vt(r,e,n,"read"),o.oncomplete=function(){n instanceof Array?i(u,h):i(u[0],h[0])},s=0;s<u.length;s++)c=o.objectStore(f),l=c.get.call(c,u[s]),l.onsuccess=function(n){var i=n.target.result;h.push(i?i.v:t)}},r)},w.prototype.remove=function(n,t,i){var u=this.name,f=this.defaultError,r=n instanceof Array?n:[n];fi(this,ur,function(n){var e,o;for(n.onerror=vt(i,f),n.oncomplete=function(){t()},e=0;e<r.length;e++)o=n.objectStore(u),o["delete"].call(o,r[e])},i)},w.prototype.update=function(n,t,i,r){var e=this.name,o=this.defaultError,u=[],f=[];n instanceof Array?(u=n,f=t):(u=[n],f=[t]),fi(this,ur,function(s){var h,c,l;for(s.onabort=vt(r,o),s.oncomplete=function(){n instanceof Array?i(u,f):i(n,t)},h=0;h<u.length&&h<f.length;h++)c=s.objectStore(e).openCursor(hit.only(u[h])),l={v:f[h]},c.pair={key:u[h],value:l},c.onsuccess=function(n){var t=n.target.result;t?t.update(n.target.pair.value):s.abort()}},r)},ei=function(n){var e=[],r=[],i={},u,f;this.name=n,u=function(n){return n||this.defaultError},f=function(n,i){var r;return(n instanceof Array&&(r="Array of keys not supported"),(n===t||n===null)&&(r="Invalid key"),r)?(o(i,{message:r}),!1):!0},this.add=function(n,t,r,e){e=u(e),f(n,e)&&(i.hasOwnProperty(n)?e({message:"key already exists",key:n}):this.addOrUpdate(n,t,r,e))},this.addOrUpdate=function(n,s,h,c){if(c=u(c),f(n,c)){var l=i[n];l===t&&(l=e.length>0?e.splice(0,1):r.length),r[l]=s,i[n]=l,o(h,n,s)}},this.clear=function(n){r=[],i={},e=[],o(n)},this.contains=function(n,t){var r=i.hasOwnProperty(n);o(t,r)},this.getAllKeys=function(n){var t=[],r;for(r in i)t.push(r);o(n,t)},this.read=function(n,t,e){if(e=u(e),f(n,e)){var s=i[n];o(t,n,r[s])}},this.remove=function(n,s,h){if(h=u(h),f(n,h)){var c=i[n];c!==t&&(c===r.length-1?r.pop():(r[c]=t,e.push(c)),delete i[n],r.length===0&&(e=[])),o(s)}},this.update=function(n,t,r,e){e=u(e),f(n,e)&&(i.hasOwnProperty(n)?this.addOrUpdate(n,t,r,e):e({message:"key not found",key:n}))}},ei.create=function(n){return new ei(n)},ei.isSupported=function(){return!0},ei.prototype.close=function(){},ei.prototype.defaultError=uu,ei.prototype.mechanism="memory",ay={indexeddb:w,dom:y,memory:ei},yt.defaultStoreMechanism="best",yt.createStore=function(n,t){t||(t=yt.defaultStoreMechanism),t==="best"&&(t=y.isSupported()?"dom":"memory");var i=ay[t];if(i)return i.create(n);throw{message:"Failed to create store",name:n,mechanism:t};};var lit=function(n,t){var i=n.indexOf("?")>=0?"&":"?";return n+i+t},ait=function(n,t){var i=n.indexOf("?"),r="";return i>=0&&(r=n.substr(i),n=n.substr(0,i)),n[n.length-1]!=="/"&&(n+="/"),n+t+r},vy=function(n,t){return{method:"GET",requestUri:n,user:t.user,password:t.password,enableJsonpCallback:t.enableJsonpCallback,callbackParameterName:t.callbackParameterName,formatQueryString:t.formatQueryString}},git=function(n,t){var u=-1,r=n.indexOf("?"),i;return r!==-1&&(i=n.indexOf("?"+t+"=",r),i===-1&&(i=n.indexOf("&"+t+"=",r)),i!==-1&&(u=i+t.length+2)),u},vit=function(n,t,i,r){return yy(n,t,[],i,r)},yy=function(n,i,u,f,e){var s=vy(n,i),o=r.request(s,function(n){var t=n.__next,r=n.results;u=u.concat(r),t?o=yy(t,i,u,f,e):f(u)},e,t,i.httpClient,i.metadata);return{abort:function(){o.abort()}}},yit=function(n){var i=this,u=n.source;return i.identifier=op(encodeURI(decodeURI(u))),i.options=n,i.count=function(n,f){var e=i.options;return r.request(vy(ait(u,"$count"),e),function(t){var i=s(t.toString());isNaN(i)?f({message:"Count is NaN",count:i}):n(i)},f,t,e.httpClient,e.metadata)},i.read=function(n,t,r,f){var e="$skip="+n+"&$top="+t;return vit(lit(u,e),i.options,r,f)},i},pit=function(n,t){var r=wit(n,t),i,u;r&&(i=r.i-t.i,u=i+(n.c-n.d.length),n.d=n.d.concat(t.d.slice(i,u)))},wit=function(n,t){var r=n.i+n.c,u=t.i+t.c,i=n.i>t.i?n.i:t.i,f=r<u?r:u,e;return f>=i&&(e={i:i,c:f-i}),e},py=function(n,i){if(n===t||typeof n!="number")throw{message:"'"+i+"' must be a number."};if(isNaN(n)||n<0||!isFinite(n))throw{message:"'"+i+"' must be greater than or equal to zero."};},bit=function(n,i){if(n!==t){if(typeof n!="number")throw{message:"'"+i+"' must be a number."};if(isNaN(n)||n<=0||!isFinite(n))throw{message:"'"+i+"' must be greater than zero."};}},wy=function(n,i){if(n!==t&&(typeof n!="number"||isNaN(n)||!isFinite(n)))throw{message:"'"+i+"' must be a number."};},vo=function(n,t){for(var i=0,r=n.length;i<r;i++)if(n[i]===t)return n.splice(i,1),!0;return!1},by=function(n){var t=0,r=typeof n,i;if(r==="object"&&n)for(i in n)t+=i.length*2+by(n[i]);else t=r==="string"?n.length*2:8;return t},ky=function(n,t,i){return n=Math.floor(n/i)*i,t=Math.ceil((t+1)/i)*i,{i:n,c:t-n}},hf="destroy",vi="idle",dy="init",yo="read",po="prefetch",wo="write",nu="cancel",oi="end",bo="error",yi="start",gy="wait",np="clear",tu="done",iu="local",tp="save",ip="source",fr=function(n,t,i,r,u,f,e){var h,c,o=this,l,s;return o.p=t,o.i=r,o.c=u,o.d=f,o.s=yi,o.canceled=!1,o.pending=e,o.oncomplete=null,o.cancel=function(){if(i){var n=o.s;n!==bo&&n!==oi&&n!==nu&&(o.canceled=!0,s(nu,h))}},o.complete=function(){s(oi,h)},o.error=function(n){o.canceled||s(bo,n)},o.run=function(n){c=n,o.transition(o.s,h)},o.wait=function(n){s(gy,n)},l=function(t,i,r){switch(t){case yi:i!==dy&&n(o,t,i,r);break;case gy:n(o,t,i,r);break;case nu:n(o,t,i,r),o.fireCanceled(),s(oi);break;case bo:n(o,t,i,r),o.canceled=!0,o.fireRejected(r),s(oi);break;case oi:if(o.oncomplete)o.oncomplete(o);o.canceled||o.fireResolved(),n(o,t,i,r);break;default:n(o,t,i,r)}},s=function(n,t){o.s=n,h=t,l(n,c,t)},o.transition=s,o};fr.prototype.fireResolved=function(){var n=this.p;n&&(this.p=null,n.resolve(this.d))},fr.prototype.fireRejected=function(n){var t=this.p;t&&(this.p=null,t.reject(n))},fr.prototype.fireCanceled=function(){this.fireRejected({canceled:!0,message:"Operation canceled"})},rp=function(i){var it=dy,y={counts:0,netReads:0,prefetches:0,cacheReads:0},c=[],b=[],p=[],nt=0,l=!1,k=af(i.cacheSize,1048576),a=0,h=0,d=0,tt=k===0,r=af(i.pageSize,50),ut=af(i.prefetchSize,r),ht="1.0",f,ft=0,w=i.source,v,u;typeof w=="string"&&(w=new yit(i)),w.options=i,v=yt.createStore(i.name,i.mechanism),u=this,u.onidle=i.idle,u.stats=y,u.count=function(){var n,i,t;if(f)throw f;return(n=hu(),i=!1,l)?(o(function(){n.resolve(a)}),n.promise()):(t=w.count(function(i){t=null,y.counts++,n.resolve(i)},function(r){t=null,n.reject(g(r,{canceled:i}))}),g(n.promise(),{cancel:function(){t&&(i=!0,t.abort(),t=null)}}))},u.clear=function(){if(f)throw f;if(c.length===0){var n=hu(),t=new fr(ii,n,!1);return et(t,c),n.promise()}return c[0].p},u.filterForward=function(n,t,i){return lt(n,t,i,!1)},u.filterBack=function(n,t,i){return lt(n,t,i,!0)},u.readRange=function(n,t){if(py(n,"index"),py(t,"count"),f)throw f;var i=hu(),r=new fr(ui,i,!0,n,t,[],0);return et(r,b),g(i.promise(),{cancel:function(){r.cancel()}})},u.ToObservable=u.toObservable=function(){if(!n.Rx||!n.Rx.Observable)throw{message:"Rx library not available - include rx.js"};if(f)throw f;return n.Rx.Observable.CreateWithDisposable(function(n){var t=!1,i=0,f=function(i){t||n.OnError(i)},e=function(o){if(!t){for(var s=0,h=o.length;s<h;s++)n.OnNext(o[s]);o.length<r?n.OnCompleted():(i+=r,u.readRange(i,r).then(e,f))}};return u.readRange(i,r).then(e,f),{Dispose:function(){t=!0}}})};var rt=function(n){return function(t){f={message:n,error:t};for(var i=0,r=b.length;i<r;i++)b[i].fireRejected(f);for(i=0,r=c.length;i<r;i++)c[i].fireRejected(f);b=c=null}},e=function(n){if(n!==it){it=n;for(var i=c.concat(b,p),t=0,r=i.length;t<r;t++)i[t].run(it)}},ct=function(){var n=new di;return v.clear(function(){nt=0,l=!1,a=0,h=0,d=0,tt=k===0,y={counts:0,netReads:0,prefetches:0,cacheReads:0},u.stats=y,v.close(),n.resolve()},function(t){n.reject(t)}),n},kt=function(n){var t=vo(c,n);t||(t=vo(b,n),t||vo(p,n)),ft--,e(vi)},dt=function(n){var t=new di,u=!1,i=w.read(n,r,function(i){var r={i:n,c:i.length,d:i};t.resolve(r)},function(n){t.reject(n)});return g(t,{cancel:function(){i&&(i.abort(),u=!0,i=null)}})},lt=function(n,t,i,e){if(n=s(n),t=s(t),isNaN(n))throw{message:"'index' must be a valid number.",index:n};if(isNaN(t))throw{message:"'count' must be a valid number.",count:t};if(f)throw f;n=Math.max(n,0);var h=hu(),o=[],a=!1,l=null,v=function(n,f){a||(t>=0&&o.length>=t?h.resolve(o):l=u.readRange(n,f).then(function(u){for(var l,a,y,p,s=0,c=u.length;s<c&&(t<0||o.length<t);s++)l=e?c-s-1:s,a=u[l],i(a)&&(y={index:n+l,item:a},e?o.unshift(y):o.push(y));!e&&u.length<f||e&&n<=0?h.resolve(o):(p=e?Math.max(n-r,0):n+f,v(p,r))},function(n){h.reject(n)}))},c=ky(n,n,r),y=e?c.i:n,p=e?n-c.i+1:c.i+c.c-n;return v(y,p),g(h.promise(),{cancel:function(){l&&l.cancel(),a=!0}})},at=function(){u.onidle&&ft===0&&u.onidle()},gt=function(n){if(!l&&ut!==0&&!tt&&(p.length===0||p[0]&&p[0].c!==-1)){var t=new fr(ri,null,!0,n,ut,null,ut);et(t,p)}},et=function(n,t){n.oncomplete=kt,t.push(n),ft++,n.run(it)},ni=function(n){var r=!1,i=g(new di,{cancel:function(){r=!0}}),u=vt(i,"Read page from store failure");return v.contains(n,function(f){if(!r){if(f){v.read(n,function(n,u){r||i.resolve(u!==t,u)},u);return}i.resolve(!1)}},u),i},ti=function(n,t){var e=!1,i=g(new di,{cancel:function(){e=!0}}),r=vt(i,"Save page to store failure"),u=function(){i.resolve(!0)},f;return t.c>0?(f=by(t),tt=k>=0&&k<nt+f,tt?u():v.addOrUpdate(n,t,function(){pt(t,f),st(u,r)},r)):(pt(t,0),st(u,r)),i},st=function(n,t){var i={actualCacheSize:nt,allDataLocal:l,cacheSize:k,collectionCount:a,highestSavedPage:h,highestSavedPageSize:d,pageSize:r,sourceId:w.identifier,version:ht};v.addOrUpdate("__settings",i,n,t)},vt=function(n){return function(){n.resolve(!1)}},pt=function(n,t){var i=n.c,u=n.i;i===0?h===u-r&&(a=h+d):(h=Math.max(h,u),h===u&&(d=i),nt+=t,i<r&&!a&&(a=u+i)),l||a!==h+d||(l=!0)},wt=function(n,t,i,r){var u=n.canceled&&t!==oi;return u&&t===nu&&r&&r.cancel&&r.cancel(),u},ii=function(n,t,i){var r=n.transition;if(i!==hf)return e(hf),!0;switch(t){case yi:r(np);break;case oi:at();break;case np:ct().then(function(){n.complete()}),n.wait();break;default:return!1}return!0},ri=function(n,t,i,u){var o,f;if(!wt(n,t,i,u)){if(o=n.transition,i!==po)return i===hf?t!==nu&&n.cancel():i===vi&&e(po),!0;switch(t){case yi:p[0]===n&&o(iu,n.i);break;case tu:f=n.pending,f>0&&(f-=Math.min(f,u.c)),l||f===0||u.c<r||tt?n.complete():(n.pending=f,o(iu,u.i+r));break;default:return bt(n,t,i,u,!0)}}return!0},ui=function(n,t,i,u){var f,o,s;if(!wt(n,t,i,u)){if(f=n.transition,i!==yo&&t!==yi)return i===hf?t!==yi&&n.cancel():i!==wo&&e(yo),!0;switch(t){case yi:(i===vi||i===po)&&(e(yo),n.c>0?(o=ky(n.i,n.c,r),f(iu,o.i)):f(tu,n));break;case tu:pit(n,u),s=n.d.length,n.c===s||u.c<r?(y.cacheReads++,gt(u.i+u.c),n.complete()):f(iu,u.i+r);break;default:return bt(n,t,i,u,!1)}}return!0},bt=function(n,t,i,r,u){var s=n.error,o=n.transition,h=n.wait,f;switch(t){case oi:at();break;case iu:f=ni(r).then(function(t,i){n.canceled||(t?o(tu,i):o(ip,r))});break;case ip:f=dt(r).then(function(t){n.canceled||(u?y.prefetches++:y.netReads++,o(tp,t))},s);break;case tp:i!==wo&&(e(wo),f=ti(r.i,r).then(function(t){n.canceled||(!t&&u&&(n.pending=0),o(tu,r)),e(vi)}));break;default:return!1}return f&&(n.canceled?f.cancel():n.s===t&&h(f)),!0};return v.read("__settings",function(n,t){if(ot(t)){var i=t.version;if(!i||i.indexOf("1.")!==0){rt("Unsupported cache store version "+i)();return}r!==t.pageSize||w.identifier!==t.sourceId?ct().then(function(){e(vi)},rt("Unable to clear store during initialization")):(nt=t.actualCacheSize,l=t.allDataLocal,k=t.cacheSize,a=t.collectionCount,h=t.highestSavedPage,d=t.highestSavedPageSize,ht=i,e(vi))}else st(function(){e(vi)},rt("Unable to write settings during initialization."))},rt("Unable to read settings from store.")),u},yt.createDataCache=function(n){if(bit(n.pageSize,"pageSize"),wy(n.cacheSize,"cacheSize"),wy(n.prefetchSize,"prefetchSize"),!ot(n.name))throw{message:"Undefined or null name",options:n};if(!ot(n.source))throw{message:"Undefined source",options:n};return new rp(n)}})(this) | |
// Copyright (c) Microsoft Corporation | |
// All rights reserved. | |
// Licensed under the Apache License, Version 2.0 (the ""License""); you may not use this file except in compliance with | |
// the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 | |
// | |
// THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, | |
// INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | |
// MERCHANTABLITY OR NON-INFRINGEMENT. | |
// | |
// See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. | |
// Simple OData query builder based on DataJS and jQuery. | |
// Declare namespaces. | |
var OData = OData || {}; | |
OData.explorer = OData.explorer || {}; | |
OData.explorer.constants = OData.explorer.constants || {}; | |
// Constants. | |
OData.explorer.constants.queryTimeout = 30 * 1000; | |
OData.explorer.constants.defaultTop = 20; | |
OData.explorer.constants.displayErrorMessageDuration = 20 * 1000; | |
// The version. | |
OData.explorer.version = "1.1.0"; | |
/// <summary> | |
/// Extends the built in String class with a format function if one is not already defined. | |
/// <code> | |
/// var input = '{0} and {1}'; | |
/// var output = input.format('you', 'I') = 'you and I' | |
/// </code> | |
/// </summary> | |
if (!String.prototype.format) { | |
String.prototype.format = function () { | |
var args = arguments; | |
return this.replace(/{(\d+)}/g, function (match, number) { | |
return typeof args[number] != 'undefined' ? args[number] : match; | |
}); | |
}; | |
} | |
/// <summary> | |
/// Gives the child object a copy of the parent object's prototype. | |
/// </summary> | |
/// <param name="base" type="Method">The base method whose prototype will be copied.</param> | |
/// <param name="child" type="Method">The child method who will get a copy of the parent's prototype.</param> | |
/// <returns type="Method">The augmented child method is returned.</returns> | |
OData.extend = function (base, child) { | |
child.prototype = new base(); | |
child.prototype.constructor = child; | |
child.base = base.prototype; | |
return child; | |
}; | |
/// <summary> | |
/// Clean the OData endpoint url from extra / and $metadata. | |
/// </summary> | |
/// <param name ="url" type="String">The endpoint url.</param> | |
/// <returns type="String">The cleaned endpoint url.</returns> | |
OData.explorer._cleanODataEndpointUrl = function (url) { | |
var metadataString = '$metadata'; | |
// Check if it ends with the word $metadata. | |
if (url.indexOf(metadataString, url.length - metadataString.length) !== -1) { | |
url = url.replace(metadataString, ''); | |
} | |
if (url[url.length - 1] !== '/') { | |
url += '/'; | |
} | |
return url; | |
}; | |
// ------------------------------------------------------------------------------ | |
// The filters used to make meaningful queries to the service. | |
// ------------------------------------------------------------------------------ | |
/// <summary> | |
/// Where clause filter options base class. | |
/// </summary> | |
OData.explorer.FilterOptions = function () { | |
this.options = { | |
encodeUrlComponents: false | |
}; | |
this.values = []; | |
}; | |
/// <summary> | |
/// Where clause filter base class init method. | |
/// </summary> | |
OData.explorer.FilterOptions.prototype.init = function (options) { | |
for (var name in options) { | |
this.options[name] = options[name]; | |
} | |
}; | |
/// <summary> | |
/// Gets the filter options. | |
/// </summary> | |
/// <returns type="Array">An array with the filter objects.</returns> | |
OData.explorer.FilterOptions.prototype.getFilterOptions = function () { | |
return this.values; | |
}; | |
/// <summary> | |
/// Gets the where query. | |
/// Queries we want to be able to generate (examples): | |
/// Orders?$filter=startswith(Employee/FirstName, 'A') eq true | |
/// Orders?$filter=Employee/FirstName ne 'A' | |
/// Regions?$filter=Territories/any(x: x/RegionID eq 1) | |
/// Regions?$filter=Territories/any(x: substringof('so', x/TerritoryDescription) eq true) | |
/// </summary> | |
/// <param name="propertiesListNames">The list of property names.</param> | |
/// <param name="filterId">The id of the filter.</param> | |
/// <param name="value">The value of the property.</param> | |
/// <param name="propertiesListMultiplicityIsTrue">Multiplicity check.</param> | |
/// <returns type="String">The query string for the specified where filter.</returns> | |
OData.explorer.FilterOptions.prototype.getWhereQuery = function (propertiesListNames, filterId, value, propertiesListMultiplicityIsTrue) { | |
if (!propertiesListNames || propertiesListNames.length === 0) { | |
return ''; | |
} | |
// Clean the input value by doubling the single ' with another one before and finally by escaping it. | |
// Example: alert( escape("http://hello ' world") ); // displays: http%3A//hello%20%27%27%20world | |
// First replace all the ' with '' (not "). | |
value = String(value).replace(new RegExp("'", 'g'), "''"); | |
// Finally encode the value. | |
if (this.options.encodeUrlComponents) { | |
value = encodeURIComponent(value); | |
} | |
var filter = this.values[filterId]; | |
if (typeof propertiesListNames === 'string' || propertiesListNames.length == 1) { | |
// We have only one property. | |
return filter.stringFormat.format(propertiesListNames, value); | |
} | |
// We are handling navigation properties. | |
var lastProperty = propertiesListNames[propertiesListNames.length - 1]; | |
var secondLastElementIndex = propertiesListNames.length - 2; // We previously checked that the length is >= 2 | |
var query = lastProperty; | |
// Check if the previous navigation property has multiplicity, to know if we need to add the x/ or not. | |
if (propertiesListMultiplicityIsTrue[secondLastElementIndex]) { | |
query = 'x/' + query; | |
} | |
// Goal: Orders?$filter=startswith(Employee/FirstName, 'A') eq true | |
// While the navigations have a 1:1 multiplicity, keep recursing and adding them because the function filter | |
// has to be added only at the end. | |
while (secondLastElementIndex >= 0 && !propertiesListMultiplicityIsTrue[secondLastElementIndex]) { | |
query = this.createNavigationNoMultiplicityWhereQuery(propertiesListNames[secondLastElementIndex], query); | |
secondLastElementIndex--; | |
} | |
// Done building the "Employee/FirstName" now add the filter startswith([...], 'A') eq true | |
query = filter.stringFormat.format(query, value); | |
// Keep adding the rest of the properties | |
for (var i = secondLastElementIndex; i >= 0; i--) { | |
if (propertiesListMultiplicityIsTrue[i]) { | |
query = this.createNavigationAnyWhereQuery(propertiesListNames[i], query); | |
} else { | |
query = this.createNavigationNoMultiplicityWhereQuery(propertiesListNames[i], query); | |
} | |
} | |
return query; | |
}; | |
/// <summary> | |
/// Creates an "in any" where query clause against a navigation property. | |
/// e.g. Foo.svc/Bar?$filter=Users/any(x: x/IsHappy eq true) | |
/// </summary> | |
/// <param name="navigationProperty">The property name.</param> | |
/// <param name="propertyWhereQuery">The desired value(s) of the property.</param> | |
/// <returns type="String">Part of the query string for that specific navigation property.</returns> | |
OData.explorer.FilterOptions.prototype.createNavigationAnyWhereQuery = function (navigationProperty, propertyWhereQuery) { | |
return navigationProperty + '/any(x: ' + propertyWhereQuery + ')'; | |
}; | |
/// <summary> | |
/// Creates a basic where query clause against a navigation property. | |
/// e.g. Foo.svc/Bar?$filter=Users/Name ne 'a' | |
/// </summary> | |
/// <param name="navigationProperty">The property name.</param> | |
/// <param name="propertyWhereQuery">The desired value of the property.</param> | |
/// <returns type="String">Part of the query string for that specific property.</returns> | |
OData.explorer.FilterOptions.prototype.createNavigationNoMultiplicityWhereQuery = function (navigationProperty, propertyWhereQuery) { | |
return navigationProperty + '/' + propertyWhereQuery; | |
}; | |
/// <summary> | |
/// Null where clause filter class. | |
/// </summary> | |
OData.explorer.NullFilterOptions = OData.extend(OData.explorer.FilterOptions, function (options) { | |
this.init(options); | |
this.values = [ | |
{ errorMessage: 'You are not able to query on this property.' } | |
]; | |
}); | |
/// <summary> | |
/// Gets the where query, which for null is an empty string. | |
/// </summary> | |
OData.explorer.NullFilterOptions.prototype.getWhereQuery = function () { | |
return ''; | |
}; | |
/// <summary> | |
/// Boolean where clause filter class. | |
/// </summary> | |
OData.explorer.BooleanFilterOptions = OData.extend(OData.explorer.FilterOptions, function (options) { | |
this.init(options); | |
this.values = [ | |
{ displayName: 'is true', stringFormat: '{0} eq true', inputType: false }, | |
{ displayName: 'is false', stringFormat: '{0} eq false', inputType: false } | |
]; | |
}); | |
/// <summary> | |
/// FloatingPoint where clause filter class. | |
/// </summary> | |
OData.explorer.FloatingPointFilterOptions = OData.extend(OData.explorer.FilterOptions, function (options) { | |
this.init(options); | |
this.values = [ | |
{ displayName: 'round equals', stringFormat: 'round({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'floor equals', stringFormat: 'floor({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'ceiling equals', stringFormat: 'ceiling({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'equals', stringFormat: '{0} eq {1}', inputType: 'double' }, | |
{ displayName: 'not equals', stringFormat: '{0} ne {1}', inputType: 'double' }, | |
{ displayName: 'greater than', stringFormat: '{0} gt {1}', inputType: 'double' }, | |
{ displayName: 'greater than or equal to', stringFormat: '{0} ge {1}', inputType: 'double' }, | |
{ displayName: 'less than', stringFormat: '{0} lt {1}', inputType: 'double' }, | |
{ displayName: 'less than or equal to', stringFormat: '{0} le {1}', inputType: 'double' } | |
]; | |
}); | |
/// <summary> | |
/// Integer where clause filter class. | |
/// </summary> | |
OData.explorer.IntegerFilterOptions = OData.extend(OData.explorer.FilterOptions, function (options) { | |
this.init(options); | |
this.values = [ | |
{ displayName: 'equals', stringFormat: '{0} eq {1}', inputType: 'int' }, | |
{ displayName: 'not equals', stringFormat: '{0} ne {1}', inputType: 'int' }, | |
{ displayName: 'greater than', stringFormat: '{0} gt {1}', inputType: 'int' }, | |
{ displayName: 'greater than or equal to', stringFormat: '{0} ge {1}', inputType: 'int' }, | |
{ displayName: 'less than', stringFormat: '{0} lt {1}', inputType: 'int' }, | |
{ displayName: 'less than or equal to', stringFormat: '{0} le {1}', inputType: 'int' } | |
]; | |
}); | |
/// <summary> | |
/// Date and time where clause filter class. | |
/// </summary> | |
OData.explorer.DateTimeFilterOptions = OData.extend(OData.explorer.FilterOptions, function (options) { | |
this.init(options); | |
this.values = [ | |
{ | |
displayName: 'before', | |
stringFormat: "{0} le datetime'{1}'", | |
inputType: false, | |
inputTypeOptions: ['now', 'yesterday', 'a week ago', 'a month ago', 'tomorrow', 'next week', 'next month'] | |
}, | |
{ | |
displayName: 'after', | |
stringFormat: "{0} ge datetime'{1}'", | |
inputType: false, | |
inputTypeOptions: ['now', 'yesterday', 'a week ago', 'a month ago', 'tomorrow', 'next week', 'next month'] | |
}, | |
{ displayName: 'year equals', stringFormat: 'year({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'month number equals', stringFormat: 'month({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'day number equals', stringFormat: 'day({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'hour equals', stringFormat: 'hour({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'minute equals', stringFormat: 'minute({0}) eq {1}', inputType: 'int' }, | |
{ displayName: 'second equals', stringFormat: 'second({0}) eq {1}', inputType: 'int' } | |
]; | |
}); | |
/// <summary> | |
/// Gets the where query for DateTime objects. | |
/// </summary> | |
/// <param name="propertiesListNames">The list of property names.</param> | |
/// <param name="filterId">The id of the filter.</param> | |
/// <param name="value">The value of the property.</param> | |
/// <param name="propertiesListMultiplicityIsTrue">Multiplicity check.</param> | |
OData.explorer.DateTimeFilterOptions.prototype.getWhereQuery = function (propertiesList, filterId, value, propertiesListMultiplicityIsTrue) { | |
switch (parseInt(filterId)) { | |
case 0: | |
case 1: { | |
var time = new Date(); | |
var now = new Date(); | |
switch (parseInt(value)) { | |
case 0: // now | |
break; | |
case 1: // yesterday | |
time.setDate(now.getDate() - 1); | |
break; | |
case 2: // a week ago | |
time.setDate(now.getDate() - 7); | |
break; | |
case 3: // a month ago | |
time.setMonth(now.getMonth() - 1); | |
break; | |
case 4: // tomorrow | |
time.setDate(now.getDate() + 1); | |
break; | |
case 5: // next week | |
time.setDate(now.getDate() + 7); | |
break; | |
case 6: // next month | |
time.setMonth(now.getMonth() + 1); | |
break; | |
default: | |
return OData.explorer.DateTimeFilterOptions.base.getWhereQuery.call( | |
this, propertiesList, filterId, value, propertiesListMultiplicityIsTrue); | |
} | |
return OData.explorer.DateTimeFilterOptions.base.getWhereQuery.call( | |
this, propertiesList, filterId, time.toISOString(), propertiesListMultiplicityIsTrue); | |
} | |
} | |
return OData.explorer.DateTimeFilterOptions.base.getWhereQuery.call( | |
this, propertiesList, filterId, value, propertiesListMultiplicityIsTrue); | |
}; | |
/// <summary> | |
/// GUID where clause filter class. | |
/// </summary> | |
/// <param name="options">The options object.</param> | |
OData.explorer.GuidFilterOptions = OData.extend(OData.explorer.FilterOptions, function (options) { | |
this.init(options); | |
this.values = [ | |
{ displayName: 'equals', stringFormat: "{0} eq guid'{1}'", inputType: 'guid' }, | |
{ displayName: 'not equals', stringFormat: "{0} ne guid'{1}'", inputType: 'guid' } | |
]; | |
}); | |
/// <summary> | |
/// String where clause filter class. | |
/// </summary> | |
/// <param name="options">The options object.</param> | |
OData.explorer.StringFilterOptions = OData.extend(OData.explorer.FilterOptions, function (options) { | |
this.init(options); | |
this.values = [ | |
{ displayName: 'equals', stringFormat: "{0} eq '{1}'", inputType: 'string' }, | |
{ displayName: 'not equals', stringFormat: "{0} ne '{1}'", inputType: 'string' }, | |
{ displayName: 'in (; separated)', stringFormat: "{0} eq '{1}'", inputType: 'string' }, | |
{ displayName: 'case-insensitive equals', stringFormat: "tolower({0}) eq tolower('{1}')", inputType: 'string' }, | |
{ displayName: 'case-insensitive does not equal', stringFormat: "tolower({0}) eq tolower('{1}')", inputType: 'string' }, | |
{ displayName: 'starts with', stringFormat: "startswith({0}, '{1}') eq true", inputType: 'string' }, | |
{ displayName: 'does not start with', stringFormat: "startswith({0}, '{1}') eq false", inputType: 'string' }, | |
{ displayName: 'ends with', stringFormat: "endswith({0}, '{1}') eq true", inputType: 'string' }, | |
{ displayName: 'does not end with', stringFormat: "endswith({0}, '{1}') eq false", inputType: 'string' }, | |
{ displayName: 'contains', stringFormat: "substringof('{1}', {0}) eq true", inputType: 'string' }, | |
{ displayName: 'has length', stringFormat: "length({0}) eq {1}", inputType: 'int' } | |
]; | |
}); | |
/// <summary> | |
/// Gets the where query for String objects. | |
/// </summary> | |
/// <param name="propertiesListNames">The list of property names.</param> | |
/// <param name="filterId">The id of the filter.</param> | |
/// <param name="value">The value of the property.</param> | |
/// <param name="propertiesListMultiplicityIsTrue">Multiplicity check.</param> | |
OData.explorer.StringFilterOptions.prototype.getWhereQuery = function (propertiesList, filterId, value, propertiesListMultiplicityIsTrue) { | |
var index = parseInt(filterId); | |
var filter = this.values[index]; | |
switch (filter.displayName) { | |
case 'in (; separated)': { | |
var valueSegments = value.split(';'); | |
var finalValue = []; | |
for (var i = 0; i < valueSegments.length; i++) { | |
finalValue.push(OData.explorer.StringFilterOptions.base.getWhereQuery.call( | |
this, propertiesList, filterId, valueSegments[i].trim(), propertiesListMultiplicityIsTrue)); | |
} | |
return finalValue.join(' or '); | |
} | |
} | |
return OData.explorer.StringFilterOptions.base.getWhereQuery.call( | |
this, propertiesList, filterId, value, propertiesListMultiplicityIsTrue); | |
}; | |
/// <summary> | |
/// Where clause filter class. | |
/// </summary> | |
/// <param name="options">The options object.</param> | |
OData.explorer.WhereFilterOptions = function (options) { | |
this['Null'] = new OData.explorer.NullFilterOptions(options); | |
this['Edm.Boolean'] = new OData.explorer.BooleanFilterOptions(options); | |
this['Edm.Decimal'] = | |
this['Edm.Single'] = | |
this['Edm.Double'] = new OData.explorer.FloatingPointFilterOptions(options); | |
this['Edm.Byte'] = | |
this['Edm.SByte'] = | |
this['Edm.Int16'] = | |
this['Edm.Int32'] = | |
this['Edm.Int64'] = new OData.explorer.IntegerFilterOptions(options); | |
this['Edm.Time'] = | |
this['Edm.DateTime'] = | |
this['Edm.DateTimeOffset'] = new OData.explorer.DateTimeFilterOptions(options); | |
this['Edm.Guid'] = new OData.explorer.GuidFilterOptions(options); | |
this['Edm.String'] = new OData.explorer.StringFilterOptions(options); | |
}; | |
/// <summary> | |
/// Where clause filter class. | |
/// </summary> | |
OData.explorer.WhereFilterOptions.prototype.getFilterHandler = function (type) { | |
if (this[type]) { | |
return this[type]; | |
} else { | |
return this.Null; | |
} | |
}; | |
// ----------------------------------------------------------------------------------- | |
// The query builder class, which knows everything about entities, properties, etc. | |
// ----------------------------------------------------------------------------------- | |
/// <summary> | |
/// Query builder class. | |
/// </summary> | |
/// <param name="oDataUrlEndpoint">The URL of the service endpoint to read the metadata from.</param> | |
/// <param name ="metadataInput" type="Object">The metadata associated with the endpoint. | |
/// If this parameter is passed, we will use it to generate the querybuilder without fetching from the service.</param> | |
/// <param name="options">The options object.</param> | |
OData.explorer.QueryBuilder = function (oDataUrlEndpoint, metadataInput, options) { | |
if (!oDataUrlEndpoint) { | |
throw 'You must specify the OData service endpoint URL.'; | |
} | |
this.options = options || {}; | |
// Constants. | |
this.multiplicityValues = ["0..1", "1", "*"]; | |
this.maxNavigationRecursion = 1; | |
// Metadata and schema variables. | |
this.metadata = metadataInput; | |
this.entities = null; | |
this.association = null; | |
this.namespace = null; | |
this.entitySchema = null; | |
this.entitySet = null; | |
this.associationSet = null; | |
// Query variables. | |
this.oDataUrl = OData.explorer._cleanODataEndpointUrl(oDataUrlEndpoint); | |
this.top = null; | |
this.skip = null; | |
this.selectedEntityId = null; | |
this.whereFilterId = 0; | |
this.whereFilter = []; | |
this.orderByPropertyList = []; | |
this.columnsList = []; | |
this.expandList = []; | |
this.filterOptions = new OData.explorer.WhereFilterOptions(this.options); | |
if (this.metadata) { | |
this._updateMetadata(this.metadata); | |
} | |
}; | |
OData.explorer.QueryBuilder.prototype.initialize = function () { | |
var deferred = $.Deferred(); | |
if (!this.metadata) { | |
OData.read({ requestUri: this.getODataUrl() + '$metadata' }, | |
// Success callback. | |
$.proxy(function (data) { | |
this.metadata = data; | |
this._updateMetadata(this.metadata); | |
deferred.resolve(); | |
}, this), | |
// Error callback. | |
function (err) { | |
var error = JSON.stringify(err); | |
deferred.reject(error); | |
}, | |
OData.metadataHandler); | |
} else { | |
deferred.resolve(); | |
} | |
return deferred; | |
}; | |
/// <summary> | |
/// Updates the metadata. | |
/// </summary> | |
/// <param name="someMetadata">The new metadata to use.</param> | |
OData.explorer.QueryBuilder.prototype._updateMetadata = function (someMetadata) { | |
this.metadata = someMetadata; | |
for (var e in this.metadata.dataServices.schema) { | |
var schema = this.metadata.dataServices.schema[e]; | |
if (schema.entityType) { | |
this.entities = schema.entityType; | |
this.association = schema.association; | |
this.namespace = schema.namespace; | |
} | |
if (schema.entityContainer) { | |
this.entitySchema = schema; | |
this.entitySet = schema.entityContainer[0].entitySet; | |
this.associationSet = schema.entityContainer[0].associationSet; | |
} | |
} | |
this.selectedEntityId = null; | |
this.whereFilterId = 0; | |
this.whereFilter = []; | |
this.orderByPropertyList = []; | |
this.columnsList = []; | |
this.expandList = []; | |
}; | |
/// <summary> | |
/// Set the top value in the final query. | |
/// </summary> | |
/// <param name ="val" type="String">The top value.</param> | |
OData.explorer.QueryBuilder.prototype.setTop = function (val) { | |
this.top = isNaN(parseInt(val)) ? null : parseInt(val); | |
}; | |
/// <summary> | |
/// Set the skip value in the final query. | |
/// </summary> | |
/// <param name ="val" type="String">The skip value.</param> | |
OData.explorer.QueryBuilder.prototype.setSkip = function (val) { | |
this.skip = isNaN(parseInt(val)) ? null : parseInt(val); | |
}; | |
/// <summary> | |
/// Set the selected entity in the final query. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
OData.explorer.QueryBuilder.prototype.setSelectedEntityId = function (entityId) { | |
this.selectedEntityId = entityId; | |
}; | |
/// <summary> | |
/// Add/Change/Remove a property from the expand filter in the OData query. | |
/// </summary> | |
/// <param name ="propertyId" type="Integer">The property id.</param> | |
/// <param name ="val" type="Integer">0 = remove, 1 = add</param> | |
OData.explorer.QueryBuilder.prototype.setExpandProperty = function (propertyId, val) { | |
this._setPropertyValueInArray(this.expandList, propertyId, val); | |
}; | |
/// <summary> | |
/// Add/Change/Remove a property from the select filter in the OData query. | |
/// </summary> | |
/// <param name ="propertyId" type="Integer">The property id.</param> | |
/// <param name ="val" type="Integer">0 = remove, 1 = add</param> | |
OData.explorer.QueryBuilder.prototype.setSelectColumnProperty = function (propertyId, val) { | |
this._setPropertyValueInArray(this.columnsList, propertyId, val); | |
}; | |
/// <summary> | |
/// Add/Change/Remove a property from the orderby filter in the OData query. | |
/// </summary> | |
/// <param name ="propertyId" type="Integer">The property id.</param> | |
/// <param name ="val" type="Integer">0 = do not sort on this property, 1 = sort asc, 2 = sort desc</param> | |
OData.explorer.QueryBuilder.prototype.setOrderByProperty = function (propertyId, val) { | |
if (val !== 0 && val !== 1 && val !== 2) { | |
throw 'Not acceptable sorting value: ' + val; | |
} | |
this._setPropertyValueInArray(this.orderByPropertyList, propertyId, val); | |
}; | |
/// <summary> | |
/// Add/Change/Remove a property from the orderby filter in the OData query. | |
/// </summary> | |
/// <param name ="array" type="array">The array.</param> | |
/// <param name ="propertyId" type="Integer">The property id.</param> | |
/// <param name ="val" type="Integer">0 = do not sort on this property, 1 = sort asc, 2 = sort desc</param> | |
OData.explorer.QueryBuilder.prototype._setPropertyValueInArray = function (array, propertyId, val) { | |
// Try to see if the property is already in the array. | |
for (var i in array) { | |
if (array[i].propertyId == propertyId) { | |
// Remove the property from the array. | |
if (val == 0) { | |
array.splice(i, 1); | |
} else { // Change the value of the property in the array. | |
array[i].value = val; | |
} | |
return; | |
} | |
} | |
// Add new one. | |
var element = { | |
propertyId: propertyId, | |
value: val | |
}; | |
array.push(element); | |
}; | |
/// <summary> | |
/// Clear the expand filter list. | |
/// </summary> | |
OData.explorer.QueryBuilder.prototype.clearExpandProperty = function () { | |
this.expandList.length = 0; | |
}; | |
/// <summary> | |
/// Clear the select filter list. | |
/// </summary> | |
OData.explorer.QueryBuilder.prototype.clearSelectColumnsProperty = function () { | |
this.columnsList.length = 0; | |
}; | |
/// <summary> | |
/// Clear the orderby filter list. | |
/// </summary> | |
OData.explorer.QueryBuilder.prototype.clearOrderByProperty = function () { | |
this.orderByPropertyList.length = 0; | |
}; | |
/// <summary> | |
/// Return the endpoint url. | |
/// </summary> | |
/// <returns type="String">The OData endpoint url.</returns> | |
OData.explorer.QueryBuilder.prototype.getODataUrl = function () { | |
return this.oDataUrl; | |
}; | |
/// <summary> | |
/// Return the parsed metadata object. | |
/// </summary> | |
/// <returns type="Object">The metadata.</returns> | |
OData.explorer.QueryBuilder.prototype.getMetadata = function () { | |
return this.metadata; | |
}; | |
/// <summary> | |
/// Return how deep we can navigate inside navigation properties. | |
/// </summary> | |
/// <returns type="Integer">How deep we can navigate inside navigation properties.</returns> | |
OData.explorer.QueryBuilder.prototype.getMaxNavigationRecursion = function () { | |
return this.maxNavigationRecursion; | |
}; | |
/// <summary> | |
/// Return the selected entity id. | |
/// </summary> | |
/// <returns type="Integer">The selected entity id.</returns> | |
OData.explorer.QueryBuilder.prototype.getSelectedEntityId = function () { | |
return this.selectedEntityId; | |
}; | |
/// <summary> | |
/// Return all the entities' names, excluding abstract entities. | |
/// </summary> | |
/// <returns type="Array">An array with the entities names, ids, and objects.</returns> | |
OData.explorer.QueryBuilder.prototype.getEntitiesNames = function () { | |
var entitiesNames = this._getNamesValueFromEntities(this.entities); | |
var filteredEntitiesNames = []; | |
// We do not display abstract classes. | |
for (var i = 0, l = entitiesNames.length; i < l; i++) { | |
if (!entitiesNames[i].entity.abstract) { | |
filteredEntitiesNames.push(entitiesNames[i]); | |
} | |
} | |
return filteredEntitiesNames; | |
}; | |
/// <summary> | |
/// Return a sorted list of entities' names with padding for hierarchical entitites. | |
/// We need to sort them. They may have a tree structure. This solution is a partial sorting based on the | |
/// assumption that all the children of an entity are allways grouped togehter (but not sorted). | |
/// </summary> | |
/// <param name ="theEntities" type="Array">A list of all the entities.</param> | |
/// <param name ="theInheritanceLevel" type="Integer">Parameter used in the recursion steps to know | |
/// the level of inheritance of the previous entity. The default value is 0.</param> | |
/// <param name ="index" type="Integer">Index in the "theEntities" array. Used in the recursion step. The default value is 0.</param> | |
/// <returns type="Array">An array with the entities' names.</returns> | |
OData.explorer.QueryBuilder.prototype._getNamesValueFromEntities = function (theEntities, theInheritanceLevel, index) { | |
theInheritanceLevel = theInheritanceLevel || 0; | |
index = index || 0; | |
var keys = []; | |
var position = 0; | |
// Default padding for hierarchy up to 4 levels. | |
// If the hierarchy is deeper, new levels will be created automatically. | |
var padding = ['', '. . ', , '. . . . ', , '. . . . . . ']; | |
for (var i = index, l = theEntities.length; i < l; i++) { | |
var level = this._getNumberOfLevelOfInheritance(theEntities[i]); | |
var paddingLevel = this._getNumberOfLevelOfInheritance(theEntities[i], true); | |
// Add new padding levels for very deep hierarchies. | |
if (!padding[paddingLevel]) { | |
padding[paddingLevel] = Array(paddingLevel + 1).join(padding[1]); | |
} | |
if (level < theInheritanceLevel) { | |
// Base step. | |
return keys; | |
} else if (level == theInheritanceLevel) { | |
var entry = { | |
key: i, | |
value: padding[paddingLevel] + theEntities[i].name, | |
inheritanceLevel: level, | |
entity: theEntities[i] | |
}; | |
position = this._locationOf(entry, keys); | |
keys.splice(position, 0, entry); | |
} else if (level > theInheritanceLevel) { | |
// Recursion step. | |
var result = this._getNamesValueFromEntities(theEntities, level, i); | |
var args = [position + 1, 0].concat(result); | |
Array.prototype.splice.apply(keys, args); | |
i += result.length - 1; | |
} | |
} | |
return keys; | |
}; | |
/// <summary> | |
/// Return the location of the element in the dictionary, by value comparison. | |
/// </summary> | |
/// <param name ="element" type="Object">The element.</param> | |
/// <param name ="dictionary" type="Array">The array to be searched.</param> | |
/// <returns type="String">The index in the dictionary.</returns> | |
OData.explorer.QueryBuilder.prototype._locationOf = function (element, dictionary) { | |
for (var i = dictionary.length - 1; i >= 0; i--) { | |
if (dictionary[i].inheritanceLevel == element.inheritanceLevel && | |
dictionary[i].value >= element.value) { | |
return i; | |
} | |
} | |
// Not found. | |
return dictionary.length; | |
}; | |
/// <summary> | |
/// Return the entity by its id. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <returns type="Object">The entity.</returns> | |
OData.explorer.QueryBuilder.prototype._getEntityById = function (entityId) { | |
return this.entities[entityId]; | |
}; | |
/// <summary> | |
/// Return the entity by its name. | |
/// </summary> | |
/// <param name ="entityName" type="String">The entity name.</param> | |
/// <returns type="Object">The entity.</returns> | |
OData.explorer.QueryBuilder.prototype._getEntityByName = function (entityName) { | |
for (var i = this.entities.length - 1; i >= 0; i--) { | |
if (this.entities[i].name == entityName) { | |
return this.entities[i]; | |
} | |
} | |
return undefined; | |
}; | |
/// <summary> | |
/// Return the entity id by its name. | |
/// </summary> | |
/// <param name ="entityName" type="String">The entity name.</param> | |
/// <returns type="Object">The entity.</returns> | |
OData.explorer.QueryBuilder.prototype._getEntityIdByName = function (entityName) { | |
for (var i = this.entities.length - 1; i >= 0; i--) { | |
if (this.entities[i].name == entityName) { | |
return i; | |
} | |
} | |
return undefined; | |
}; | |
/// <summary> | |
/// Return the root base entity for the entity passed as an argument. | |
/// </summary> | |
/// <param name ="entity" type="Object">The entity.</param> | |
/// <returns type="Object">The entity.</returns> | |
OData.explorer.QueryBuilder.prototype._getRootParentEntity = function (entity) { | |
var baseType = entity.baseType; | |
// If it is a hierarchical entity. | |
if (typeof baseType !== 'undefined' && baseType != null) { | |
var baseEntityName = baseType.replace(this.namespace + '.', ''); | |
var baseEntity = this._getEntityByName(baseEntityName); | |
return this._getRootParentEntity(baseEntity); | |
} | |
return entity; | |
}; | |
/// <summary> | |
/// Return the number of levels of class inheritance in the hierarchy of the specified entity. | |
/// </summary> | |
/// <param name ="entity" type="Object">The entity.</param> | |
/// <param name ="skipAbstract" type="Boolean">If true it will not count abstract classes in the inheritance path.</param> | |
/// <returns type="Integer">The number of level of inheritance.</returns> | |
OData.explorer.QueryBuilder.prototype._getNumberOfLevelOfInheritance = function (entity, skipAbstract) { | |
skipAbstract = skipAbstract || false; | |
var baseType = entity.baseType; | |
// If it is a hierarchical entity. | |
if (typeof baseType !== 'undefined' && baseType != null) { | |
var baseEntityName = baseType.replace(this.namespace + '.', ''); | |
var baseEntity = this._getEntityByName(baseEntityName); | |
if (skipAbstract && baseEntity.abstract) { | |
return this._getNumberOfLevelOfInheritance(baseEntity, skipAbstract); | |
} | |
return 1 + this._getNumberOfLevelOfInheritance(baseEntity); | |
} | |
return 0; | |
}; | |
/// <summary> | |
/// Return the properties and navigation properties. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <param name ="onlyProperties" type="Boolean">If true it will not return navigation properties.</param> | |
/// <returns type="Array">An array with the properties and navigation properties (if onlyProperties != false).</returns> | |
OData.explorer.QueryBuilder.prototype.getQueryPropertiesAndNavigationPropertiesForEntity = function (entityId) { | |
var properties = this.getQueryPropertiesForEntity(entityId); | |
var navigationProps = this.getQueryNavigationPropertiesForEntity(entityId); | |
var keys = properties.concat(navigationProps); | |
return keys.sort(this._sortDictionaryByValueComparator); | |
}; | |
/// <summary> | |
/// Return the properties. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
OData.explorer.QueryBuilder.prototype.getQueryPropertiesForEntity = function (entityId) { | |
var keys = []; | |
var index = 0; | |
var properties = this._getPropertyNamesForEntity(entityId); | |
for (var i = 0, l = properties.length; i < l; i++) { | |
keys.push({ | |
key: index++, | |
value: properties[i].value, | |
id: properties[i].key, | |
type: "property" | |
}); | |
} | |
return keys.sort(this._sortDictionaryByValueComparator); | |
}; | |
/// <summary> | |
/// Return the navigation properties. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
OData.explorer.QueryBuilder.prototype.getQueryNavigationPropertiesForEntity = function (entityId) { | |
var keys = []; | |
// The index for navigations start after the properties! | |
var index = this._getPropertyNamesForEntity(entityId).length; | |
var navigationProps = this._getNavigationPropertyNamesForEntity(entityId); | |
for (var i = 0, l = navigationProps.length; i < l; i++) { | |
keys.push({ | |
key: index++, | |
value: navigationProps[i].value, | |
id: navigationProps[i].key, | |
type: "navigationProperty" | |
}); | |
} | |
return keys.sort(this._sortDictionaryByValueComparator); | |
}; | |
/// <summary> | |
/// Compare the two objects by value | |
/// </summary> | |
/// <param name ="element1" type="Object">The first element.</param> | |
/// <param name ="element2" type="Object">The second element.</param> | |
/// <returns type="Integer">-1 if the first element comes first, 0 if they have the same value, 1 otherwise.</returns> | |
OData.explorer.QueryBuilder.prototype._sortDictionaryByValueComparator = function (element1, element2) { | |
var a = element1.value; | |
var b = element2.value; | |
return a < b ? -1 : (a > b ? 1 : 0); | |
}; | |
/// <summary> | |
/// Return the property or navigation property with the specified id. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <param name ="propOrNavPropId" type="Integer">The property or navigation property id.</param> | |
/// <returns type="Array">The property or navigation property.</returns> | |
OData.explorer.QueryBuilder.prototype.getQueryPropertiesAndNavigationPropertiesFromQueryId = function (entityId, propOrNavPropId) { | |
var keys = this.getQueryPropertiesAndNavigationPropertiesForEntity(entityId); | |
for (var i = keys.length - 1; i >= 0; i--) { | |
if (keys[i].key == propOrNavPropId) { | |
return keys[i]; | |
} | |
} | |
return undefined; | |
}; | |
/// <summary> | |
/// Return the keys for the entity. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <returns type="Array">The entity's keys.</returns> | |
OData.explorer.QueryBuilder.prototype.getKeysForEntity = function (entityId) { | |
var e = this.entities[entityId]; | |
var keys = e.key.propertyRef; | |
return this._getNamesValueFromObject(keys); | |
}; | |
/// <summary> | |
/// Return the acceptable properties for the entity, including also the base classes properties. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <returns type="Array">The entity's properties.</returns> | |
OData.explorer.QueryBuilder.prototype._getAcceptableProperties = function (entityId) { | |
var e = this.entities[entityId]; | |
var properties = e.property || []; | |
// If it is a hierarchical entity add the base classes' properties. | |
if (e.baseType) { | |
var baseEntityName = e.baseType.replace(this.namespace + '.', ''); | |
var baseEntityId = this._getEntityIdByName(baseEntityName); | |
var baseProperties = this._getAcceptableProperties(baseEntityId); | |
properties = properties.concat(baseProperties); | |
} | |
return properties; | |
}; | |
/// <summary> | |
/// Return the property names. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <returns type="Array">The entity's properties' names.</returns> | |
OData.explorer.QueryBuilder.prototype._getPropertyNamesForEntity = function (entityId) { | |
var properties = this._getAcceptableProperties(entityId); | |
return this._getNamesValueFromObject(properties); | |
}; | |
/// <summary> | |
/// Return the property. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <param name ="propertyId" type="Integer">The property id.</param> | |
/// <returns type="Object">The entity's property.</returns> | |
OData.explorer.QueryBuilder.prototype._getPropertyForEntity = function (entityId, propertyId) { | |
var properties = this._getAcceptableProperties(entityId); | |
return properties[propertyId]; | |
}; | |
/// <summary> | |
/// Return the property with the specified property name. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <param name ="propertyName" type="String">The property name.</param> | |
/// <returns type="Object">The entity's property.</returns> | |
OData.explorer.QueryBuilder.prototype._getPropertyForEntityFromName = function (entityId, propertyName) { | |
var properties = this._getAcceptableProperties(entityId); | |
for (var i = properties.length - 1; i >= 0; i--) { | |
if (properties[i].name === propertyName) { | |
return properties[i]; | |
} | |
} | |
return undefined; | |
}; | |
/// <summary> | |
/// Return the filter options for the property. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <param name ="propId" type="Integer">The property id.</param> | |
/// <returns type="Object">The filter options.</returns> | |
OData.explorer.QueryBuilder.prototype.getFilterOptionsForProperty = function (entityId, propId) { | |
var properties = this._getAcceptableProperties(entityId); | |
var prop = properties[propId]; | |
return this.filterOptions.getFilterHandler(prop.type).getFilterOptions(); | |
}; | |
/// <summary> | |
/// Return the acceptable navigation properties for the entity, including also the base classes properties | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <returns type="Array">The entity's navigation properties.</returns> | |
OData.explorer.QueryBuilder.prototype._getAcceptableNavigationProperties = function (entityId) { | |
var e = this.entities[entityId]; | |
var navigationProperties = e.navigationProperty || []; | |
// If it is a hierarchical entity add the base classes' properties. | |
if (e.baseType) { | |
var baseEntityName = e.baseType.replace(this.namespace + '.', ''); | |
var baseEntityId = this._getEntityIdByName(baseEntityName); | |
var baseNavigationProperties = this._getAcceptableNavigationProperties(baseEntityId); | |
navigationProperties = navigationProperties.concat(baseNavigationProperties); | |
} | |
return navigationProperties; | |
}; | |
/// <summary> | |
/// Return the navigation property names. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <returns type="Array">The entity's navigation properties.</returns> | |
OData.explorer.QueryBuilder.prototype._getNavigationPropertyNamesForEntity = function (entityId) { | |
var navigationProperties = this._getAcceptableNavigationProperties(entityId); | |
return this._getNamesValueFromObject(navigationProperties); | |
}; | |
/// <summary> | |
/// Return the navigation property. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <param name ="navPropId" type="Integer">The navigation property id.</param> | |
/// <returns type="Object">The entity's navigation property.</returns> | |
OData.explorer.QueryBuilder.prototype._getNavigationPropertyForEntity = function (entityId, navPropId) { | |
var index = this._getPropertyNamesForEntity(entityId).length; | |
var navigationProperties = this._getAcceptableNavigationProperties(entityId); | |
return navigationProperties[navPropId - index]; | |
}; | |
/// <summary> | |
/// Return the navigation property's entity id that it is referring to. | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <param name ="navPropId" type="Integer">The navigation property id.</param> | |
/// <returns type="Object">The entity id.</returns> | |
OData.explorer.QueryBuilder.prototype.getNavigationPropertyReferringEntityId = function (entityId, navPropId) { | |
var navigationProperty = this._getNavigationPropertyForEntity(entityId, navPropId); | |
return this._getReferringEntityIdFromNavigationProperty(navigationProperty); | |
}; | |
/// <summary> | |
/// Return the final OData query url. | |
/// </summary> | |
/// <returns type="String">The query url.</returns> | |
OData.explorer.QueryBuilder.prototype.getGeneratedODataQueryUrl = function () { | |
var url = this.getODataUrl(); | |
// 0 is an acceptable value therefore we need to compare it like this. | |
if (typeof this.selectedEntityId !== "undefined" && this.selectedEntityId != null) { | |
var entityQueryName = this._getEntityQueryName(this.selectedEntityId); | |
if (typeof entityQueryName === "undefined") { | |
throw 'Invalid entity selected with id: ' + this.selectedEntityId; | |
} | |
url += entityQueryName + '?'; | |
} | |
if (typeof this.skip !== "undefined" && this.skip != null) { | |
url += '$skip=' + this.skip + '&'; | |
} | |
if (typeof this.top !== "undefined" && this.top != null) { | |
url += '$top=' + this.top + '&'; | |
} | |
if (this.whereFilter && this.whereFilter.length > 0) { | |
var queryFiltersString = this._getWhereQueryFilter(this.whereFilter); | |
if (typeof queryFiltersString === "undefined") { | |
throw 'Invalid query filters selected with id: ' + JSON.stringify(this.whereFilter); | |
} | |
url += '$filter=' + queryFiltersString + '&'; | |
} | |
if (typeof this.selectedEntityId !== "undefined" && this.selectedEntityId != null) { | |
if (this.orderByPropertyList && this.orderByPropertyList.length > 0) { | |
url += '$orderby='; | |
var sortingOptions = []; | |
for (var i in this.orderByPropertyList) { | |
var propertyId = this.orderByPropertyList[i].propertyId; | |
var value = this.orderByPropertyList[i].value; | |
var propertyName = this._getPropertyForEntity(this.selectedEntityId, propertyId).name; | |
if (propertyName) { | |
switch (value) { | |
case 0: { | |
// Do not order by this propertyId. | |
break; | |
} | |
case 1: { | |
// Sort in asc order. | |
sortingOptions.push(propertyName); | |
break; | |
} | |
case 2: { | |
// Sort in desc order. | |
sortingOptions.push(propertyName + ' desc'); | |
break; | |
} | |
} | |
} | |
} | |
// Separate the elements with a comma ',' and add the '&' at the end. | |
url += sortingOptions.join() + '&'; | |
} | |
if (this.columnsList && this.columnsList.length > 0) { | |
url += '$select='; | |
var selectOptions = []; | |
for (var i in this.columnsList) { | |
var propertyId = this.columnsList[i].propertyId; | |
var propertyName = this._getPropertyForEntity(this.selectedEntityId, propertyId).name; | |
selectOptions.push(propertyName); | |
} | |
// Separate the elements with a comma ',' and add the '&' at the end. | |
url += selectOptions.join() + '&'; | |
} | |
if (this.expandList && this.expandList.length > 0) { | |
url += '$expand='; | |
var expandOptions = []; | |
for (var i in this.expandList) { | |
var navigationPropertyId = this.expandList[i].propertyId; | |
var navigationPropertyName = this._getNavigationPropertyForEntity(this.selectedEntityId, navigationPropertyId).name; | |
expandOptions.push(navigationPropertyName); | |
} | |
// Separate the elements with a comma ',' and add the '&' at the end. | |
url += expandOptions.join() + '&'; | |
} | |
} | |
// Remove the & at the end. | |
var lastUrlCharIndex = url.length - 1; | |
if (url[lastUrlCharIndex] === '&') { | |
url = url.substring(0, lastUrlCharIndex); | |
} | |
return url; | |
}; | |
/// <summary> | |
/// Return the next filter id, used to know which of the filters we are adding/modifying/deleting. | |
/// </summary> | |
/// <returns type="String">The next where filter id.</returns> | |
OData.explorer.QueryBuilder.prototype.getNextWhereId = function () { | |
return 'odataExplorerFilter' + this.whereFilterId++; | |
}; | |
/// <summary> | |
/// Clear the filter list for the OData final query url. | |
/// </summary> | |
OData.explorer.QueryBuilder.prototype.emptyWhereFilter = function () { | |
this.whereFilter = []; | |
}; | |
/// <summary> | |
/// Delete a specific filter in the filter list. | |
/// </summary> | |
/// <param name ="specificId" type="Integer">The filter id, which has to be removed.</param> | |
OData.explorer.QueryBuilder.prototype.removeWhereFilter = function (specificId) { | |
for (var i = this.whereFilter.length - 1; i >= 0; i--) { | |
// Only doing double equals here because sometimes the id is of type string and sometimes int. | |
if (this.whereFilter[i].id == specificId) { | |
this.whereFilter.splice(i, 1); | |
break; | |
} | |
} | |
}; | |
/// <summary> | |
/// Add or update a specific filter in the filter list. | |
/// </summary> | |
/// <param name ="specificId" type="Integer">The filter id, which has to be added or updated.</param> | |
/// <param name ="propListNames" type="Array">A list of property names.</param> | |
/// <param name ="propListIds" type="Array">A list of property ids.</param> | |
/// <param name ="propListReferringEntityIds" type="Array">A list of the referring entity for every | |
/// navigation property in the query.</param> | |
/// <param name ="propFilterId" type="Integer">The property filter id.</param> | |
/// <param name ="val" type="String/Integer">The value for the filter.</param> | |
OData.explorer.QueryBuilder.prototype.addOrUpdateWhereFilter = function (specificId, propListNames, propListIds, propListReferringEntityIds, propFilterId, val) { | |
var whereClause = { | |
id: specificId, | |
propertyListNames: propListNames, | |
propertyListReferringEntityIds: propListReferringEntityIds, | |
propertiesListIds: propListIds, | |
propertyFilterId: propFilterId, | |
value: val | |
}; | |
// Check if element already exist. | |
for (var i = this.whereFilter.length - 1; i >= 0; i--) { | |
if (this.whereFilter[i].id === specificId) { | |
// Update. | |
this.whereFilter[i] = whereClause; | |
return; | |
} | |
} | |
// Element not found: add a new one. | |
this.whereFilter.push(whereClause); | |
}; | |
/// <summary> | |
/// Return a standardized array of objects for the array passed as a parameter. | |
/// </summary> | |
/// <param name ="obj" type="Array">A list of objects with a name property.</param> | |
/// <param name ="startIndex" type="int">The starting index number.</param> | |
/// <returns type="Array">A standardized array of objects for the array passed as a parameter.</returns> | |
OData.explorer.QueryBuilder.prototype._getNamesValueFromObject = function (obj, startIndex) { | |
startIndex = startIndex || 0; | |
var keys = []; | |
for (var i = 0, l = obj.length; i < l; i++) { | |
keys.push({ key: i + startIndex, value: obj[i].name, object: obj[i] }); | |
} | |
return keys; | |
}; | |
/// <summary> | |
/// Return the where filters formatted for the final OData query url. | |
/// </summary> | |
/// <param name ="whereFilterList" type="Array">A list of all the query filters.</param> | |
/// <returns type="String">The where filters formatted for the final OData query url.</returns> | |
OData.explorer.QueryBuilder.prototype._getWhereQueryFilter = function (whereFilterList) { | |
var result = ''; | |
for (var i = 0, l = whereFilterList.length; i < l; i++) { | |
var filter = whereFilterList[i]; | |
var propertyListNames = filter.propertyListNames; | |
var propertiesListIds = filter.propertiesListIds; | |
var propListReferringEntityIds = filter.propertyListReferringEntityIds; | |
var lastPropName = propertyListNames[propertyListNames.length - 1]; | |
var lastPropReferringEntityId = propListReferringEntityIds[propListReferringEntityIds.length - 1]; | |
var propertiesListMultiplicityIsTrue = []; | |
for (var k = 0, t = propertiesListIds.length; k < t; k++) { | |
var referringEntityId = propListReferringEntityIds[k]; | |
var id = propertiesListIds[k]; | |
var element = this.getQueryPropertiesAndNavigationPropertiesFromQueryId(referringEntityId, id); | |
switch (element.type) { | |
case 'navigationProperty': | |
var navigationProperty = this._getNavigationPropertyForEntity(referringEntityId, element.key); | |
var multiplicity = this._getNavigationPropertyMultiplicity(navigationProperty); | |
if (multiplicity <= 1) { | |
propertiesListMultiplicityIsTrue.push(false); | |
} else { | |
propertiesListMultiplicityIsTrue.push(true); | |
} | |
break; | |
case 'property': | |
// Properties do not have multiplicity, because they are not navigations. | |
propertiesListMultiplicityIsTrue.push(false); | |
break; | |
} | |
} | |
var prop = this._getPropertyForEntityFromName(lastPropReferringEntityId, lastPropName); | |
var aQuery = this.filterOptions.getFilterHandler(prop.type).getWhereQuery( | |
propertyListNames, filter.propertyFilterId, filter.value, propertiesListMultiplicityIsTrue); | |
result += aQuery; | |
if (i < l - 1) { | |
result += ' and '; | |
} | |
} | |
return result; | |
}; | |
/// <summary> | |
/// Return the multiplicity for the navigation property. | |
/// </summary> | |
/// <param name ="navigationProperty" type="Object">The navigation property.</param> | |
/// <returns type="String">The multiplicity for the navigation property.</returns> | |
OData.explorer.QueryBuilder.prototype._getNavigationPropertyMultiplicity = function (navigationProperty) { | |
if (!navigationProperty) { | |
return undefined; | |
} | |
var relationshipName = navigationProperty.relationship; | |
var toRoleName = navigationProperty.toRole; | |
var correctAssociationSet = this._getAssociationSetFromRelationshipName(relationshipName); | |
if (!correctAssociationSet) { | |
return undefined; | |
} | |
var correctAssociation = this._getAssociationFromAssociationSet(correctAssociationSet); | |
var multiplicity; | |
if (!correctAssociation) { | |
return undefined; | |
} else if (correctAssociation.end[0].role == toRoleName) { | |
multiplicity = correctAssociation.end[0].multiplicity; | |
} else if (correctAssociation.end[1].role == toRoleName) { | |
multiplicity = correctAssociation.end[1].multiplicity; | |
} else { | |
return undefined; | |
} | |
return this.multiplicityValues.indexOf(multiplicity); | |
}; | |
/// <summary> | |
/// Return the association set for the relationship. | |
/// </summary> | |
/// <param name ="relationshipName" type="String">The relationship name.</param> | |
/// <returns type="String">The association set for the relationship.</returns> | |
OData.explorer.QueryBuilder.prototype._getAssociationSetFromRelationshipName = function (relationshipName) { | |
if (!relationshipName) { | |
return undefined; | |
} | |
for (var i = this.associationSet.length - 1; i >= 0; i--) { | |
if (this.associationSet[i].association == relationshipName) { | |
return this.associationSet[i]; | |
} | |
} | |
return undefined; | |
}; | |
/// <summary> | |
/// Return the association for the association set. | |
/// </summary> | |
/// <param name ="associationSet" type="String">The association set.</param> | |
/// <returns type="String">The association for the association set.</returns> | |
OData.explorer.QueryBuilder.prototype._getAssociationFromAssociationSet = function (associationSet) { | |
if (!associationSet) { | |
return undefined; | |
} | |
for (var i = this.association.length - 1; i >= 0; i--) { | |
if (this.namespace + '.' + this.association[i].name == associationSet.association) { | |
return this.association[i]; | |
} | |
} | |
return undefined; | |
}; | |
/// <summary> | |
/// Return the referring entity for the specified navigation property. | |
/// </summary> | |
/// <param name ="navigationProperty" type="Object">The navigation property.</param> | |
/// <returns type="String">The entity id.</returns> | |
OData.explorer.QueryBuilder.prototype._getReferringEntityIdFromNavigationProperty = function (navigationProperty) { | |
if (!navigationProperty) { | |
return undefined; | |
} | |
var relationshipName = navigationProperty.relationship; | |
var toRoleName = navigationProperty.toRole; | |
var correctAssociationSet; | |
var correctEntitySetName; | |
// Retrieve the id from the entitySet using the associacionSet. | |
correctAssociationSet = this._getAssociationSetFromRelationshipName(relationshipName); | |
if (!correctAssociationSet) { | |
return undefined; | |
} else if (correctAssociationSet.end[0].role == toRoleName) { | |
correctEntitySetName = correctAssociationSet.end[0].entitySet; | |
} else if (correctAssociationSet.end[1].role == toRoleName) { | |
correctEntitySetName = correctAssociationSet.end[1].entitySet; | |
} else { | |
return undefined; | |
} | |
for (var k = this.entitySet.length - 1; k >= 0; k--) { | |
if (this.entitySet[k].name == correctEntitySetName) { | |
var entityName = this.entitySet[k].entityType.slice(this.namespace.length + 1); | |
return this.entities.indexOf(this._getEntityByName(entityName)); | |
} | |
} | |
return undefined; | |
}; | |
/// <summary> | |
/// Return the entity name that has to be used in the final OData query url. | |
/// Example: | |
/// Not hierarchical model: | |
/// sometimes the name gets pluralized ex: Category -> Categories or it stays the same Account -> Account | |
/// Hierarchical model: | |
/// The path would be something like: | |
/// Service.svc/Item/Service.Server where “Server†extends “Device†which extends “Item†| |
/// but the URL takes the form of .../Service.svc/<root base class>/<namespace>.<derived class> and | |
/// all the intermediate classes in the hierarchy are “ignored†(with regards to the URL). | |
/// </summary> | |
/// <param name ="entityId" type="Integer">The entity id.</param> | |
/// <returns type="String">The entity name.</returns> | |
OData.explorer.QueryBuilder.prototype._getEntityQueryName = function (entityId) { | |
var entity = this._getEntityById(entityId); | |
// If it is a hierarchical entity. | |
if (typeof entity.baseType !== 'undefined') { | |
var parentEntity = this._getRootParentEntity(entity); | |
if (!parentEntity.abstract) { | |
return this._getEntitySetQueryNameFromEntityName(parentEntity.name) + '/' + | |
this.namespace + '.' + entity.name; | |
} | |
} | |
return this._getEntitySetQueryNameFromEntityName(entity.name); | |
}; | |
/// <summary> | |
/// Return the entitySet query name from the entity name. | |
/// </summary> | |
/// <param name ="entityName" type="String">The entity name.</param> | |
/// <returns type="String">The entitySet query name from the entity name.</returns> | |
OData.explorer.QueryBuilder.prototype._getEntitySetQueryNameFromEntityName = function (entityName) { | |
if (!entityName) { | |
throw 'Missing required parameter "name".'; | |
} | |
var namespacedName = this.namespace + '.' + entityName; | |
for (var i = this.entitySet.length - 1; i >= 0; i--) { | |
if (this.entitySet[i].entityType === namespacedName) { | |
return this.entitySet[i].name; | |
} | |
} | |
return undefined; | |
}; | |
// ------------------------------------------------------------------------------ | |
// UI display functions and bindings. | |
// ------------------------------------------------------------------------------ | |
/// <summary> | |
/// DataExplorer class which constructs the query builder and loads the query results. | |
/// </summary> | |
/// <param name="options"> | |
/// Required: an array containing the different endpoints. | |
/// Optionals true|false parameters: | |
/// encodeUrlComponents, hideOrderbyFilters, hideColumnFilters, hideExpandFilters | |
/// Optional override methods (examples): | |
/// onUrlChange: function (url) { } | |
/// onSubmit: function (url) { return url; } | |
/// onResults: function (data) { return data; } | |
/// onError: function (error, url) { } | |
/// </param> | |
OData.explorer.DataExplorer = function (options) { | |
if (!options) { | |
throw 'You must specify at least one parameter.'; | |
} | |
this.options = {}; | |
// Set the options. | |
if (options) { | |
for (var option in options) { | |
this.options[option] = options[option]; | |
} | |
} | |
if (options.url || $.isArray(options) && options.length !== 0) { | |
// The options is the array of endpoints. | |
this.options.endpoints = options; | |
} else if (!options.endpoints || | |
(!options.endpoints.url && (!$.isArray(options.endpoints) || options.endpoints.length === 0))) { | |
throw 'You must specify at least one endpoint URL.'; | |
} | |
this.defaultTop = OData.explorer.constants.defaultTop; | |
this.endpoints = this.options.endpoints.url ? [this.options.endpoints] : this.options.endpoints; | |
// Find or create the container. | |
this.$container = $('#queryBuilderContainer'); | |
if (this.$container.size() === 0) { | |
this.$container = $('body').prepend('<div id="queryBuilderContainer" />'); | |
} | |
// Create the control contents. | |
this.$container.empty(); | |
this.$queryBuilder = $('<div id="queryBuilder" />'); | |
this.$results = $('<div id="results"></div>'); | |
this.$container.append(this.$queryBuilder, this.$results); | |
this.$queryBuilder.append('<div id="queryBusy"></div>'); | |
this.$busy = $('#queryBusy', this.$queryBuilder); | |
var $queryBuilderForm = $('<form autocomplete="off" id="queryBuilderForm" />'); | |
this.$queryBuilder.append($queryBuilderForm); | |
$queryBuilderForm.append($([ | |
'<label for="endpoints">Endpoint:</label>', | |
'<select id="endpoints"></select>', | |
'<div id="queryFilters">', | |
'<label for="entities">Select:</label><select id="top"></select>', | |
'<select id="entities"></select>', | |
'<div id="filtersConditions">', | |
'<div id="whereConditions" class="filterContainer">', | |
'<label class="filterLabel">Where:</label><button id="addCondition" class="addCondition">+</button>', | |
'</div>', | |
'<div id="orderByConditions" class="filterContainer">', | |
'<label class="filterLabel">Order by:</label><button id="addOrderByCondition" class="addCondition">+</button>', | |
'<span id="orderByFiltersList" class="filterList"></span> ', | |
'</div>', | |
'<div id="selectConditions" class="filterContainer">', | |
'<label class="filterLabel">Columns:</label><button id="addSelectCondition" class="addCondition">+</button>', | |
'<span id="selectFiltersList" class="filterList"></span> ', | |
'</div>', | |
'<div id="expandConditions" class="filterContainer">', | |
'<label class="filterLabel">Expand:</label><button id="addExpandCondition" class="addCondition">+</button>', | |
'<span id="expandFiltersList" class="filterList"></span> ', | |
'</div>', | |
'</div>', | |
'<div><a id="queryUrl" href="/" target="_blank"></a></div>', | |
'</div>'].join(''))); | |
this.$whereConditions = $('#whereConditions', $queryBuilderForm); | |
this.$orderByConditions = $('#orderByConditions', $queryBuilderForm); | |
this.$addOrderByCondition = $('#addOrderByCondition', $queryBuilderForm); | |
this.$orderByFiltersList = $('#orderByFiltersList', $queryBuilderForm); | |
this.$selectConditions = $('#selectConditions', $queryBuilderForm); | |
this.$addSelectCondition = $('#addSelectCondition', $queryBuilderForm); | |
this.$selectFiltersList = $('#selectFiltersList', $queryBuilderForm); | |
this.$expandConditions = $('#expandConditions', $queryBuilderForm); | |
this.$addExpandCondition = $('#addExpandCondition', $queryBuilderForm); | |
this.$expandFiltersList = $('#expandFiltersList', $queryBuilderForm); | |
this.$filtersConditions = $('#filtersConditions', $queryBuilderForm); | |
this.$entities = $('#entities', $queryBuilderForm); | |
this.$queryFilters = $('#queryFilters', $queryBuilderForm); | |
this.$addCondition = $('#addCondition', $queryBuilderForm); | |
this.$queryUrl = $('#queryUrl', $queryBuilderForm); | |
this.$top = $('#top', $queryBuilderForm); | |
this.$skip = $('#skip', $queryBuilderForm); | |
this.addOptions( | |
[ | |
{ key: 1, value: 'top 1' }, | |
{ key: 10, value: 'top 10' }, | |
{ key: 20, value: 'top 20' }, | |
{ key: 50, value: 'top 50' }, | |
{ key: 100, value: 'top 100' }, | |
], | |
this.$top); | |
this.$endpoints = $('#endpoints', $queryBuilderForm); | |
var endpointOptions = []; | |
var endpointsCount = this.endpoints.length; | |
for (var i = 0; i < endpointsCount; i++) { | |
var endpoint = this.endpoints[i]; | |
endpointOptions.push({ key: endpoint.url, value: endpoint.name || endpoint.url }); | |
}; | |
this.addOptions(endpointOptions, this.$endpoints); | |
this.$queryBuilder.append([ | |
'<div id="queryButtons">', | |
'<button id="submitQuery" class="buttonQuery">Search</button>', | |
'<button id="clearQuery" class="buttonQuery">Reset</button>', | |
'</div>', | |
'<div id="errorMessage" />'].join('')); | |
this.$queryButtons = $('#queryButtons', this.$queryBuilder); | |
this.$errorMessage = $("#errorMessage", this.$queryBuilder); | |
this.$submitQuery = $('#submitQuery', this.$queryBuilder); | |
this.$clearQuery = $('#clearQuery', this.$queryBuilder); | |
// Cache of query builders for different URL's. | |
this.queryBuilders = []; | |
// Set the options. | |
if (this.options.hideOrderbyFilters) { | |
this.$orderByConditions.hide(); | |
} | |
if (this.options.hideColumnFilters) { | |
this.$selectConditions.hide(); | |
} | |
if (this.options.hideExpandFilters) { | |
this.$expandConditions.hide(); | |
} | |
// Event handler for updating the metadata model. | |
this.$endpoints.change($.proxy(function (event) { | |
var url = OData.explorer._cleanODataEndpointUrl($(event.target).val()); | |
this.$queryFilters.hide(); | |
this.$queryButtons.hide(); | |
this.$results.empty(); | |
this.showErrorMessage('Generating the query builder...', -1); | |
if (this.queryBuilders[url]) { | |
this.queryBuilder = this.queryBuilders[url]; | |
this.Reset(); | |
} else { | |
var endpoint = this.endpoints[event.target.selectedIndex]; | |
if (endpoint.provider) { | |
this.queryBuilder = this.queryBuilders[url] = | |
new OData.explorer.QueryBuilder(url, endpoint.provider()); | |
} else { | |
this.queryBuilder = this.queryBuilders[url] = | |
new OData.explorer.QueryBuilder(url); | |
} | |
var promise = this.queryBuilder.initialize(); | |
// The query builder has been successfully initialized. | |
promise.done($.proxy(this.Reset, this)); | |
// The query builder has NOT been successfully initialized. | |
promise.fail($.proxy(function (error) { | |
this.queryBuilders[url] = null; | |
this.Reset(error); | |
}, this)); | |
} | |
}, this)); | |
// Now that we have the event handler lets set the default selection and trigger the handler. | |
this.$endpoints.val(this.$endpoints.find('option:first').val()); | |
this.$endpoints.change(); | |
if (endpointOptions.length === 1) { | |
this.$endpoints.attr('disabled', true); | |
} | |
// Event handler for adding another criteria row when the plus button is clicked. | |
this.$addCondition.click($.proxy(function (event) { | |
// Prevent the default behaviour of the button from submitting the form. | |
event.preventDefault(); | |
this.createNewWhereQuery(); | |
}, this)); | |
// Event handler for setting a skip value. | |
this.$skip.keyup($.proxy(function (event) { | |
var itemId = $(event.target).val(); | |
this.queryBuilder.setSkip(itemId); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
// Event handler for setting a top value. | |
this.$top.change($.proxy(function (event) { | |
var itemId = $(event.target).val(); | |
this.queryBuilder.setTop(itemId); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
// Event handler for adding order by conditions. | |
this.$addOrderByCondition.click($.proxy(function (event) { | |
// Prevent the default behaviour of the button from submitting the form. | |
event.preventDefault(); | |
if (this.$orderByFiltersList.is(":visible")) { | |
// Reset the order by filters when the list is being hidden. | |
this.$orderByFiltersList.find('input[type="checkbox"]').prop('checked', false); | |
this.queryBuilder.clearOrderByProperty(); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
} | |
this.$orderByConditions.toggleClass('listVisible'); | |
}, this)); | |
// Event handler for adding order by columns. | |
this.$orderByFiltersList.on('click', ':input', $.proxy(function (event) { | |
var $e = $(event.target); | |
var propertyId = $e.val(); | |
var isChecked = +$e.is(':checked'); // The + converts the bool to integer. | |
this.queryBuilder.setOrderByProperty(propertyId, isChecked); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
// Event handler for adding select column conditions. | |
this.$addSelectCondition.click($.proxy(function (event) { | |
// Prevent the default behaviour of the button from submitting the form. | |
event.preventDefault(); | |
if (this.$selectFiltersList.is(":visible")) { | |
// Reset the order by filters when the list is being hidden. | |
this.$selectFiltersList.find('input[type="checkbox"]').prop('checked', false); | |
this.queryBuilder.clearSelectColumnsProperty(); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
} | |
this.$selectConditions.toggleClass('listVisible'); | |
}, this)); | |
// Event handler for adding select columns. | |
this.$selectFiltersList.on('click', ':input', $.proxy(function (event) { | |
var $e = $(event.target); | |
var propertyId = $e.val(); | |
var isChecked = +$e.is(':checked'); // The + converts the bool to integer. | |
this.queryBuilder.setSelectColumnProperty(propertyId, isChecked); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
// Event handler for adding expand conditions. | |
this.$addExpandCondition.click($.proxy(function (event) { | |
// Prevent the default behaviour of the button from submitting the form. | |
event.preventDefault(); | |
if (this.$expandFiltersList.is(":visible")) { | |
// Reset the order by filters when the list is being hidden. | |
this.$expandFiltersList.find('input[type="checkbox"]').prop('checked', false); | |
this.queryBuilder.clearExpandProperty(); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
} | |
this.$expandConditions.toggleClass('listVisible'); | |
}, this)); | |
// Event handler for adding expands. | |
this.$expandFiltersList.on('click', ':input', $.proxy(function (event) { | |
var $e = $(event.target); | |
var propertyId = $e.val(); | |
var isChecked = +$e.is(':checked'); // The + converts the bool to integer. | |
this.queryBuilder.setExpandProperty(propertyId, isChecked); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
// Event handler for changing entity selection. | |
this.$entities.change($.proxy(function (event) { | |
var $e = $(event.target); | |
var entityIndex = $e.val(); | |
if (entityIndex >= 0) { | |
this.$queryButtons.show(); | |
} else { | |
this.$queryButtons.hide(); | |
} | |
this.resetQuery(entityIndex); | |
}, this)); | |
// Event handler for clicking the cancel/clear/reset query button. | |
this.$clearQuery.click($.proxy(function () { | |
this.$queryButtons.hide(); | |
this.$entities.val(-1); | |
this.resetQuery(); | |
}, this)); | |
// Event handler for removing a condition. | |
this.$whereConditions.on('click', '.removeCondition', $.proxy(function (event) { | |
var $e = $(event.target); | |
var whereClauseId = $e.data('whereclauseid'); | |
$e.parent().remove(); | |
this.queryBuilder.removeWhereFilter(whereClauseId); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
// Event handler for clicking the submit search query button. | |
this.$submitQuery.click($.proxy(function () { | |
var url = this.getUrl(); | |
if (url) { | |
this.hideErrorMessage(); | |
this.queryData(url); | |
} | |
}, this)); | |
// Event handler for clicking any of the navigation links drop downs and selecting a link option. | |
this.$results.on('change', '.links', $.proxy(function (event) { | |
var $e = $(event.target); | |
var uri = $e.val(); | |
if (uri.indexOf('http') !== -1) { | |
this.queryData(uri, this.linkResultsCallback, $e); | |
} else { | |
var $tr = $e.parents('tr:first'); | |
$tr.next('tr.expandedChild').remove(); | |
$tr.find('span.expandChild').remove(); | |
} | |
}, this)); | |
// Event handler for expanding and collapsing the navigation link child results. | |
this.$results.on('click', '.expandChild', function (event) { | |
var $me = $(this); | |
event.stopPropagation(); // We don't want the event to bubble. | |
$me.parents('tr:first').next('tr:has(table)').slideToggle("fast"); | |
$me.toggleClass('collapsed'); | |
}); | |
// Event handler for when the filter property or navigation propety has changed. | |
this.$queryBuilder.on('change', '.property, .navPropertyProperties', $.proxy(function (event) { | |
var $e = $(event.target); | |
var propertyId = $e.val(); | |
var $whereClause = $e.parent(); | |
var whereClauseId = $whereClause.attr('id'); | |
var navigationPropertiesNumber = 1 + $whereClause.children(".navPropertyProperties").length; | |
// Remove all filters and input filters because the (navigation) property has changed. | |
$e.nextAll().remove(); | |
this.queryBuilder.removeWhereFilter(whereClauseId); | |
if (propertyId >= 0) { | |
var entityReferringId = $e.data("referringentityid"); | |
var queryProperty = this.queryBuilder.getQueryPropertiesAndNavigationPropertiesFromQueryId(entityReferringId, propertyId); | |
if (queryProperty.type == 'property') { | |
var propertyOptions = this.queryBuilder.getFilterOptionsForProperty(entityReferringId, propertyId); | |
// If the only filter of this property is an error message, then display it. | |
if (propertyOptions.length == 1 && propertyOptions[0].errorMessage) { | |
$whereClause.append('<span>' + propertyOptions[0].errorMessage + '</span>'); | |
} else { // If there are possible filters for this properties, display them. | |
var keys = []; | |
for (var i = 0, l = propertyOptions.length; i < l; i++) { | |
keys.push({ key: i, value: propertyOptions[i].displayName }); | |
} | |
var displayThePropertyFilterInput = false; | |
for (var k = 0; k < propertyOptions.length; k++) { | |
if (propertyOptions[k].inputType != false || | |
typeof propertyOptions[k].inputTypeOptions !== 'undefined') { | |
displayThePropertyFilterInput = true; | |
break; | |
} | |
} | |
this.addDropdown( | |
'propertyFilter', | |
keys, | |
$whereClause, | |
entityReferringId, | |
false, | |
displayThePropertyFilterInput); | |
} | |
} else { | |
// Only allow navigation recursion to the maximum depth set in the query builder class. | |
var refEntityId = this.queryBuilder.getNavigationPropertyReferringEntityId(entityReferringId, propertyId); | |
var navigationOptions; | |
if (navigationPropertiesNumber >= this.queryBuilder.getMaxNavigationRecursion()) { | |
navigationOptions = this.queryBuilder.getQueryPropertiesForEntity(refEntityId); | |
} else { | |
navigationOptions = this.queryBuilder.getQueryPropertiesAndNavigationPropertiesForEntity(refEntityId); | |
} | |
// If the navigation property has nothing to display afterwards, then show a message. | |
if (!navigationOptions || navigationOptions.length == 0) { | |
$whereClause.append('<span>No options to query for this navigation property</span>'); | |
} else { | |
this.addDropdown('navPropertyProperties', navigationOptions, $whereClause, refEntityId, false, false); | |
} | |
} | |
} | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
// Event handler for when the property filter has changed. | |
this.$queryBuilder.on('change', '.propertyFilter', $.proxy(function (event) { | |
var $e = $(event.target); | |
// Update the propertyFilterInput or remove it if not needed. | |
var parent = $e.parent(); | |
var whereClauseId = parent.attr('id'); | |
var selectedReferringEntityId = $e.data("referringentityid"); | |
var propertyFilterInput = parent.children('.propertyFilterInput'); | |
var propertyId = $e.prev().children("option:selected").val(); | |
var propertyFilterId = $e.children("option:selected").val(); | |
if (propertyFilterId >= 0) { | |
var propOptions = this.queryBuilder.getFilterOptionsForProperty(selectedReferringEntityId, propertyId); | |
var whereFilter = propOptions[propertyFilterId]; | |
var inputType = whereFilter.inputType; | |
var inputTypeOptions = whereFilter.inputTypeOptions; | |
if (inputType == false) { | |
propertyFilterInput.remove(); | |
if (typeof inputTypeOptions !== 'undefined') { | |
var options = []; | |
for (var i in inputTypeOptions) { | |
options.push({ key: i, value: inputTypeOptions[i] }); | |
} | |
this.addDropdown('propertyFilterInput', options, '#' + whereClauseId, selectedReferringEntityId, false, false); | |
} | |
} else { | |
// Check if we need to remove the dropdown to add an input field. | |
if (propertyFilterInput.is('select')) { | |
propertyFilterInput.remove(); | |
this.addInput('propertyFilterInput', '#' + whereClauseId); | |
propertyFilterInput = parent.children('.propertyFilterInput'); | |
} | |
propertyFilterInput.data("inputType", inputType); | |
} | |
} | |
}, this)); | |
// Event handler for allowing only specific values in the input fields. | |
this.$queryBuilder.on('keypress paste keyup', '.propertyFilterInput', function (event) { | |
var $e = $(event.target); | |
var inputType = $e.data("inputType"); | |
switch (inputType) { | |
case 'int': | |
return OData.explorer.validation.allowOnlyInts(event); | |
case 'double': | |
return OData.explorer.validation.allowOnlyDoubles(event); | |
case 'guid': | |
return OData.explorer.validation.allowOnlyGuids(event); | |
} | |
return true; | |
}); | |
// Event handler for when the filter property or input has changed. | |
this.$queryBuilder.on('change keyup', '.propertyFilterInput, .propertyFilter', $.proxy(function (event) { | |
var $e = $(event.target); | |
var parent = $e.parent(); | |
var id = parent.attr('id'); | |
var $property = parent.children('.property'); | |
var propertyListNames = [$property.children("option:selected").text()]; | |
var propertyListIds = [$property.val()]; | |
var propertyListReferringEntityIds = [$property.data("referringentityid")]; | |
parent.children('.navPropertyProperties').each(function () { | |
var $thisElement = $(this); | |
var selectedPropName = $thisElement.children("option:selected").text(); | |
propertyListNames.push(selectedPropName); | |
propertyListIds.push($thisElement.val()); | |
var selectedReferringEntityId = $thisElement.data("referringentityid"); | |
propertyListReferringEntityIds.push(selectedReferringEntityId); | |
}); | |
var propFilterId = parent.children('.propertyFilter').children("option:selected").val(); | |
var itemText = parent.children('.propertyFilterInput').val(); | |
this.queryBuilder.addOrUpdateWhereFilter(id, propertyListNames, propertyListIds, propertyListReferringEntityIds, propFilterId, itemText); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
}, this)); | |
}; | |
/// <summary> | |
/// Display an error message. | |
/// </summary> | |
/// <param name ="msg" type="String">The message.</param> | |
/// <param name ="delay" type="Integer">How long the message will be on screen. | |
/// A negative numbers leaves the message forever on screen. Default: 10 secs</param> | |
OData.explorer.DataExplorer.prototype.showErrorMessage = function (msg, delay) { | |
delay = delay || OData.explorer.constants.displayErrorMessageDuration; | |
if (delay < 0) { | |
this.$errorMessage.text(msg).addClass('error').show(); | |
} else { | |
this.$errorMessage.text(msg).addClass('error').show().delay(delay).fadeOut('fast'); | |
} | |
}; | |
/// <summary> | |
/// Display an error message. | |
/// </summary> | |
/// <param name ="url" type="String">The new url.</param> | |
OData.explorer.DataExplorer.prototype.hideErrorMessage = function () { | |
this.$errorMessage.hide(); | |
}; | |
/// <summary>Adds a drop down select control.</summary> | |
/// <param name="classId">The CSS class name for the select that will be created.</param> | |
/// <param name="options">The array of options data for the select.</param> | |
/// <param name="appendTo">The element, or selector for the element, to append the select to.</param> | |
/// <param name="referringEntityId">The id of the entity the select refers to.</param> | |
/// <param name="addEmptySelect">Flag indicating if an empty first option should be added.</param> | |
/// <param name="addPropertyFilterInput">Flag indicating if the property filter input should be added.</param> | |
OData.explorer.DataExplorer.prototype.addDropdown = function ( | |
classId, options, appendTo, referringEntityId, addEmptySelect, addPropertyFilterInput) { | |
var select = $('<select class="' + classId + | |
'" data-referringentityid=' + referringEntityId + '/>'); | |
this.addOptions(options, select, addEmptySelect); | |
select.appendTo(appendTo); | |
if (addPropertyFilterInput) { | |
this.addInput('propertyFilterInput', appendTo); | |
} | |
// Trigger change so that the code knows that it has been added. | |
select.trigger('change'); | |
}; | |
/// <summary>Adds options to a drop down select control.</summary> | |
/// <param name="options">The array of options data to generate option tags with.</param> | |
/// <param name="select">The select element, or selector for it, to append the options to.</param> | |
/// <param name="addEmptySelect">Flag indicating if an empty first option should be added.</param> | |
OData.explorer.DataExplorer.prototype.addOptions = function (options, select, addEmptySelect) { | |
if (addEmptySelect) { | |
$('<option />', { value: -1, text: '-- Select --' }).appendTo(select); | |
} | |
$.each(options, function (index, e) { | |
var $option = $('<option />', { value: e.key, text: e.value }); | |
if (typeof e.type !== 'undefined' && e.type == 'navigationProperty') { | |
$option.addClass('navigationDropdown'); | |
} | |
$option.appendTo(select); | |
}); | |
}; | |
/// <summary>Adds an text input control.</summary> | |
/// <param name="classId">The CSS class name for the input that will be created.</param> | |
/// <param name="appendTo">The element, or selector for the element, to append the input to.</param> | |
OData.explorer.DataExplorer.prototype.addInput = function (classId, appendTo) { | |
var s = $('<input type="text" class="' + classId + '"/>'); | |
s.appendTo(appendTo); | |
}; | |
/// <summary>Updates the displayed URL.</summary> | |
/// <param name="url">The URL to update the display with. Defaults to an empty string.</param> | |
OData.explorer.DataExplorer.prototype.updateUrl = function (url) { | |
url = url || ''; | |
var urlToBeUpdated = this.getUrl(); | |
this.$queryUrl.text(url).attr('href', url); | |
if (url && this.options.onUrlChange && urlToBeUpdated != url) { | |
// Raise an event | |
this.options.onUrlChange(url); | |
} | |
}; | |
/// <summary>Retrieve the displayed URL.</summary> | |
OData.explorer.DataExplorer.prototype.getUrl = function () { | |
return this.$queryUrl.attr('href'); | |
}; | |
/// <summary> | |
/// Sets the busy status as indicated. | |
/// </summary> | |
/// <param name ="isBusy" type="Boolean">True to show busy indicator(s) or false to hide them.</param> | |
OData.explorer.DataExplorer.prototype.busy = function (isBusy) { | |
if (isBusy) { | |
this.$busy.show(); | |
} else { | |
this.$busy.hide(); | |
} | |
}; | |
/// <summary> | |
/// Handles updating the UI when the metadata model has been generated for a service endpoint. | |
/// </summary> | |
/// <param name ="error">An error message if there was an error processing the metadata.</param> | |
OData.explorer.DataExplorer.prototype.Reset = function (error) { | |
this.$entities.val(-1); | |
this.resetQuery(); | |
this.hideErrorMessage(); | |
if (error) { | |
this.showErrorMessage(JSON.stringify(error), -1); | |
} else { | |
// set up | |
this.$queryFilters.show(); | |
this.$entities.children('option').remove(); | |
this.addOptions(this.queryBuilder.getEntitiesNames(), this.$entities, true); | |
this.$top.val(this.defaultTop); | |
this.resetQuery(); | |
} | |
}; | |
/// <summary> | |
/// Resets the query. | |
/// </summary> | |
/// <param name ="entityIndex">The index of an entity.</param> | |
OData.explorer.DataExplorer.prototype.resetQuery = function (entityIndex) { | |
this.hideErrorMessage(); | |
this.$queryUrl.hide(); | |
this.updateUrl(); | |
this.$whereConditions.children('div').remove(); | |
this.$filtersConditions.hide(); | |
this.$orderByConditions.removeClass('listVisible'); | |
this.$selectConditions.removeClass('listVisible'); | |
this.$expandConditions.removeClass('listVisible'); | |
this.$orderByFiltersList.empty(); | |
this.$selectFiltersList.empty(); | |
this.$expandFiltersList.empty(); | |
this.$results.empty(); | |
if (this.queryBuilder) { | |
this.queryBuilder.emptyWhereFilter(); | |
this.queryBuilder.clearOrderByProperty(); | |
this.queryBuilder.clearSelectColumnsProperty(); | |
this.queryBuilder.clearExpandProperty(); | |
this.queryBuilder.setTop(this.$top.val()); | |
entityIndex = entityIndex || this.$entities.val(); | |
if (entityIndex && entityIndex >= 0) { | |
// Set the selected entity. | |
this.queryBuilder.setSelectedEntityId(entityIndex); | |
this.updateUrl(this.queryBuilder.getGeneratedODataQueryUrl()); | |
this.$queryUrl.show(); | |
// Set up the more filter options. | |
// Add the order by and select conditions (They are the same). | |
var properties = this.queryBuilder.getQueryPropertiesForEntity(this.queryBuilder.getSelectedEntityId()); | |
for (var i in properties) { | |
var property = properties[i]; | |
// Order by. | |
var orderByHtmlId = 'orderby_' + property.key; | |
var $orderByLabel = $('<label />', { 'for': orderByHtmlId, text: property.value }); | |
$orderByLabel.appendTo(this.$orderByFiltersList); | |
$('<input />', { type: 'checkbox', id: orderByHtmlId, value: property.key }).prependTo($orderByLabel); | |
// Select column. | |
var selectColumnHtmlId = 'selectcolumn_' + property.key; | |
var $selectColumnLabel = $('<label />', { 'for': selectColumnHtmlId, text: property.value }); | |
$selectColumnLabel.appendTo(this.$selectFiltersList); | |
$('<input />', { type: 'checkbox', id: selectColumnHtmlId, value: property.key }).prependTo($selectColumnLabel); | |
}; | |
var navigationProperties = this.queryBuilder.getQueryNavigationPropertiesForEntity(this.queryBuilder.getSelectedEntityId()); | |
for (var i in navigationProperties) { | |
var navigationProperty = navigationProperties[i]; | |
// Expand. | |
var expandHtmlId = 'expand_' + navigationProperty.key; | |
var $expandLabel = $('<label />', { 'for': expandHtmlId, text: navigationProperty.value }); | |
$expandLabel.appendTo(this.$expandFiltersList); | |
$('<input />', { type: 'checkbox', id: selectColumnHtmlId, value: navigationProperty.key }).prependTo($expandLabel); | |
}; | |
// Show the filters. | |
this.$filtersConditions.show(); | |
} else { | |
this.updateUrl(); | |
} | |
} | |
}; | |
/// <summary> | |
/// Creates a new where clause for the query. | |
/// </summary> | |
OData.explorer.DataExplorer.prototype.createNewWhereQuery = function () { | |
// Add a div for the first property. | |
var whereClauseId = this.queryBuilder.getNextWhereId(); | |
var $whereClause = $('<div />', { id: whereClauseId }) | |
.insertBefore(this.$addCondition); | |
$whereClause.append($( | |
'<button />', { 'class': 'removeCondition', 'data-whereclauseid': whereClauseId, text: 'X' })); | |
var selectedEntityId = this.queryBuilder.getSelectedEntityId(); | |
this.addDropdown( | |
'property', | |
this.queryBuilder.getQueryPropertiesAndNavigationPropertiesForEntity(selectedEntityId), | |
$whereClause, | |
this.queryBuilder.getSelectedEntityId(), | |
false, | |
false); | |
}; | |
/// <summary> | |
/// Queries data from the specified URL and passes the results to the results callback handler method. | |
/// </summary> | |
/// <param name ="url">The URL to query data from.</param> | |
/// <param name ="callback">A callback to call with the data, the calling context will be "this".</param> | |
/// <param name ="context">An additional context, besides "this", to pass to the callback.</param> | |
OData.explorer.DataExplorer.prototype.queryData = function (url, callback, context) { | |
if (this.options.onSubmit) { | |
var newUrl = this.options.onSubmit(url); | |
if (newUrl) { | |
url = newUrl; | |
} else { | |
return; | |
} | |
} | |
callback = callback || this.resultsCallback; | |
var me = this; | |
this.busy(true); | |
// First try without jsonp. | |
OData.read( | |
{ requestUri: url, enableJsonpCallback: false, timeoutMS: OData.explorer.constants.queryTimeout }, | |
// Success callback. | |
function (data, request) { | |
callback.call(me, data, context); | |
}, | |
// Error callback. | |
$.proxy(function (err) { | |
// If it fails try with jsonp: | |
// two calls gets spawn at the same time. The goal is to hopefully to get the | |
// fullmetadata, however some services returns an error when asked for fullmetadata, and therefore we have | |
// the fallback. | |
var correctAjax = 0; | |
var errorsAjax = 0; | |
// Error callback. | |
var errorCallback = $.proxy(function (errorFinal) { | |
if (errorsAjax > 0) { | |
this.showErrorMessage(JSON.stringify(errorFinal)); | |
this.busy(false); | |
if (this.options.onError) { | |
this.options.onError(errorFinal, url); | |
} | |
} | |
errorsAjax++; | |
}, this); | |
// First Try | |
// Set the formatQueryString so that it returns application/json;odata=fullmetadata | |
OData.defaultHttpClient.formatQueryString = '$format=application/json;odata=fullmetadata;'; | |
OData.read( | |
{ requestUri: url, enableJsonpCallback: true, timeoutMS: OData.explorer.constants.queryTimeout }, | |
// Success callback. | |
function (data, request) { | |
correctAjax++; | |
callback.call(me, data, context); | |
}, | |
errorCallback); | |
// Second Parallel Try | |
OData.defaultHttpClient.formatQueryString = '$format=json'; | |
OData.read( | |
{ requestUri: url, enableJsonpCallback: true, timeoutMS: OData.explorer.constants.queryTimeout }, | |
// Success callback. | |
function (data, request) { | |
if (correctAjax == 0) { | |
correctAjax++; | |
callback.call(me, data, context); | |
} | |
}, | |
errorCallback); | |
}, this)); | |
}; | |
// ------------------------------------------------------------------------------ | |
// The following functions display the results of the query in the UI. | |
// ------------------------------------------------------------------------------ | |
/// <summary> | |
/// The results callback handler method for when data has been loaded from a link drop down selection. | |
/// </summary> | |
/// <param name ="data">The data from the server.</param> | |
/// <param name ="source"> | |
/// The source of the event, the select box that triggered the load of child navigation data.</param> | |
OData.explorer.DataExplorer.prototype.linkResultsCallback = function (data, source) { | |
try { | |
var results = this.sanitizeDataFormat(data); | |
if (results.length > 0) { | |
// Find or create the expand/collapse span. | |
var $tr = source.parents('tr:first'); | |
var tableTitle = source.children('option:selected').text(); | |
var $td = $tr.find('td:first'); | |
if ($td.find('> .expandChild').size() === 0) { | |
$td.prepend('<span class="expandChild" />'); | |
} else { | |
// Try to find if this table has already been created previously and remove it. | |
$tr.next().find('*[data-tabletitle="' + tableTitle + '"]').remove(); | |
} | |
// Find or create the child data row and populate it. | |
var $row = $tr.next('.expandedChild'); | |
var $childCell; | |
if ($row.size() === 0) { | |
$row = $('<tr class="expandedChild" />'); | |
$tr.after($row); | |
var columnCount = $tr.children().size(); | |
$childCell = $('<td />', { colspan: columnCount }); | |
$row.append($childCell); | |
} else { | |
$childCell = $row.find('td:first'); | |
} | |
// Add the link table before all the others for easier reading. | |
$childCell.append(this.createResultsTable(results, tableTitle)); | |
this.hideErrorMessage(); | |
} else { | |
this.noResults(); | |
} | |
} catch (e) { | |
this.noResults(); | |
} finally { | |
this.busy(false); | |
} | |
}; | |
/// <summary> | |
/// Generates the display table of the specified data. | |
/// </summary> | |
/// <param name ="data">The data to generate the display from.</param> | |
/// <param name ="title">The title of the table.</param> | |
OData.explorer.DataExplorer.prototype.createResultsTable = function (data, title) { | |
var me = this; | |
var $table = $('<table class="defaultResultsFormatting"/>'); | |
if (data && data.length > 0) { | |
var $thead = $('<thead />'); | |
$table.append($thead); | |
var columnCount = 0; | |
// Add the column names. | |
var $headRow = $('<tr/>'); | |
$thead.append($headRow); | |
var result = data[0]; | |
for (var property in result) { | |
var type = typeof result[property]; | |
// DataJS returns the dates as objects and not as strings. | |
if (type === 'string' || type === 'number' || type === 'boolean' || | |
result[property] instanceof Date || !result[property]) { | |
$headRow.append($('<th />', { text: property })); | |
++columnCount; | |
} | |
} | |
var hasLinks = false; | |
var $tbody = $('<tbody />'); | |
$table.append($tbody); | |
$.each(data, function (index, e) { | |
var $bodyRow = $('<tr/>'); | |
$tbody.append($bodyRow); | |
var expandedChildResults = null; | |
var links = []; | |
$.each(e, function (index, property) { | |
var type = typeof property; | |
if (type === 'string' || type === 'number' || type === 'boolean') { | |
$bodyRow.append($('<td />', { text: property })); | |
} else if (property instanceof Date) { // DataJS returns the dates as objects and not as strings. | |
$bodyRow.append($('<td />', { text: property.toDateString() })); | |
} else if (!property) { | |
$bodyRow.append('<td />'); | |
} else if (typeof property === 'object' && property.results && index !== '__metadata') { | |
expandedChildResults = property.results; | |
} else if (property.__deferred) { | |
links.push({ key: property.__deferred.uri, value: index }); | |
hasLinks = true; | |
} | |
}); | |
// Display the links only if there are some. | |
if (links.length !== 0) { | |
columnCount += 2; | |
var $cell = $('<td />'); | |
$bodyRow.prepend($cell); | |
me.addDropdown('links', links, $cell, '', true, false); | |
// Prepend a blank cell for the expand icon. | |
var $expandCell = $('<td/>'); | |
$bodyRow.prepend($expandCell); | |
if (expandedChildResults) { | |
// Add the expand/collapse button. | |
$expandCell.append('<span class="expandChild" />'); | |
// Create a new row for the child results. | |
$bodyRow = $('<tr class="expandedChild" />'); | |
$table.append($bodyRow); | |
var $childCell = $('<td />', { colspan: columnCount }); | |
$bodyRow.append($childCell); | |
$childCell.append(me.createResultsTable(expandedChildResults)); | |
} | |
} | |
}); | |
// Display the links column names only if they exist. | |
if (hasLinks) { | |
$headRow.prepend('<th></th><th>Links</th>'); | |
} | |
// Add a title to the table. | |
if (title) { | |
var $titleRow = $('<tr />'); | |
$thead.prepend($titleRow); | |
$titleRow.append($('<th />', { text: title, colspan: columnCount })); | |
$table.attr('data-tabletitle', title); | |
} | |
} else { | |
this.noResults(); | |
} | |
return $table; | |
}; | |
/// <summary> | |
/// Shows a message indicating there are no results for an attempted data load query. | |
/// </summary> | |
OData.explorer.DataExplorer.prototype.noResults = function () { | |
this.showErrorMessage('No results.'); | |
}; | |
/// <summary> | |
/// The results callback handler method for when data has been loaded. | |
/// </summary> | |
/// <param name ="data">The data from the server.</param> | |
OData.explorer.DataExplorer.prototype.resultsCallback = function (data) { | |
try { | |
this.$results.empty(); | |
var results = this.sanitizeDataFormat(data); | |
if (this.options.onResults) { | |
// User custom handling. | |
var formattedResults = this.options.onResults(results); | |
if (formattedResults) { | |
this.$results.append(formattedResults); | |
} | |
} else { | |
// Default handling. | |
if (results.length > 0) { | |
this.$results.append(this.createResultsTable(results)); | |
} else { | |
// No results. | |
this.noResults(); | |
} | |
} | |
} finally { | |
this.busy(false); | |
} | |
}; | |
/// <summary> | |
/// Sanitizes the format of the data that has been loaded. | |
/// </summary> | |
/// <param name ="data">The data from the server.</param> | |
OData.explorer.DataExplorer.prototype.sanitizeDataFormat = function (data) { | |
var results = []; | |
if (!data) { | |
return results; | |
} | |
// data.results or data should be the only encoding returned by DataJS. | |
if (data.results) { | |
results = data.results; | |
} else if (data.d) { | |
results = data.d.results ? data.d.results : (Array.isArray(data.d) ? data.d : [data.d]); | |
} else if (data.value) { | |
results = data.value; | |
} else if (!Array.isArray(data)) { | |
// DataJS does not return an array if only one element is present. | |
results = [data]; | |
} else { | |
throw 'Unknown results format.'; | |
} | |
return results; | |
}; | |
// Validation namespace. | |
OData.explorer.validation = OData.explorer.validation || {}; | |
/// <summary> | |
/// Event handler for key presses that prevents entering any key stroke which would produce an invalid integer. | |
/// </summary> | |
/// <param name ="event">The event object.</param> | |
OData.explorer.validation.allowOnlyInts = function (event) { | |
var $element = $(event.target); | |
var value = $element.val(); | |
// Allow only backspace and delete to support also Firefox. | |
var key = event.keyCode ? event.keyCode : (event.which ? event.which : event.charCode); | |
// Backspace. | |
if (key == 8) { | |
return true; | |
} | |
// Allow one - sign at the beginning. | |
if (key == 45) { | |
if (value.length == 0) { | |
// Just allow to regularly add it. | |
return true; | |
} else { | |
// Add it manually at the beginning of the string. | |
value = value[0] == '-' ? value.substr(1) : '-' + value; | |
$element.val(value); | |
return false; | |
} | |
} | |
// Ensure that it is a number and stop the keypress. | |
if (key < 48 || key > 57) { | |
event.preventDefault(); | |
return false; | |
} | |
return true; | |
}; | |
/// <summary> | |
/// Event handler for key presses that prevents entering any key stroke which would produce an invalid double. | |
/// </summary> | |
/// <param name ="event">The event object.</param> | |
OData.explorer.validation.allowOnlyDoubles = function (event) { | |
var $element = $(event.target); | |
var value = $element.val(); | |
// Allow only backspace and delete to support also Firefox. | |
var key = event.keyCode ? event.keyCode : (event.which ? event.which : event.charCode); | |
// Allow only one dot. | |
if (key == 46 && value.indexOf('.') == -1) { | |
return true; | |
} | |
return OData.explorer.validation.allowOnlyInts(event); | |
}; | |
/// <summary> | |
/// Event handler for key presses that prevents entering any key stroke which would produce an invalid GUID. | |
/// </summary> | |
/// <param name ="event">The event object.</param> | |
OData.explorer.validation.allowOnlyGuids = function (event) { | |
var $element = $(event.target); | |
var value = $element.val(); | |
if (value) { | |
var matchValue = value.match('^(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$'); | |
if (matchValue == null) { | |
$element.addClass('wrongInput'); | |
} else { | |
$element.removeClass('wrongInput'); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment