Skip to content

Instantly share code, notes, and snippets.

@philbritton
Created March 2, 2014 06:50
Show Gist options
  • Save philbritton/9302904 to your computer and use it in GitHub Desktop.
Save philbritton/9302904 to your computer and use it in GitHub Desktop.
/* 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;
}
<!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>
// 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,"&amp;").replace(/"/g,"&quot;").replace(/\</g,"&lt;"),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