/*! URI.js v1.12.0 http://medialize.github.com/URI.js/ */ /* build contains: IPv6.js, punycode.js, SecondLevelDomains.js, URI.js, URI.fragmentQuery.js */ (function(e,k){"object"===typeof exports?module.exports=k():"function"===typeof define&&define.amd?define(k):e.IPv6=k(e)})(this,function(e){var k=e&&e.IPv6;return{best:function(e){e=e.toLowerCase().split(":");var k=e.length,d=8;""===e[0]&&""===e[1]&&""===e[2]?(e.shift(),e.shift()):""===e[0]&&""===e[1]?e.shift():""===e[k-1]&&""===e[k-2]&&e.pop();k=e.length;-1!==e[k-1].indexOf(".")&&(d=7);var g;for(g=0;g<k&&""!==e[g];g++);if(g<d)for(e.splice(g,1,"0000");e.length<d;)e.splice(g,0,"0000");for(g=0;g<d;g++){for(var k= e[g].split(""),q=0;3>q;q++)if("0"===k[0]&&1<k.length)k.splice(0,1);else break;e[g]=k.join("")}var k=-1,l=q=0,r=-1,z=!1;for(g=0;g<d;g++)z?"0"===e[g]?l+=1:(z=!1,l>q&&(k=r,q=l)):"0"==e[g]&&(z=!0,r=g,l=1);l>q&&(k=r,q=l);1<q&&e.splice(k,q,"");k=e.length;d="";""===e[0]&&(beststr=":");for(g=0;g<k;g++){d+=e[g];if(g===k-1)break;d+=":"}""===e[k-1]&&(d+=":");return d},noConflict:function(){e.IPv6===this&&(e.IPv6=k);return this}}}); (function(e){function k(a){throw RangeError(p[a]);}function u(a,b){for(var c=a.length;c--;)a[c]=b(a[c]);return a}function m(a,b){return u(a.split(h),b).join(".")}function d(a){for(var b=[],c=0,d=a.length,h,p;c<d;)h=a.charCodeAt(c++),55296<=h&&56319>=h&&c<d?(p=a.charCodeAt(c++),56320==(p&64512)?b.push(((h&1023)<<10)+(p&1023)+65536):(b.push(h),c--)):b.push(h);return b}function g(a){return u(a,function(a){var b="";65535<a&&(a-=65536,b+=x(a>>>10&1023|55296),a=56320|a&1023);return b+=x(a)}).join("")}function q(a, b){return a+22+75*(26>a)-((0!=b)<<5)}function l(a,b,c){var d=0;a=c?A(a/H):a>>1;for(a+=A(a/b);a>n*y>>1;d+=s)a=A(a/n);return A(d+(n+1)*a/(a+I))}function r(b){var c=[],d=b.length,h,p=0,e=F,f=G,n,x,q,t,m;n=b.lastIndexOf(a);0>n&&(n=0);for(x=0;x<n;++x)128<=b.charCodeAt(x)&&k("not-basic"),c.push(b.charCodeAt(x));for(n=0<n?n+1:0;n<d;){x=p;h=1;for(q=s;;q+=s){n>=d&&k("invalid-input");t=b.charCodeAt(n++);t=10>t-48?t-22:26>t-65?t-65:26>t-97?t-97:s;(t>=s||t>A((w-p)/h))&&k("overflow");p+=t*h;m=q<=f?v:q>=f+y?y: q-f;if(t<m)break;t=s-m;h>A(w/t)&&k("overflow");h*=t}h=c.length+1;f=l(p-x,h,0==x);A(p/h)>w-e&&k("overflow");e+=A(p/h);p%=h;c.splice(p++,0,e)}return g(c)}function z(b){var c,h,p,e,f,n,g,m,r,t=[],B,u,z;b=d(b);B=b.length;c=F;h=0;f=G;for(n=0;n<B;++n)r=b[n],128>r&&t.push(x(r));for((p=e=t.length)&&t.push(a);p<B;){g=w;for(n=0;n<B;++n)r=b[n],r>=c&&r<g&&(g=r);u=p+1;g-c>A((w-h)/u)&&k("overflow");h+=(g-c)*u;c=g;for(n=0;n<B;++n)if(r=b[n],r<c&&++h>w&&k("overflow"),r==c){m=h;for(g=s;;g+=s){r=g<=f?v:g>=f+y?y:g-f; if(m<r)break;z=m-r;m=s-r;t.push(x(q(r+z%m,0)));m=A(z/m)}t.push(x(q(m,0)));f=l(h,u,p==e);h=0;++p}++h;++c}return t.join("")}var D="object"==typeof exports&&exports,E="object"==typeof module&&module&&module.exports==D&&module,C="object"==typeof global&&global;if(C.global===C||C.window===C)e=C;var f,w=2147483647,s=36,v=1,y=26,I=38,H=700,G=72,F=128,a="-",b=/^xn--/,c=/[^ -~]/,h=/\x2E|\u3002|\uFF0E|\uFF61/g,p={overflow:"Overflow: input needs wider integers to process","not-basic":"Illegal input >= 0x80 (not a basic code point)", "invalid-input":"Invalid input"},n=s-v,A=Math.floor,x=String.fromCharCode,B;f={version:"1.2.3",ucs2:{decode:d,encode:g},decode:r,encode:z,toASCII:function(a){return m(a,function(a){return c.test(a)?"xn--"+z(a):a})},toUnicode:function(a){return m(a,function(a){return b.test(a)?r(a.slice(4).toLowerCase()):a})}};if("function"==typeof define&&"object"==typeof define.amd&&define.amd)define(function(){return f});else if(D&&!D.nodeType)if(E)E.exports=f;else for(B in f)f.hasOwnProperty(B)&&(D[B]=f[B]);else e.punycode= f})(this); (function(e,k){"object"===typeof exports?module.exports=k():"function"===typeof define&&define.amd?define(k):e.SecondLevelDomains=k(e)})(this,function(e){var k=e&&e.SecondLevelDomains,u=Object.prototype.hasOwnProperty,m={list:{ac:"com|gov|mil|net|org",ae:"ac|co|gov|mil|name|net|org|pro|sch",af:"com|edu|gov|net|org",al:"com|edu|gov|mil|net|org",ao:"co|ed|gv|it|og|pb",ar:"com|edu|gob|gov|int|mil|net|org|tur",at:"ac|co|gv|or",au:"asn|com|csiro|edu|gov|id|net|org",ba:"co|com|edu|gov|mil|net|org|rs|unbi|unmo|unsa|untz|unze",bb:"biz|co|com|edu|gov|info|net|org|store|tv", bh:"biz|cc|com|edu|gov|info|net|org",bn:"com|edu|gov|net|org",bo:"com|edu|gob|gov|int|mil|net|org|tv",br:"adm|adv|agr|am|arq|art|ato|b|bio|blog|bmd|cim|cng|cnt|com|coop|ecn|edu|eng|esp|etc|eti|far|flog|fm|fnd|fot|fst|g12|ggf|gov|imb|ind|inf|jor|jus|lel|mat|med|mil|mus|net|nom|not|ntr|odo|org|ppg|pro|psc|psi|qsl|rec|slg|srv|tmp|trd|tur|tv|vet|vlog|wiki|zlg",bs:"com|edu|gov|net|org",bz:"du|et|om|ov|rg",ca:"ab|bc|mb|nb|nf|nl|ns|nt|nu|on|pe|qc|sk|yk",ck:"biz|co|edu|gen|gov|info|net|org",cn:"ac|ah|bj|com|cq|edu|fj|gd|gov|gs|gx|gz|ha|hb|he|hi|hl|hn|jl|js|jx|ln|mil|net|nm|nx|org|qh|sc|sd|sh|sn|sx|tj|tw|xj|xz|yn|zj", co:"com|edu|gov|mil|net|nom|org",cr:"ac|c|co|ed|fi|go|or|sa",cy:"ac|biz|com|ekloges|gov|ltd|name|net|org|parliament|press|pro|tm","do":"art|com|edu|gob|gov|mil|net|org|sld|web",dz:"art|asso|com|edu|gov|net|org|pol",ec:"com|edu|fin|gov|info|med|mil|net|org|pro",eg:"com|edu|eun|gov|mil|name|net|org|sci",er:"com|edu|gov|ind|mil|net|org|rochest|w",es:"com|edu|gob|nom|org",et:"biz|com|edu|gov|info|name|net|org",fj:"ac|biz|com|info|mil|name|net|org|pro",fk:"ac|co|gov|net|nom|org",fr:"asso|com|f|gouv|nom|prd|presse|tm", gg:"co|net|org",gh:"com|edu|gov|mil|org",gn:"ac|com|gov|net|org",gr:"com|edu|gov|mil|net|org",gt:"com|edu|gob|ind|mil|net|org",gu:"com|edu|gov|net|org",hk:"com|edu|gov|idv|net|org",id:"ac|co|go|mil|net|or|sch|web",il:"ac|co|gov|idf|k12|muni|net|org","in":"ac|co|edu|ernet|firm|gen|gov|i|ind|mil|net|nic|org|res",iq:"com|edu|gov|i|mil|net|org",ir:"ac|co|dnssec|gov|i|id|net|org|sch",it:"edu|gov",je:"co|net|org",jo:"com|edu|gov|mil|name|net|org|sch",jp:"ac|ad|co|ed|go|gr|lg|ne|or",ke:"ac|co|go|info|me|mobi|ne|or|sc", kh:"com|edu|gov|mil|net|org|per",ki:"biz|com|de|edu|gov|info|mob|net|org|tel",km:"asso|com|coop|edu|gouv|k|medecin|mil|nom|notaires|pharmaciens|presse|tm|veterinaire",kn:"edu|gov|net|org",kr:"ac|busan|chungbuk|chungnam|co|daegu|daejeon|es|gangwon|go|gwangju|gyeongbuk|gyeonggi|gyeongnam|hs|incheon|jeju|jeonbuk|jeonnam|k|kg|mil|ms|ne|or|pe|re|sc|seoul|ulsan",kw:"com|edu|gov|net|org",ky:"com|edu|gov|net|org",kz:"com|edu|gov|mil|net|org",lb:"com|edu|gov|net|org",lk:"assn|com|edu|gov|grp|hotel|int|ltd|net|ngo|org|sch|soc|web", lr:"com|edu|gov|net|org",lv:"asn|com|conf|edu|gov|id|mil|net|org",ly:"com|edu|gov|id|med|net|org|plc|sch",ma:"ac|co|gov|m|net|org|press",mc:"asso|tm",me:"ac|co|edu|gov|its|net|org|priv",mg:"com|edu|gov|mil|nom|org|prd|tm",mk:"com|edu|gov|inf|name|net|org|pro",ml:"com|edu|gov|net|org|presse",mn:"edu|gov|org",mo:"com|edu|gov|net|org",mt:"com|edu|gov|net|org",mv:"aero|biz|com|coop|edu|gov|info|int|mil|museum|name|net|org|pro",mw:"ac|co|com|coop|edu|gov|int|museum|net|org",mx:"com|edu|gob|net|org",my:"com|edu|gov|mil|name|net|org|sch", nf:"arts|com|firm|info|net|other|per|rec|store|web",ng:"biz|com|edu|gov|mil|mobi|name|net|org|sch",ni:"ac|co|com|edu|gob|mil|net|nom|org",np:"com|edu|gov|mil|net|org",nr:"biz|com|edu|gov|info|net|org",om:"ac|biz|co|com|edu|gov|med|mil|museum|net|org|pro|sch",pe:"com|edu|gob|mil|net|nom|org|sld",ph:"com|edu|gov|i|mil|net|ngo|org",pk:"biz|com|edu|fam|gob|gok|gon|gop|gos|gov|net|org|web",pl:"art|bialystok|biz|com|edu|gda|gdansk|gorzow|gov|info|katowice|krakow|lodz|lublin|mil|net|ngo|olsztyn|org|poznan|pwr|radom|slupsk|szczecin|torun|warszawa|waw|wroc|wroclaw|zgora", pr:"ac|biz|com|edu|est|gov|info|isla|name|net|org|pro|prof",ps:"com|edu|gov|net|org|plo|sec",pw:"belau|co|ed|go|ne|or",ro:"arts|com|firm|info|nom|nt|org|rec|store|tm|www",rs:"ac|co|edu|gov|in|org",sb:"com|edu|gov|net|org",sc:"com|edu|gov|net|org",sh:"co|com|edu|gov|net|nom|org",sl:"com|edu|gov|net|org",st:"co|com|consulado|edu|embaixada|gov|mil|net|org|principe|saotome|store",sv:"com|edu|gob|org|red",sz:"ac|co|org",tr:"av|bbs|bel|biz|com|dr|edu|gen|gov|info|k12|name|net|org|pol|tel|tsk|tv|web",tt:"aero|biz|cat|co|com|coop|edu|gov|info|int|jobs|mil|mobi|museum|name|net|org|pro|tel|travel", tw:"club|com|ebiz|edu|game|gov|idv|mil|net|org",mu:"ac|co|com|gov|net|or|org",mz:"ac|co|edu|gov|org",na:"co|com",nz:"ac|co|cri|geek|gen|govt|health|iwi|maori|mil|net|org|parliament|school",pa:"abo|ac|com|edu|gob|ing|med|net|nom|org|sld",pt:"com|edu|gov|int|net|nome|org|publ",py:"com|edu|gov|mil|net|org",qa:"com|edu|gov|mil|net|org",re:"asso|com|nom",ru:"ac|adygeya|altai|amur|arkhangelsk|astrakhan|bashkiria|belgorod|bir|bryansk|buryatia|cbg|chel|chelyabinsk|chita|chukotka|chuvashia|com|dagestan|e-burg|edu|gov|grozny|int|irkutsk|ivanovo|izhevsk|jar|joshkar-ola|kalmykia|kaluga|kamchatka|karelia|kazan|kchr|kemerovo|khabarovsk|khakassia|khv|kirov|koenig|komi|kostroma|kranoyarsk|kuban|kurgan|kursk|lipetsk|magadan|mari|mari-el|marine|mil|mordovia|mosreg|msk|murmansk|nalchik|net|nnov|nov|novosibirsk|nsk|omsk|orenburg|org|oryol|penza|perm|pp|pskov|ptz|rnd|ryazan|sakhalin|samara|saratov|simbirsk|smolensk|spb|stavropol|stv|surgut|tambov|tatarstan|tom|tomsk|tsaritsyn|tsk|tula|tuva|tver|tyumen|udm|udmurtia|ulan-ude|vladikavkaz|vladimir|vladivostok|volgograd|vologda|voronezh|vrn|vyatka|yakutia|yamal|yekaterinburg|yuzhno-sakhalinsk", rw:"ac|co|com|edu|gouv|gov|int|mil|net",sa:"com|edu|gov|med|net|org|pub|sch",sd:"com|edu|gov|info|med|net|org|tv",se:"a|ac|b|bd|c|d|e|f|g|h|i|k|l|m|n|o|org|p|parti|pp|press|r|s|t|tm|u|w|x|y|z",sg:"com|edu|gov|idn|net|org|per",sn:"art|com|edu|gouv|org|perso|univ",sy:"com|edu|gov|mil|net|news|org",th:"ac|co|go|in|mi|net|or",tj:"ac|biz|co|com|edu|go|gov|info|int|mil|name|net|nic|org|test|web",tn:"agrinet|com|defense|edunet|ens|fin|gov|ind|info|intl|mincom|nat|net|org|perso|rnrt|rns|rnu|tourism",tz:"ac|co|go|ne|or", ua:"biz|cherkassy|chernigov|chernovtsy|ck|cn|co|com|crimea|cv|dn|dnepropetrovsk|donetsk|dp|edu|gov|if|in|ivano-frankivsk|kh|kharkov|kherson|khmelnitskiy|kiev|kirovograd|km|kr|ks|kv|lg|lugansk|lutsk|lviv|me|mk|net|nikolaev|od|odessa|org|pl|poltava|pp|rovno|rv|sebastopol|sumy|te|ternopil|uzhgorod|vinnica|vn|zaporizhzhe|zhitomir|zp|zt",ug:"ac|co|go|ne|or|org|sc",uk:"ac|bl|british-library|co|cym|gov|govt|icnet|jet|lea|ltd|me|mil|mod|national-library-scotland|nel|net|nhs|nic|nls|org|orgn|parliament|plc|police|sch|scot|soc", us:"dni|fed|isa|kids|nsn",uy:"com|edu|gub|mil|net|org",ve:"co|com|edu|gob|info|mil|net|org|web",vi:"co|com|k12|net|org",vn:"ac|biz|com|edu|gov|health|info|int|name|net|org|pro",ye:"co|com|gov|ltd|me|net|org|plc",yu:"ac|co|edu|gov|org",za:"ac|agric|alt|bourse|city|co|cybernet|db|edu|gov|grondar|iaccess|imt|inca|landesign|law|mil|net|ngo|nis|nom|olivetti|org|pix|school|tm|web",zm:"ac|co|com|edu|gov|net|org|sch"},has_expression:null,is_expression:null,has:function(d){return!!d.match(m.has_expression)}, is:function(d){return!!d.match(m.is_expression)},get:function(d){return(d=d.match(m.has_expression))&&d[1]||null},noConflict:function(){e.SecondLevelDomains===this&&(e.SecondLevelDomains=k);return this},init:function(){var d="",e;for(e in m.list)u.call(m.list,e)&&(d+="|("+("("+m.list[e]+")."+e)+")");m.has_expression=RegExp("\\.("+d.substr(1)+")$","i");m.is_expression=RegExp("^("+d.substr(1)+")$","i")}};m.init();return m}); (function(e,k){"object"===typeof exports?module.exports=k(require("./punycode"),require("./IPv6"),require("./SecondLevelDomains")):"function"===typeof define&&define.amd?define(["./punycode","./IPv6","./SecondLevelDomains"],k):e.URI=k(e.punycode,e.IPv6,e.SecondLevelDomains,e)})(this,function(e,k,u,m){function d(a,b){if(!(this instanceof d))return new d(a,b);void 0===a&&(a="undefined"!==typeof location?location.href+"":"");this.href(a);return void 0!==b?this.absoluteTo(b):this}function g(a){return a.replace(/([.*+?^=!:${}()|[\]\/\\])/g, "\\$1")}function q(a){return void 0===a?"Undefined":String(Object.prototype.toString.call(a)).slice(8,-1)}function l(a){return"Array"===q(a)}function r(a,b){var c,d;if(l(b)){c=0;for(d=b.length;c<d;c++)if(!r(a,b[c]))return!1;return!0}var p=q(b);c=0;for(d=a.length;c<d;c++)if("RegExp"===p){if("string"===typeof a[c]&&a[c].match(b))return!0}else if(a[c]===b)return!0;return!1}function z(a,b){if(!l(a)||!l(b)||a.length!==b.length)return!1;a.sort();b.sort();for(var c=0,d=a.length;c<d;c++)if(a[c]!==b[c])return!1; return!0}function D(a){return escape(a)}function E(a){return encodeURIComponent(a).replace(/[!'()*]/g,D).replace(/\*/g,"%2A")}var C=m&&m.URI;d.version="1.12.0";var f=d.prototype,w=Object.prototype.hasOwnProperty;d._parts=function(){return{protocol:null,username:null,password:null,hostname:null,urn:null,port:null,path:null,query:null,fragment:null,duplicateQueryParameters:d.duplicateQueryParameters,escapeQuerySpace:d.escapeQuerySpace}};d.duplicateQueryParameters=!1;d.escapeQuerySpace=!0;d.protocol_expression= /^[a-z][a-z0-9.+-]*$/i;d.idn_expression=/[^a-z0-9\.-]/i;d.punycode_expression=/(xn--)/i;d.ip4_expression=/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/;d.ip6_expression=/^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/; d.find_uri_expression=/\b((?:[a-z][\w-]+:(?:\/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u2018\u2019]))/ig;d.findUri={start:/\b(?:([a-z][a-z0-9.+-]*:\/\/)|www\.)/gi,end:/[\s\r\n]|$/,trim:/[`!()\[\]{};:'".,<>?\u00ab\u00bb\u201c\u201d\u201e\u2018\u2019]+$/};d.defaultPorts={http:"80",https:"443",ftp:"21",gopher:"70",ws:"80",wss:"443"};d.invalid_hostname_characters= /[^a-zA-Z0-9\.-]/;d.domAttributes={a:"href",blockquote:"cite",link:"href",base:"href",script:"src",form:"action",img:"src",area:"href",iframe:"src",embed:"src",source:"src",track:"src",input:"src"};d.getDomAttribute=function(a){if(a&&a.nodeName){var b=a.nodeName.toLowerCase();return"input"===b&&"image"!==a.type?void 0:d.domAttributes[b]}};d.encode=E;d.decode=decodeURIComponent;d.iso8859=function(){d.encode=escape;d.decode=unescape};d.unicode=function(){d.encode=E;d.decode=decodeURIComponent};d.characters= {pathname:{encode:{expression:/%(24|26|2B|2C|3B|3D|3A|40)/ig,map:{"%24":"$","%26":"&","%2B":"+","%2C":",","%3B":";","%3D":"=","%3A":":","%40":"@"}},decode:{expression:/[\/\?#]/g,map:{"/":"%2F","?":"%3F","#":"%23"}}},reserved:{encode:{expression:/%(21|23|24|26|27|28|29|2A|2B|2C|2F|3A|3B|3D|3F|40|5B|5D)/ig,map:{"%3A":":","%2F":"/","%3F":"?","%23":"#","%5B":"[","%5D":"]","%40":"@","%21":"!","%24":"$","%26":"&","%27":"'","%28":"(","%29":")","%2A":"*","%2B":"+","%2C":",","%3B":";","%3D":"="}}}};d.encodeQuery= function(a,b){var c=d.encode(a+"");return b?c.replace(/%20/g,"+"):c};d.decodeQuery=function(a,b){a+="";try{return d.decode(b?a.replace(/\+/g,"%20"):a)}catch(c){return a}};d.recodePath=function(a){a=(a+"").split("/");for(var b=0,c=a.length;b<c;b++)a[b]=d.encodePathSegment(d.decode(a[b]));return a.join("/")};d.decodePath=function(a){a=(a+"").split("/");for(var b=0,c=a.length;b<c;b++)a[b]=d.decodePathSegment(a[b]);return a.join("/")};var s={encode:"encode",decode:"decode"},v,y=function(a,b){return function(c){return d[b](c+ "").replace(d.characters[a][b].expression,function(c){return d.characters[a][b].map[c]})}};for(v in s)d[v+"PathSegment"]=y("pathname",s[v]);d.encodeReserved=y("reserved","encode");d.parse=function(a,b){var c;b||(b={});c=a.indexOf("#");-1<c&&(b.fragment=a.substring(c+1)||null,a=a.substring(0,c));c=a.indexOf("?");-1<c&&(b.query=a.substring(c+1)||null,a=a.substring(0,c));"//"===a.substring(0,2)?(b.protocol=null,a=a.substring(2),a=d.parseAuthority(a,b)):(c=a.indexOf(":"),-1<c&&(b.protocol=a.substring(0, c)||null,b.protocol&&!b.protocol.match(d.protocol_expression)?b.protocol=void 0:"file"===b.protocol?a=a.substring(c+3):"//"===a.substring(c+1,c+3)?(a=a.substring(c+3),a=d.parseAuthority(a,b)):(a=a.substring(c+1),b.urn=!0)));b.path=a;return b};d.parseHost=function(a,b){var c=a.indexOf("/"),d;-1===c&&(c=a.length);"["===a.charAt(0)?(d=a.indexOf("]"),b.hostname=a.substring(1,d)||null,b.port=a.substring(d+2,c)||null):a.indexOf(":")!==a.lastIndexOf(":")?(b.hostname=a.substring(0,c)||null,b.port=null):(d= a.substring(0,c).split(":"),b.hostname=d[0]||null,b.port=d[1]||null);b.hostname&&"/"!==a.substring(c).charAt(0)&&(c++,a="/"+a);return a.substring(c)||"/"};d.parseAuthority=function(a,b){a=d.parseUserinfo(a,b);return d.parseHost(a,b)};d.parseUserinfo=function(a,b){var c=a.indexOf("/"),h=-1<c?a.lastIndexOf("@",c):a.indexOf("@");-1<h&&(-1===c||h<c)?(c=a.substring(0,h).split(":"),b.username=c[0]?d.decode(c[0]):null,c.shift(),b.password=c[0]?d.decode(c.join(":")):null,a=a.substring(h+1)):(b.username=null, b.password=null);return a};d.parseQuery=function(a,b){if(!a)return{};a=a.replace(/&+/g,"&").replace(/^\?*&*|&+$/g,"");if(!a)return{};for(var c={},h=a.split("&"),p=h.length,n,e,f=0;f<p;f++)n=h[f].split("="),e=d.decodeQuery(n.shift(),b),n=n.length?d.decodeQuery(n.join("="),b):null,c[e]?("string"===typeof c[e]&&(c[e]=[c[e]]),c[e].push(n)):c[e]=n;return c};d.build=function(a){var b="";a.protocol&&(b+=a.protocol+":");a.urn||!b&&!a.hostname||(b+="//");b+=d.buildAuthority(a)||"";"string"===typeof a.path&& ("/"!==a.path.charAt(0)&&"string"===typeof a.hostname&&(b+="/"),b+=a.path);"string"===typeof a.query&&a.query&&(b+="?"+a.query);"string"===typeof a.fragment&&a.fragment&&(b+="#"+a.fragment);return b};d.buildHost=function(a){var b="";if(a.hostname)d.ip6_expression.test(a.hostname)?b=a.port?b+("["+a.hostname+"]:"+a.port):b+a.hostname:(b+=a.hostname,a.port&&(b+=":"+a.port));else return"";return b};d.buildAuthority=function(a){return d.buildUserinfo(a)+d.buildHost(a)};d.buildUserinfo=function(a){var b= "";a.username&&(b+=d.encode(a.username),a.password&&(b+=":"+d.encode(a.password)),b+="@");return b};d.buildQuery=function(a,b,c){var h="",p,e,f,k;for(e in a)if(w.call(a,e)&&e)if(l(a[e]))for(p={},f=0,k=a[e].length;f<k;f++)void 0!==a[e][f]&&void 0===p[a[e][f]+""]&&(h+="&"+d.buildQueryParameter(e,a[e][f],c),!0!==b&&(p[a[e][f]+""]=!0));else void 0!==a[e]&&(h+="&"+d.buildQueryParameter(e,a[e],c));return h.substring(1)};d.buildQueryParameter=function(a,b,c){return d.encodeQuery(a,c)+(null!==b?"="+d.encodeQuery(b, c):"")};d.addQuery=function(a,b,c){if("object"===typeof b)for(var h in b)w.call(b,h)&&d.addQuery(a,h,b[h]);else if("string"===typeof b)void 0===a[b]?a[b]=c:("string"===typeof a[b]&&(a[b]=[a[b]]),l(c)||(c=[c]),a[b]=a[b].concat(c));else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");};d.removeQuery=function(a,b,c){var h;if(l(b))for(c=0,h=b.length;c<h;c++)a[b[c]]=void 0;else if("object"===typeof b)for(h in b)w.call(b,h)&&d.removeQuery(a,h,b[h]);else if("string"=== typeof b)if(void 0!==c)if(a[b]===c)a[b]=void 0;else{if(l(a[b])){h=a[b];var p={},e,f;if(l(c))for(e=0,f=c.length;e<f;e++)p[c[e]]=!0;else p[c]=!0;e=0;for(f=h.length;e<f;e++)void 0!==p[h[e]]&&(h.splice(e,1),f--,e--);a[b]=h}}else a[b]=void 0;else throw new TypeError("URI.addQuery() accepts an object, string as the first parameter");};d.hasQuery=function(a,b,c,h){if("object"===typeof b){for(var e in b)if(w.call(b,e)&&!d.hasQuery(a,e,b[e]))return!1;return!0}if("string"!==typeof b)throw new TypeError("URI.hasQuery() accepts an object, string as the name parameter"); switch(q(c)){case "Undefined":return b in a;case "Boolean":return a=Boolean(l(a[b])?a[b].length:a[b]),c===a;case "Function":return!!c(a[b],b,a);case "Array":return l(a[b])?(h?r:z)(a[b],c):!1;case "RegExp":return l(a[b])?h?r(a[b],c):!1:Boolean(a[b]&&a[b].match(c));case "Number":c=String(c);case "String":return l(a[b])?h?r(a[b],c):!1:a[b]===c;default:throw new TypeError("URI.hasQuery() accepts undefined, boolean, string, number, RegExp, Function as the value parameter");}};d.commonPath=function(a,b){var c= Math.min(a.length,b.length),d;for(d=0;d<c;d++)if(a.charAt(d)!==b.charAt(d)){d--;break}if(1>d)return a.charAt(0)===b.charAt(0)&&"/"===a.charAt(0)?"/":"";if("/"!==a.charAt(d)||"/"!==b.charAt(d))d=a.substring(0,d).lastIndexOf("/");return a.substring(0,d+1)};d.withinString=function(a,b,c){c||(c={});var h=c.start||d.findUri.start,e=c.end||d.findUri.end,f=c.trim||d.findUri.trim,k=/[a-z0-9-]=["']?$/i;for(h.lastIndex=0;;){var g=h.exec(a);if(!g)break;g=g.index;if(c.ignoreHtml){var l=a.slice(Math.max(g-3,0), g);if(l&&k.test(l))continue}var l=g+a.slice(g).search(e),q=a.slice(g,l).replace(f,"");c.ignore&&c.ignore.test(q)||(l=g+q.length,q=b(q,g,l,a),a=a.slice(0,g)+q+a.slice(l),h.lastIndex=g+q.length)}h.lastIndex=0;return a};d.ensureValidHostname=function(a){if(a.match(d.invalid_hostname_characters)){if(!e)throw new TypeError("Hostname '"+a+"' contains characters other than [A-Z0-9.-] and Punycode.js is not available");if(e.toASCII(a).match(d.invalid_hostname_characters))throw new TypeError("Hostname '"+ a+"' contains characters other than [A-Z0-9.-]");}};d.noConflict=function(a){if(a)return a={URI:this.noConflict()},URITemplate&&"function"==typeof URITemplate.noConflict&&(a.URITemplate=URITemplate.noConflict()),k&&"function"==typeof k.noConflict&&(a.IPv6=k.noConflict()),SecondLevelDomains&&"function"==typeof SecondLevelDomains.noConflict&&(a.SecondLevelDomains=SecondLevelDomains.noConflict()),a;m.URI===this&&(m.URI=C);return this};f.build=function(a){if(!0===a)this._deferred_build=!0;else if(void 0=== a||this._deferred_build)this._string=d.build(this._parts),this._deferred_build=!1;return this};f.clone=function(){return new d(this)};f.valueOf=f.toString=function(){return this.build(!1)._string};s={protocol:"protocol",username:"username",password:"password",hostname:"hostname",port:"port"};y=function(a){return function(b,c){if(void 0===b)return this._parts[a]||"";this._parts[a]=b||null;this.build(!c);return this}};for(v in s)f[v]=y(s[v]);s={query:"?",fragment:"#"};y=function(a,b){return function(c, d){if(void 0===c)return this._parts[a]||"";null!==c&&(c+="",c.charAt(0)===b&&(c=c.substring(1)));this._parts[a]=c;this.build(!d);return this}};for(v in s)f[v]=y(v,s[v]);s={search:["?","query"],hash:["#","fragment"]};y=function(a,b){return function(c,d){var e=this[a](c,d);return"string"===typeof e&&e.length?b+e:e}};for(v in s)f[v]=y(s[v][1],s[v][0]);f.pathname=function(a,b){if(void 0===a||!0===a){var c=this._parts.path||(this._parts.hostname?"/":"");return a?d.decodePath(c):c}this._parts.path=a?d.recodePath(a): "/";this.build(!b);return this};f.path=f.pathname;f.href=function(a,b){var c;if(void 0===a)return this.toString();this._string="";this._parts=d._parts();var h=a instanceof d,e="object"===typeof a&&(a.hostname||a.path||a.pathname);a.nodeName&&(e=d.getDomAttribute(a),a=a[e]||"",e=!1);!h&&e&&void 0!==a.pathname&&(a=a.toString());if("string"===typeof a)this._parts=d.parse(a,this._parts);else if(h||e)for(c in h=h?a._parts:a,h)w.call(this._parts,c)&&(this._parts[c]=h[c]);else throw new TypeError("invalid input"); this.build(!b);return this};f.is=function(a){var b=!1,c=!1,h=!1,e=!1,f=!1,g=!1,k=!1,l=!this._parts.urn;this._parts.hostname&&(l=!1,c=d.ip4_expression.test(this._parts.hostname),h=d.ip6_expression.test(this._parts.hostname),b=c||h,f=(e=!b)&&u&&u.has(this._parts.hostname),g=e&&d.idn_expression.test(this._parts.hostname),k=e&&d.punycode_expression.test(this._parts.hostname));switch(a.toLowerCase()){case "relative":return l;case "absolute":return!l;case "domain":case "name":return e;case "sld":return f; case "ip":return b;case "ip4":case "ipv4":case "inet4":return c;case "ip6":case "ipv6":case "inet6":return h;case "idn":return g;case "url":return!this._parts.urn;case "urn":return!!this._parts.urn;case "punycode":return k}return null};var I=f.protocol,H=f.port,G=f.hostname;f.protocol=function(a,b){if(void 0!==a&&a&&(a=a.replace(/:(\/\/)?$/,""),!a.match(d.protocol_expression)))throw new TypeError("Protocol '"+a+"' contains characters other than [A-Z0-9.+-] or doesn't start with [A-Z]");return I.call(this, a,b)};f.scheme=f.protocol;f.port=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a&&(0===a&&(a=null),a&&(a+="",":"===a.charAt(0)&&(a=a.substring(1)),a.match(/[^0-9]/))))throw new TypeError("Port '"+a+"' contains characters other than [0-9]");return H.call(this,a,b)};f.hostname=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0!==a){var c={};d.parseHost(a,c);a=c.hostname}return G.call(this,a,b)};f.host=function(a,b){if(this._parts.urn)return void 0===a?"":this; if(void 0===a)return this._parts.hostname?d.buildHost(this._parts):"";d.parseHost(a,this._parts);this.build(!b);return this};f.authority=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a)return this._parts.hostname?d.buildAuthority(this._parts):"";d.parseAuthority(a,this._parts);this.build(!b);return this};f.userinfo=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.username)return"";var c=d.buildUserinfo(this._parts);return c.substring(0, c.length-1)}"@"!==a[a.length-1]&&(a+="@");d.parseUserinfo(a,this._parts);this.build(!b);return this};f.resource=function(a,b){var c;if(void 0===a)return this.path()+this.search()+this.hash();c=d.parse(a);this._parts.path=c.path;this._parts.query=c.query;this._parts.fragment=c.fragment;this.build(!b);return this};f.subdomain=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.length-this.domain().length- 1;return this._parts.hostname.substring(0,c)||""}c=this._parts.hostname.length-this.domain().length;c=this._parts.hostname.substring(0,c);c=RegExp("^"+g(c));a&&"."!==a.charAt(a.length-1)&&(a+=".");a&&d.ensureValidHostname(a);this._parts.hostname=this._parts.hostname.replace(c,a);this.build(!b);return this};f.domain=function(a,b){if(this._parts.urn)return void 0===a?"":this;"boolean"===typeof a&&(b=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.match(/\./g); if(c&&2>c.length)return this._parts.hostname;c=this._parts.hostname.length-this.tld(b).length-1;c=this._parts.hostname.lastIndexOf(".",c-1)+1;return this._parts.hostname.substring(c)||""}if(!a)throw new TypeError("cannot set domain empty");d.ensureValidHostname(a);!this._parts.hostname||this.is("IP")?this._parts.hostname=a:(c=RegExp(g(this.domain())+"$"),this._parts.hostname=this._parts.hostname.replace(c,a));this.build(!b);return this};f.tld=function(a,b){if(this._parts.urn)return void 0===a?"": this;"boolean"===typeof a&&(b=a,a=void 0);if(void 0===a){if(!this._parts.hostname||this.is("IP"))return"";var c=this._parts.hostname.lastIndexOf("."),c=this._parts.hostname.substring(c+1);return!0!==b&&u&&u.list[c.toLowerCase()]?u.get(this._parts.hostname)||c:c}if(a)if(a.match(/[^a-zA-Z0-9-]/))if(u&&u.is(a))c=RegExp(g(this.tld())+"$"),this._parts.hostname=this._parts.hostname.replace(c,a);else throw new TypeError("TLD '"+a+"' contains characters other than [A-Z0-9]");else{if(!this._parts.hostname|| this.is("IP"))throw new ReferenceError("cannot set TLD on non-domain host");c=RegExp(g(this.tld())+"$");this._parts.hostname=this._parts.hostname.replace(c,a)}else throw new TypeError("cannot set TLD empty");this.build(!b);return this};f.directory=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path&&!this._parts.hostname)return"";if("/"===this._parts.path)return"/";var c=this._parts.path.length-this.filename().length-1,c=this._parts.path.substring(0, c)||(this._parts.hostname?"/":"");return a?d.decodePath(c):c}c=this._parts.path.length-this.filename().length;c=this._parts.path.substring(0,c);c=RegExp("^"+g(c));this.is("relative")||(a||(a="/"),"/"!==a.charAt(0)&&(a="/"+a));a&&"/"!==a.charAt(a.length-1)&&(a+="/");a=d.recodePath(a);this._parts.path=this._parts.path.replace(c,a);this.build(!b);return this};f.filename=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return""; var c=this._parts.path.lastIndexOf("/"),c=this._parts.path.substring(c+1);return a?d.decodePathSegment(c):c}c=!1;"/"===a.charAt(0)&&(a=a.substring(1));a.match(/\.?\//)&&(c=!0);var h=RegExp(g(this.filename())+"$");a=d.recodePath(a);this._parts.path=this._parts.path.replace(h,a);c?this.normalizePath(b):this.build(!b);return this};f.suffix=function(a,b){if(this._parts.urn)return void 0===a?"":this;if(void 0===a||!0===a){if(!this._parts.path||"/"===this._parts.path)return"";var c=this.filename(),h=c.lastIndexOf("."); if(-1===h)return"";c=c.substring(h+1);c=/^[a-z0-9%]+$/i.test(c)?c:"";return a?d.decodePathSegment(c):c}"."===a.charAt(0)&&(a=a.substring(1));if(c=this.suffix())h=a?RegExp(g(c)+"$"):RegExp(g("."+c)+"$");else{if(!a)return this;this._parts.path+="."+d.recodePath(a)}h&&(a=d.recodePath(a),this._parts.path=this._parts.path.replace(h,a));this.build(!b);return this};f.segment=function(a,b,c){var d=this._parts.urn?":":"/",e=this.path(),f="/"===e.substring(0,1),e=e.split(d);void 0!==a&&"number"!==typeof a&& (c=b,b=a,a=void 0);if(void 0!==a&&"number"!==typeof a)throw Error("Bad segment '"+a+"', must be 0-based integer");f&&e.shift();0>a&&(a=Math.max(e.length+a,0));if(void 0===b)return void 0===a?e:e[a];if(null===a||void 0===e[a])if(l(b)){e=[];a=0;for(var g=b.length;a<g;a++)if(b[a].length||e.length&&e[e.length-1].length)e.length&&!e[e.length-1].length&&e.pop(),e.push(b[a])}else{if(b||"string"===typeof b)""===e[e.length-1]?e[e.length-1]=b:e.push(b)}else b||"string"===typeof b&&b.length?e[a]=b:e.splice(a, 1);f&&e.unshift("");return this.path(e.join(d),c)};f.segmentCoded=function(a,b,c){var e,f;"number"!==typeof a&&(c=b,b=a,a=void 0);if(void 0===b){a=this.segment(a,b,c);if(l(a))for(e=0,f=a.length;e<f;e++)a[e]=d.decode(a[e]);else a=void 0!==a?d.decode(a):void 0;return a}if(l(b))for(e=0,f=b.length;e<f;e++)b[e]=d.decode(b[e]);else b="string"===typeof b?d.encode(b):b;return this.segment(a,b,c)};var F=f.query;f.query=function(a,b){if(!0===a)return d.parseQuery(this._parts.query,this._parts.escapeQuerySpace); if("function"===typeof a){var c=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace),e=a.call(this,c);this._parts.query=d.buildQuery(e||c,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);this.build(!b);return this}return void 0!==a&&"string"!==typeof a?(this._parts.query=d.buildQuery(a,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace),this.build(!b),this):F.call(this,a,b)};f.setQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace); if("object"===typeof a)for(var f in a)w.call(a,f)&&(e[f]=a[f]);else if("string"===typeof a)e[a]=void 0!==b?b:null;else throw new TypeError("URI.addQuery() accepts an object, string as the name parameter");this._parts.query=d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};f.addQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);d.addQuery(e,a,void 0===b?null:b);this._parts.query= d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};f.removeQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace);d.removeQuery(e,a,b);this._parts.query=d.buildQuery(e,this._parts.duplicateQueryParameters,this._parts.escapeQuerySpace);"string"!==typeof a&&(c=b);this.build(!c);return this};f.hasQuery=function(a,b,c){var e=d.parseQuery(this._parts.query,this._parts.escapeQuerySpace); return d.hasQuery(e,a,b,c)};f.setSearch=f.setQuery;f.addSearch=f.addQuery;f.removeSearch=f.removeQuery;f.hasSearch=f.hasQuery;f.normalize=function(){return this._parts.urn?this.normalizeProtocol(!1).normalizeQuery(!1).normalizeFragment(!1).build():this.normalizeProtocol(!1).normalizeHostname(!1).normalizePort(!1).normalizePath(!1).normalizeQuery(!1).normalizeFragment(!1).build()};f.normalizeProtocol=function(a){"string"===typeof this._parts.protocol&&(this._parts.protocol=this._parts.protocol.toLowerCase(), this.build(!a));return this};f.normalizeHostname=function(a){this._parts.hostname&&(this.is("IDN")&&e?this._parts.hostname=e.toASCII(this._parts.hostname):this.is("IPv6")&&k&&(this._parts.hostname=k.best(this._parts.hostname)),this._parts.hostname=this._parts.hostname.toLowerCase(),this.build(!a));return this};f.normalizePort=function(a){"string"===typeof this._parts.protocol&&this._parts.port===d.defaultPorts[this._parts.protocol]&&(this._parts.port=null,this.build(!a));return this};f.normalizePath= function(a){if(this._parts.urn||!this._parts.path||"/"===this._parts.path)return this;var b,c=this._parts.path,e="",f,g;"/"!==c.charAt(0)&&(b=!0,c="/"+c);c=c.replace(/(\/(\.\/)+)|(\/\.$)/g,"/").replace(/\/{2,}/g,"/");b&&(e=c.substring(1).match(/^(\.\.\/)+/)||"")&&(e=e[0]);for(;;){f=c.indexOf("/..");if(-1===f)break;else if(0===f){c=c.substring(3);continue}g=c.substring(0,f).lastIndexOf("/");-1===g&&(g=f);c=c.substring(0,g)+c.substring(f+3)}b&&this.is("relative")&&(c=e+c.substring(1));c=d.recodePath(c); this._parts.path=c;this.build(!a);return this};f.normalizePathname=f.normalizePath;f.normalizeQuery=function(a){"string"===typeof this._parts.query&&(this._parts.query.length?this.query(d.parseQuery(this._parts.query,this._parts.escapeQuerySpace)):this._parts.query=null,this.build(!a));return this};f.normalizeFragment=function(a){this._parts.fragment||(this._parts.fragment=null,this.build(!a));return this};f.normalizeSearch=f.normalizeQuery;f.normalizeHash=f.normalizeFragment;f.iso8859=function(){var a= d.encode,b=d.decode;d.encode=escape;d.decode=decodeURIComponent;this.normalize();d.encode=a;d.decode=b;return this};f.unicode=function(){var a=d.encode,b=d.decode;d.encode=E;d.decode=unescape;this.normalize();d.encode=a;d.decode=b;return this};f.readable=function(){var a=this.clone();a.username("").password("").normalize();var b="";a._parts.protocol&&(b+=a._parts.protocol+"://");a._parts.hostname&&(a.is("punycode")&&e?(b+=e.toUnicode(a._parts.hostname),a._parts.port&&(b+=":"+a._parts.port)):b+=a.host()); a._parts.hostname&&a._parts.path&&"/"!==a._parts.path.charAt(0)&&(b+="/");b+=a.path(!0);if(a._parts.query){for(var c="",f=0,g=a._parts.query.split("&"),k=g.length;f<k;f++){var l=(g[f]||"").split("="),c=c+("&"+d.decodeQuery(l[0],this._parts.escapeQuerySpace).replace(/&/g,"%26"));void 0!==l[1]&&(c+="="+d.decodeQuery(l[1],this._parts.escapeQuerySpace).replace(/&/g,"%26"))}b+="?"+c.substring(1)}return b+=d.decodeQuery(a.hash(),!0)};f.absoluteTo=function(a){var b=this.clone(),c=["protocol","username", "password","hostname","port"],e,f;if(this._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a instanceof d||(a=new d(a));b._parts.protocol||(b._parts.protocol=a._parts.protocol);if(this._parts.hostname)return b;for(e=0;f=c[e];e++)b._parts[f]=a._parts[f];b._parts.path?".."===b._parts.path.substring(-2)&&(b._parts.path+="/"):(b._parts.path=a._parts.path,b._parts.query||(b._parts.query=a._parts.query));"/"!==b.path().charAt(0)&&(a=a.directory(),b._parts.path=(a? a+"/":"")+b._parts.path,b.normalizePath());b.build();return b};f.relativeTo=function(a){var b=this.clone().normalize(),c,e,f,g;if(b._parts.urn)throw Error("URNs do not have any generally defined hierarchical components");a=(new d(a)).normalize();c=b._parts;e=a._parts;f=b.path();g=a.path();if("/"!==f.charAt(0))throw Error("URI is already relative");if("/"!==g.charAt(0))throw Error("Cannot calculate a URI relative to another relative URI");c.protocol===e.protocol&&(c.protocol=null);if(c.username=== e.username&&c.password===e.password&&null===c.protocol&&null===c.username&&null===c.password&&c.hostname===e.hostname&&c.port===e.port)c.hostname=null,c.port=null;else return b.build();if(f===g)return c.path="",b.build();a=d.commonPath(b.path(),a.path());if(!a)return b.build();e=e.path.substring(a.length).replace(/[^\/]*$/,"").replace(/.*?\//g,"../");c.path=e+c.path.substring(a.length);return b.build()};f.equals=function(a){var b=this.clone();a=new d(a);var c={},e={},f={},g;b.normalize();a.normalize(); if(b.toString()===a.toString())return!0;c=b.query();e=a.query();b.query("");a.query("");if(b.toString()!==a.toString()||c.length!==e.length)return!1;c=d.parseQuery(c,this._parts.escapeQuerySpace);e=d.parseQuery(e,this._parts.escapeQuerySpace);for(g in c)if(w.call(c,g)){if(!l(c[g])){if(c[g]!==e[g])return!1}else if(!z(c[g],e[g]))return!1;f[g]=!0}for(g in e)if(w.call(e,g)&&!f[g])return!1;return!0};f.duplicateQueryParameters=function(a){this._parts.duplicateQueryParameters=!!a;return this};f.escapeQuerySpace= function(a){this._parts.escapeQuerySpace=!!a;return this};return d}); (function(e,k){"object"===typeof exports?module.exports=k(require("./URI")):"function"===typeof define&&define.amd?define(["./URI"],k):k(e.URI)})(this,function(e){var k=e.prototype,u=k.fragment;e.fragmentPrefix="?";var m=e._parts;e._parts=function(){var d=m();d.fragmentPrefix=e.fragmentPrefix;return d};k.fragmentPrefix=function(d){this._parts.fragmentPrefix=d;return this};k.fragment=function(d,g){var k=this._parts.fragmentPrefix,l=this._parts.fragment||"";return!0===d?l.substring(0,k.length)!==k? {}:e.parseQuery(l.substring(k.length)):void 0!==d&&"string"!==typeof d?(this._parts.fragment=k+e.buildQuery(d),this.build(!g),this):u.call(this,d,g)};k.addFragment=function(d,g,k){var l=this._parts.fragmentPrefix,m=e.parseQuery((this._parts.fragment||"").substring(l.length));e.addQuery(m,d,g);this._parts.fragment=l+e.buildQuery(m);"string"!==typeof d&&(k=g);this.build(!k);return this};k.removeFragment=function(d,g,k){var l=this._parts.fragmentPrefix,m=e.parseQuery((this._parts.fragment||"").substring(l.length)); e.removeQuery(m,d,g);this._parts.fragment=l+e.buildQuery(m);"string"!==typeof d&&(k=g);this.build(!k);return this};k.addHash=k.addFragment;k.removeHash=k.removeFragment;return{}}); ;/*global unescape, module, define, window, global*/ /* UriTemplate Copyright (c) 2012-2013 Franz Antesberger. All Rights Reserved. Available via the MIT license. */ (function (exportCallback) { "use strict"; var UriTemplateError = (function () { function UriTemplateError (options) { this.options = options; } UriTemplateError.prototype.toString = function () { if (JSON && JSON.stringify) { return JSON.stringify(this.options); } else { return this.options; } }; return UriTemplateError; }()); var objectHelper = (function () { function isArray (value) { return Object.prototype.toString.apply(value) === '[object Array]'; } function isString (value) { return Object.prototype.toString.apply(value) === '[object String]'; } function isNumber (value) { return Object.prototype.toString.apply(value) === '[object Number]'; } function isBoolean (value) { return Object.prototype.toString.apply(value) === '[object Boolean]'; } function join (arr, separator) { var result = '', first = true, index; for (index = 0; index < arr.length; index += 1) { if (first) { first = false; } else { result += separator; } result += arr[index]; } return result; } function map (arr, mapper) { var result = [], index = 0; for (; index < arr.length; index += 1) { result.push(mapper(arr[index])); } return result; } function filter (arr, predicate) { var result = [], index = 0; for (; index < arr.length; index += 1) { if (predicate(arr[index])) { result.push(arr[index]); } } return result; } function deepFreezeUsingObjectFreeze (object) { if (typeof object !== "object" || object === null) { return object; } Object.freeze(object); var property, propertyName; for (propertyName in object) { if (object.hasOwnProperty(propertyName)) { property = object[propertyName]; // be aware, arrays are 'object', too if (typeof property === "object") { deepFreeze(property); } } } return object; } function deepFreeze (object) { if (typeof Object.freeze === 'function') { return deepFreezeUsingObjectFreeze(object); } return object; } return { isArray: isArray, isString: isString, isNumber: isNumber, isBoolean: isBoolean, join: join, map: map, filter: filter, deepFreeze: deepFreeze }; }()); var charHelper = (function () { function isAlpha (chr) { return (chr >= 'a' && chr <= 'z') || ((chr >= 'A' && chr <= 'Z')); } function isDigit (chr) { return chr >= '0' && chr <= '9'; } function isHexDigit (chr) { return isDigit(chr) || (chr >= 'a' && chr <= 'f') || (chr >= 'A' && chr <= 'F'); } return { isAlpha: isAlpha, isDigit: isDigit, isHexDigit: isHexDigit }; }()); var pctEncoder = (function () { var utf8 = { encode: function (chr) { // see http://ecmanaut.blogspot.de/2006/07/encoding-decoding-utf8-in-javascript.html return unescape(encodeURIComponent(chr)); }, numBytes: function (firstCharCode) { if (firstCharCode <= 0x7F) { return 1; } else if (0xC2 <= firstCharCode && firstCharCode <= 0xDF) { return 2; } else if (0xE0 <= firstCharCode && firstCharCode <= 0xEF) { return 3; } else if (0xF0 <= firstCharCode && firstCharCode <= 0xF4) { return 4; } // no valid first octet return 0; }, isValidFollowingCharCode: function (charCode) { return 0x80 <= charCode && charCode <= 0xBF; } }; /** * encodes a character, if needed or not. * @param chr * @return pct-encoded character */ function encodeCharacter (chr) { var result = '', octets = utf8.encode(chr), octet, index; for (index = 0; index < octets.length; index += 1) { octet = octets.charCodeAt(index); result += '%' + (octet < 0x10 ? '0' : '') + octet.toString(16).toUpperCase(); } return result; } /** * Returns, whether the given text at start is in the form 'percent hex-digit hex-digit', like '%3F' * @param text * @param start * @return {boolean|*|*} */ function isPercentDigitDigit (text, start) { return text.charAt(start) === '%' && charHelper.isHexDigit(text.charAt(start + 1)) && charHelper.isHexDigit(text.charAt(start + 2)); } /** * Parses a hex number from start with length 2. * @param text a string * @param start the start index of the 2-digit hex number * @return {Number} */ function parseHex2 (text, start) { return parseInt(text.substr(start, 2), 16); } /** * Returns whether or not the given char sequence is a correctly pct-encoded sequence. * @param chr * @return {boolean} */ function isPctEncoded (chr) { if (!isPercentDigitDigit(chr, 0)) { return false; } var firstCharCode = parseHex2(chr, 1); var numBytes = utf8.numBytes(firstCharCode); if (numBytes === 0) { return false; } for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) { if (!isPercentDigitDigit(chr, 3*byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(chr, 3*byteNumber + 1))) { return false; } } return true; } /** * Reads as much as needed from the text, e.g. '%20' or '%C3%B6'. It does not decode! * @param text * @param startIndex * @return the character or pct-string of the text at startIndex */ function pctCharAt(text, startIndex) { var chr = text.charAt(startIndex); if (!isPercentDigitDigit(text, startIndex)) { return chr; } var utf8CharCode = parseHex2(text, startIndex + 1); var numBytes = utf8.numBytes(utf8CharCode); if (numBytes === 0) { return chr; } for (var byteNumber = 1; byteNumber < numBytes; byteNumber += 1) { if (!isPercentDigitDigit(text, startIndex + 3 * byteNumber) || !utf8.isValidFollowingCharCode(parseHex2(text, startIndex + 3 * byteNumber + 1))) { return chr; } } return text.substr(startIndex, 3 * numBytes); } return { encodeCharacter: encodeCharacter, isPctEncoded: isPctEncoded, pctCharAt: pctCharAt }; }()); var rfcCharHelper = (function () { /** * Returns if an character is an varchar character according 2.3 of rfc 6570 * @param chr * @return (Boolean) */ function isVarchar (chr) { return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '_' || pctEncoder.isPctEncoded(chr); } /** * Returns if chr is an unreserved character according 1.5 of rfc 6570 * @param chr * @return {Boolean} */ function isUnreserved (chr) { return charHelper.isAlpha(chr) || charHelper.isDigit(chr) || chr === '-' || chr === '.' || chr === '_' || chr === '~'; } /** * Returns if chr is an reserved character according 1.5 of rfc 6570 * or the percent character mentioned in 3.2.1. * @param chr * @return {Boolean} */ function isReserved (chr) { return chr === ':' || chr === '/' || chr === '?' || chr === '#' || chr === '[' || chr === ']' || chr === '@' || chr === '!' || chr === '$' || chr === '&' || chr === '(' || chr === ')' || chr === '*' || chr === '+' || chr === ',' || chr === ';' || chr === '=' || chr === "'"; } return { isVarchar: isVarchar, isUnreserved: isUnreserved, isReserved: isReserved }; }()); /** * encoding of rfc 6570 */ var encodingHelper = (function () { function encode (text, passReserved) { var result = '', index, chr = ''; if (typeof text === "number" || typeof text === "boolean") { text = text.toString(); } for (index = 0; index < text.length; index += chr.length) { chr = text.charAt(index); result += rfcCharHelper.isUnreserved(chr) || (passReserved && rfcCharHelper.isReserved(chr)) ? chr : pctEncoder.encodeCharacter(chr); } return result; } function encodePassReserved (text) { return encode(text, true); } function encodeLiteralCharacter (literal, index) { var chr = pctEncoder.pctCharAt(literal, index); if (chr.length > 1) { return chr; } else { return rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr); } } function encodeLiteral (literal) { var result = '', index, chr = ''; for (index = 0; index < literal.length; index += chr.length) { chr = pctEncoder.pctCharAt(literal, index); if (chr.length > 1) { result += chr; } else { result += rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr); } } return result; } return { encode: encode, encodePassReserved: encodePassReserved, encodeLiteral: encodeLiteral, encodeLiteralCharacter: encodeLiteralCharacter }; }()); // the operators defined by rfc 6570 var operators = (function () { var bySymbol = {}; function create (symbol) { bySymbol[symbol] = { symbol: symbol, separator: (symbol === '?') ? '&' : (symbol === '' || symbol === '+' || symbol === '#') ? ',' : symbol, named: symbol === ';' || symbol === '&' || symbol === '?', ifEmpty: (symbol === '&' || symbol === '?') ? '=' : '', first: (symbol === '+' ) ? '' : symbol, encode: (symbol === '+' || symbol === '#') ? encodingHelper.encodePassReserved : encodingHelper.encode, toString: function () { return this.symbol; } }; } create(''); create('+'); create('#'); create('.'); create('/'); create(';'); create('?'); create('&'); return { valueOf: function (chr) { if (bySymbol[chr]) { return bySymbol[chr]; } if ("=,!@|".indexOf(chr) >= 0) { return null; } return bySymbol['']; } }; }()); /** * Detects, whether a given element is defined in the sense of rfc 6570 * Section 2.3 of the RFC makes clear defintions: * * undefined and null are not defined. * * the empty string is defined * * an array ("list") is defined, if it is not empty (even if all elements are not defined) * * an object ("map") is defined, if it contains at least one property with defined value * @param object * @return {Boolean} */ function isDefined (object) { var propertyName; if (object === null || object === undefined) { return false; } if (objectHelper.isArray(object)) { // Section 2.3: A variable defined as a list value is considered undefined if the list contains zero members return object.length > 0; } if (typeof object === "string" || typeof object === "number" || typeof object === "boolean") { // falsy values like empty strings, false or 0 are "defined" return true; } // else Object for (propertyName in object) { if (object.hasOwnProperty(propertyName) && isDefined(object[propertyName])) { return true; } } return false; } var LiteralExpression = (function () { function LiteralExpression (literal) { this.literal = encodingHelper.encodeLiteral(literal); } LiteralExpression.prototype.expand = function () { return this.literal; }; LiteralExpression.prototype.toString = LiteralExpression.prototype.expand; return LiteralExpression; }()); var parse = (function () { function parseExpression (expressionText) { var operator, varspecs = [], varspec = null, varnameStart = null, maxLengthStart = null, index, chr = ''; function closeVarname () { var varname = expressionText.substring(varnameStart, index); if (varname.length === 0) { throw new UriTemplateError({expressionText: expressionText, message: "a varname must be specified", position: index}); } varspec = {varname: varname, exploded: false, maxLength: null}; varnameStart = null; } function closeMaxLength () { if (maxLengthStart === index) { throw new UriTemplateError({expressionText: expressionText, message: "after a ':' you have to specify the length", position: index}); } varspec.maxLength = parseInt(expressionText.substring(maxLengthStart, index), 10); maxLengthStart = null; } operator = (function (operatorText) { var op = operators.valueOf(operatorText); if (op === null) { throw new UriTemplateError({expressionText: expressionText, message: "illegal use of reserved operator", position: index, operator: operatorText}); } return op; }(expressionText.charAt(0))); index = operator.symbol.length; varnameStart = index; for (; index < expressionText.length; index += chr.length) { chr = pctEncoder.pctCharAt(expressionText, index); if (varnameStart !== null) { // the spec says: varname = varchar *( ["."] varchar ) // so a dot is allowed except for the first char if (chr === '.') { if (varnameStart === index) { throw new UriTemplateError({expressionText: expressionText, message: "a varname MUST NOT start with a dot", position: index}); } continue; } if (rfcCharHelper.isVarchar(chr)) { continue; } closeVarname(); } if (maxLengthStart !== null) { if (index === maxLengthStart && chr === '0') { throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must not start with digit 0", position: index}); } if (charHelper.isDigit(chr)) { if (index - maxLengthStart >= 4) { throw new UriTemplateError({expressionText: expressionText, message: "A :prefix must have max 4 digits", position: index}); } continue; } closeMaxLength(); } if (chr === ':') { if (varspec.maxLength !== null) { throw new UriTemplateError({expressionText: expressionText, message: "only one :maxLength is allowed per varspec", position: index}); } if (varspec.exploded) { throw new UriTemplateError({expressionText: expressionText, message: "an exploeded varspec MUST NOT be varspeced", position: index}); } maxLengthStart = index + 1; continue; } if (chr === '*') { if (varspec === null) { throw new UriTemplateError({expressionText: expressionText, message: "exploded without varspec", position: index}); } if (varspec.exploded) { throw new UriTemplateError({expressionText: expressionText, message: "exploded twice", position: index}); } if (varspec.maxLength) { throw new UriTemplateError({expressionText: expressionText, message: "an explode (*) MUST NOT follow to a prefix", position: index}); } varspec.exploded = true; continue; } // the only legal character now is the comma if (chr === ',') { varspecs.push(varspec); varspec = null; varnameStart = index + 1; continue; } throw new UriTemplateError({expressionText: expressionText, message: "illegal character", character: chr, position: index}); } // for chr if (varnameStart !== null) { closeVarname(); } if (maxLengthStart !== null) { closeMaxLength(); } varspecs.push(varspec); return new VariableExpression(expressionText, operator, varspecs); } function escape_regexp_string(string) { // http://simonwillison.net/2006/Jan/20/escape/ return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&"); } function parse (uriTemplateText) { // assert filled string var index, chr, expressions = [], expression, braceOpenIndex = null, regexp_string = '', can_match = true, literalStart = 0; for (index = 0; index < uriTemplateText.length; index += 1) { chr = uriTemplateText.charAt(index); if (literalStart !== null) { if (chr === '}') { throw new UriTemplateError({templateText: uriTemplateText, message: "unopened brace closed", position: index}); } if (chr === '{') { if (literalStart < index) { expression = new LiteralExpression(uriTemplateText.substring(literalStart, index)); expressions.push(expression); regexp_string += escape_regexp_string( expression.literal); } literalStart = null; braceOpenIndex = index; } continue; } if (braceOpenIndex !== null) { // here just { is forbidden if (chr === '{') { throw new UriTemplateError({templateText: uriTemplateText, message: "brace already opened", position: index}); } if (chr === '}') { if (braceOpenIndex + 1 === index) { throw new UriTemplateError({templateText: uriTemplateText, message: "empty braces", position: braceOpenIndex}); } try { expression = parseExpression(uriTemplateText.substring(braceOpenIndex + 1, index)); } catch (error) { if (error.prototype === UriTemplateError.prototype) { throw new UriTemplateError({templateText: uriTemplateText, message: error.options.message, position: braceOpenIndex + error.options.position, details: error.options}); } throw error; } expressions.push(expression); if (expression.operator.symbol.length === 0) { regexp_string += "([^/]+)"; } else { can_match = false; } braceOpenIndex = null; literalStart = index + 1; } continue; } throw new Error('reached unreachable code'); } if (braceOpenIndex !== null) { throw new UriTemplateError({templateText: uriTemplateText, message: "unclosed brace", position: braceOpenIndex}); } if (literalStart < uriTemplateText.length) { expression = new LiteralExpression(uriTemplateText.substring(literalStart)); expressions.push(expression); regexp_string += escape_regexp_string(expression.literal); } if (can_match === false) { regexp_string = undefined; } return new UriTemplate(uriTemplateText, expressions, regexp_string); } return parse; }()); var VariableExpression = (function () { // helper function if JSON is not available function prettyPrint (value) { return (JSON && JSON.stringify) ? JSON.stringify(value) : value; } function isEmpty (value) { if (!isDefined(value)) { return true; } if (objectHelper.isString(value)) { return value === ''; } if (objectHelper.isNumber(value) || objectHelper.isBoolean(value)) { return false; } if (objectHelper.isArray(value)) { return value.length === 0; } for (var propertyName in value) { if (value.hasOwnProperty(propertyName)) { return false; } } return true; } function propertyArray (object) { var result = [], propertyName; for (propertyName in object) { if (object.hasOwnProperty(propertyName)) { result.push({name: propertyName, value: object[propertyName]}); } } return result; } function VariableExpression (templateText, operator, varspecs) { this.templateText = templateText; this.operator = operator; this.varspecs = varspecs; } VariableExpression.prototype.toString = function () { return this.templateText; }; function expandSimpleValue(varspec, operator, value) { var result = ''; value = value.toString(); if (operator.named) { result += encodingHelper.encodeLiteral(varspec.varname); if (value === '') { result += operator.ifEmpty; return result; } result += '='; } if (varspec.maxLength !== null) { value = value.substr(0, varspec.maxLength); } result += operator.encode(value); return result; } function valueDefined (nameValue) { return isDefined(nameValue.value); } function expandNotExploded(varspec, operator, value) { var arr = [], result = ''; if (operator.named) { result += encodingHelper.encodeLiteral(varspec.varname); if (isEmpty(value)) { result += operator.ifEmpty; return result; } result += '='; } if (objectHelper.isArray(value)) { arr = value; arr = objectHelper.filter(arr, isDefined); arr = objectHelper.map(arr, operator.encode); result += objectHelper.join(arr, ','); } else { arr = propertyArray(value); arr = objectHelper.filter(arr, valueDefined); arr = objectHelper.map(arr, function (nameValue) { return operator.encode(nameValue.name) + ',' + operator.encode(nameValue.value); }); result += objectHelper.join(arr, ','); } return result; } function expandExplodedNamed (varspec, operator, value) { var isArray = objectHelper.isArray(value), arr = []; if (isArray) { arr = value; arr = objectHelper.filter(arr, isDefined); arr = objectHelper.map(arr, function (listElement) { var tmp = encodingHelper.encodeLiteral(varspec.varname); if (isEmpty(listElement)) { tmp += operator.ifEmpty; } else { tmp += '=' + operator.encode(listElement); } return tmp; }); } else { arr = propertyArray(value); arr = objectHelper.filter(arr, valueDefined); arr = objectHelper.map(arr, function (nameValue) { var tmp = encodingHelper.encodeLiteral(nameValue.name); if (isEmpty(nameValue.value)) { tmp += operator.ifEmpty; } else { tmp += '=' + operator.encode(nameValue.value); } return tmp; }); } return objectHelper.join(arr, operator.separator); } function expandExplodedUnnamed (operator, value) { var arr = [], result = ''; if (objectHelper.isArray(value)) { arr = value; arr = objectHelper.filter(arr, isDefined); arr = objectHelper.map(arr, operator.encode); result += objectHelper.join(arr, operator.separator); } else { arr = propertyArray(value); arr = objectHelper.filter(arr, function (nameValue) { return isDefined(nameValue.value); }); arr = objectHelper.map(arr, function (nameValue) { return operator.encode(nameValue.name) + '=' + operator.encode(nameValue.value); }); result += objectHelper.join(arr, operator.separator); } return result; } VariableExpression.prototype.expand = function (variables) { var expanded = [], index, varspec, value, valueIsArr, oneExploded = false, operator = this.operator; // expand each varspec and join with operator's separator for (index = 0; index < this.varspecs.length; index += 1) { varspec = this.varspecs[index]; value = variables[varspec.varname]; // if (!isDefined(value)) { // if (variables.hasOwnProperty(varspec.name)) { if (value === null || value === undefined) { continue; } if (varspec.exploded) { oneExploded = true; } valueIsArr = objectHelper.isArray(value); if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { expanded.push(expandSimpleValue(varspec, operator, value)); } else if (varspec.maxLength && isDefined(value)) { // 2.4.1 of the spec says: "Prefix modifiers are not applicable to variables that have composite values." throw new Error('Prefix modifiers are not applicable to variables that have composite values. You tried to expand ' + this + " with " + prettyPrint(value)); } else if (!varspec.exploded) { if (operator.named || !isEmpty(value)) { expanded.push(expandNotExploded(varspec, operator, value)); } } else if (isDefined(value)) { if (operator.named) { expanded.push(expandExplodedNamed(varspec, operator, value)); } else { expanded.push(expandExplodedUnnamed(operator, value)); } } } if (expanded.length === 0) { return ""; } else { return operator.first + objectHelper.join(expanded, operator.separator); } }; return VariableExpression; }()); var UriTemplate = (function () { function UriTemplate (templateText, expressions, regexp_string) { this.templateText = templateText; this.expressions = expressions; if (regexp_string !== undefined) { this.regexp = new RegExp("^" + regexp_string + "$"); } objectHelper.deepFreeze(this); } UriTemplate.prototype.toString = function () { return this.templateText; }; UriTemplate.prototype.expand = function (variables) { // this.expressions.map(function (expression) {return expression.expand(variables);}).join(''); var index, result = ''; for (index = 0; index < this.expressions.length; index += 1) { result += this.expressions[index].expand(variables); } return result; }; UriTemplate.prototype.extract = function (text) { var expression_index, extracted_index = 1, expression, varspec, matched = true, variables = {}, result; if ((this.regexp !== undefined) && (this.regexp.test(text))) { result = this.regexp.exec(text); for (expression_index = 0; expression_index < this.expressions.length; expression_index += 1) { expression = this.expressions[expression_index]; if (expression.literal === undefined) { if ((expression.operator !== undefined) && (expression.operator.symbol.length === 0) && (expression.varspecs.length === 1)) { varspec = expression.varspecs[0]; if ((varspec.exploded === false) && (varspec.maxLength === null)) { if (result[extracted_index].indexOf(',') === -1) { variables[varspec.varname] = decodeURIComponent(result[extracted_index]); extracted_index += 1; } else { matched = false; } } else { matched = false; } } else { matched = false; } } } if (matched) { return variables; } } return false; }; UriTemplate.parse = parse; UriTemplate.UriTemplateError = UriTemplateError; return UriTemplate; }()); exportCallback(UriTemplate); }(function (UriTemplate) { "use strict"; // export UriTemplate, when module is present, or pass it to window or global if (typeof module !== "undefined") { module.exports = UriTemplate; } else if (typeof define === "function") { define([],function() { return UriTemplate; }); } else if (typeof window !== "undefined") { window.UriTemplate = UriTemplate; } else { global.UriTemplate = UriTemplate; } } )); ;//! moment.js //! version : 2.5.0 //! authors : Tim Wood, Iskren Chernev, Moment.js contributors //! license : MIT //! momentjs.com (function (undefined) { /************************************ Constants ************************************/ var moment, VERSION = "2.5.0", global = this, round = Math.round, i, YEAR = 0, MONTH = 1, DATE = 2, HOUR = 3, MINUTE = 4, SECOND = 5, MILLISECOND = 6, // internal storage for language config files languages = {}, // check for nodeJS hasModule = (typeof module !== 'undefined' && module.exports && typeof require !== 'undefined'), // ASP.NET json date format regex aspNetJsonRegex = /^\/?Date\((\-?\d+)/i, aspNetTimeSpanJsonRegex = /(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/, // from http://docs.closure-library.googlecode.com/git/closure_goog_date_date.js.source.html // somewhat more in line with 4.4.3.2 2004 spec, but allows decimal anywhere isoDurationRegex = /^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/, // format tokens formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g, localFormattingTokens = /(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g, // parsing token regexes parseTokenOneOrTwoDigits = /\d\d?/, // 0 - 99 parseTokenOneToThreeDigits = /\d{1,3}/, // 0 - 999 parseTokenOneToFourDigits = /\d{1,4}/, // 0 - 9999 parseTokenOneToSixDigits = /[+\-]?\d{1,6}/, // -999,999 - 999,999 parseTokenDigits = /\d+/, // nonzero number of digits parseTokenWord = /[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i, // any word (or two) characters or numbers including two/three word month in arabic. parseTokenTimezone = /Z|[\+\-]\d\d:?\d\d/gi, // +00:00 -00:00 +0000 -0000 or Z parseTokenT = /T/i, // T (ISO separator) parseTokenTimestampMs = /[\+\-]?\d+(\.\d{1,3})?/, // 123456789 123456789.123 //strict parsing regexes parseTokenOneDigit = /\d/, // 0 - 9 parseTokenTwoDigits = /\d\d/, // 00 - 99 parseTokenThreeDigits = /\d{3}/, // 000 - 999 parseTokenFourDigits = /\d{4}/, // 0000 - 9999 parseTokenSixDigits = /[+\-]?\d{6}/, // -999,999 - 999,999 // iso 8601 regex // 0000-00-00 0000-W00 or 0000-W00-0 + T + 00 or 00:00 or 00:00:00 or 00:00:00.000 + +00:00 or +0000 or +00) isoRegex = /^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/, isoFormat = 'YYYY-MM-DDTHH:mm:ssZ', isoDates = [ 'YYYY-MM-DD', 'GGGG-[W]WW', 'GGGG-[W]WW-E', 'YYYY-DDD' ], // iso time formats and regexes isoTimes = [ ['HH:mm:ss.SSSS', /(T| )\d\d:\d\d:\d\d\.\d{1,3}/], ['HH:mm:ss', /(T| )\d\d:\d\d:\d\d/], ['HH:mm', /(T| )\d\d:\d\d/], ['HH', /(T| )\d\d/] ], // timezone chunker "+10:00" > ["10", "00"] or "-1530" > ["-15", "30"] parseTimezoneChunker = /([\+\-]|\d\d)/gi, // getter and setter names proxyGettersAndSetters = 'Date|Hours|Minutes|Seconds|Milliseconds'.split('|'), unitMillisecondFactors = { 'Milliseconds' : 1, 'Seconds' : 1e3, 'Minutes' : 6e4, 'Hours' : 36e5, 'Days' : 864e5, 'Months' : 2592e6, 'Years' : 31536e6 }, unitAliases = { ms : 'millisecond', s : 'second', m : 'minute', h : 'hour', d : 'day', D : 'date', w : 'week', W : 'isoWeek', M : 'month', y : 'year', DDD : 'dayOfYear', e : 'weekday', E : 'isoWeekday', gg: 'weekYear', GG: 'isoWeekYear' }, camelFunctions = { dayofyear : 'dayOfYear', isoweekday : 'isoWeekday', isoweek : 'isoWeek', weekyear : 'weekYear', isoweekyear : 'isoWeekYear' }, // format function strings formatFunctions = {}, // tokens to ordinalize and pad ordinalizeTokens = 'DDD w W M D d'.split(' '), paddedTokens = 'M D H h m s w W'.split(' '), formatTokenFunctions = { M : function () { return this.month() + 1; }, MMM : function (format) { return this.lang().monthsShort(this, format); }, MMMM : function (format) { return this.lang().months(this, format); }, D : function () { return this.date(); }, DDD : function () { return this.dayOfYear(); }, d : function () { return this.day(); }, dd : function (format) { return this.lang().weekdaysMin(this, format); }, ddd : function (format) { return this.lang().weekdaysShort(this, format); }, dddd : function (format) { return this.lang().weekdays(this, format); }, w : function () { return this.week(); }, W : function () { return this.isoWeek(); }, YY : function () { return leftZeroFill(this.year() % 100, 2); }, YYYY : function () { return leftZeroFill(this.year(), 4); }, YYYYY : function () { return leftZeroFill(this.year(), 5); }, YYYYYY : function () { var y = this.year(), sign = y >= 0 ? '+' : '-'; return sign + leftZeroFill(Math.abs(y), 6); }, gg : function () { return leftZeroFill(this.weekYear() % 100, 2); }, gggg : function () { return this.weekYear(); }, ggggg : function () { return leftZeroFill(this.weekYear(), 5); }, GG : function () { return leftZeroFill(this.isoWeekYear() % 100, 2); }, GGGG : function () { return this.isoWeekYear(); }, GGGGG : function () { return leftZeroFill(this.isoWeekYear(), 5); }, e : function () { return this.weekday(); }, E : function () { return this.isoWeekday(); }, a : function () { return this.lang().meridiem(this.hours(), this.minutes(), true); }, A : function () { return this.lang().meridiem(this.hours(), this.minutes(), false); }, H : function () { return this.hours(); }, h : function () { return this.hours() % 12 || 12; }, m : function () { return this.minutes(); }, s : function () { return this.seconds(); }, S : function () { return toInt(this.milliseconds() / 100); }, SS : function () { return leftZeroFill(toInt(this.milliseconds() / 10), 2); }, SSS : function () { return leftZeroFill(this.milliseconds(), 3); }, SSSS : function () { return leftZeroFill(this.milliseconds(), 3); }, Z : function () { var a = -this.zone(), b = "+"; if (a < 0) { a = -a; b = "-"; } return b + leftZeroFill(toInt(a / 60), 2) + ":" + leftZeroFill(toInt(a) % 60, 2); }, ZZ : function () { var a = -this.zone(), b = "+"; if (a < 0) { a = -a; b = "-"; } return b + leftZeroFill(toInt(a / 60), 2) + leftZeroFill(toInt(a) % 60, 2); }, z : function () { return this.zoneAbbr(); }, zz : function () { return this.zoneName(); }, X : function () { return this.unix(); }, Q : function () { return this.quarter(); } }, lists = ['months', 'monthsShort', 'weekdays', 'weekdaysShort', 'weekdaysMin']; function padToken(func, count) { return function (a) { return leftZeroFill(func.call(this, a), count); }; } function ordinalizeToken(func, period) { return function (a) { return this.lang().ordinal(func.call(this, a), period); }; } while (ordinalizeTokens.length) { i = ordinalizeTokens.pop(); formatTokenFunctions[i + 'o'] = ordinalizeToken(formatTokenFunctions[i], i); } while (paddedTokens.length) { i = paddedTokens.pop(); formatTokenFunctions[i + i] = padToken(formatTokenFunctions[i], 2); } formatTokenFunctions.DDDD = padToken(formatTokenFunctions.DDD, 3); /************************************ Constructors ************************************/ function Language() { } // Moment prototype object function Moment(config) { checkOverflow(config); extend(this, config); } // Duration Constructor function Duration(duration) { var normalizedInput = normalizeObjectUnits(duration), years = normalizedInput.year || 0, months = normalizedInput.month || 0, weeks = normalizedInput.week || 0, days = normalizedInput.day || 0, hours = normalizedInput.hour || 0, minutes = normalizedInput.minute || 0, seconds = normalizedInput.second || 0, milliseconds = normalizedInput.millisecond || 0; // representation for dateAddRemove this._milliseconds = +milliseconds + seconds * 1e3 + // 1000 minutes * 6e4 + // 1000 * 60 hours * 36e5; // 1000 * 60 * 60 // Because of dateAddRemove treats 24 hours as different from a // day when working around DST, we need to store them separately this._days = +days + weeks * 7; // It is impossible translate months into days without knowing // which months you are are talking about, so we have to store // it separately. this._months = +months + years * 12; this._data = {}; this._bubble(); } /************************************ Helpers ************************************/ function extend(a, b) { for (var i in b) { if (b.hasOwnProperty(i)) { a[i] = b[i]; } } if (b.hasOwnProperty("toString")) { a.toString = b.toString; } if (b.hasOwnProperty("valueOf")) { a.valueOf = b.valueOf; } return a; } function absRound(number) { if (number < 0) { return Math.ceil(number); } else { return Math.floor(number); } } // left zero fill a number // see http://jsperf.com/left-zero-filling for performance comparison function leftZeroFill(number, targetLength, forceSign) { var output = Math.abs(number) + '', sign = number >= 0; while (output.length < targetLength) { output = '0' + output; } return (sign ? (forceSign ? '+' : '') : '-') + output; } // helper function for _.addTime and _.subtractTime function addOrSubtractDurationFromMoment(mom, duration, isAdding, ignoreUpdateOffset) { var milliseconds = duration._milliseconds, days = duration._days, months = duration._months, minutes, hours; if (milliseconds) { mom._d.setTime(+mom._d + milliseconds * isAdding); } // store the minutes and hours so we can restore them if (days || months) { minutes = mom.minute(); hours = mom.hour(); } if (days) { mom.date(mom.date() + days * isAdding); } if (months) { mom.month(mom.month() + months * isAdding); } if (milliseconds && !ignoreUpdateOffset) { moment.updateOffset(mom); } // restore the minutes and hours after possibly changing dst if (days || months) { mom.minute(minutes); mom.hour(hours); } } // check if is an array function isArray(input) { return Object.prototype.toString.call(input) === '[object Array]'; } function isDate(input) { return Object.prototype.toString.call(input) === '[object Date]' || input instanceof Date; } // compare two arrays, return the number of differences function compareArrays(array1, array2, dontConvert) { var len = Math.min(array1.length, array2.length), lengthDiff = Math.abs(array1.length - array2.length), diffs = 0, i; for (i = 0; i < len; i++) { if ((dontConvert && array1[i] !== array2[i]) || (!dontConvert && toInt(array1[i]) !== toInt(array2[i]))) { diffs++; } } return diffs + lengthDiff; } function normalizeUnits(units) { if (units) { var lowered = units.toLowerCase().replace(/(.)s$/, '$1'); units = unitAliases[units] || camelFunctions[lowered] || lowered; } return units; } function normalizeObjectUnits(inputObject) { var normalizedInput = {}, normalizedProp, prop; for (prop in inputObject) { if (inputObject.hasOwnProperty(prop)) { normalizedProp = normalizeUnits(prop); if (normalizedProp) { normalizedInput[normalizedProp] = inputObject[prop]; } } } return normalizedInput; } function makeList(field) { var count, setter; if (field.indexOf('week') === 0) { count = 7; setter = 'day'; } else if (field.indexOf('month') === 0) { count = 12; setter = 'month'; } else { return; } moment[field] = function (format, index) { var i, getter, method = moment.fn._lang[field], results = []; if (typeof format === 'number') { index = format; format = undefined; } getter = function (i) { var m = moment().utc().set(setter, i); return method.call(moment.fn._lang, m, format || ''); }; if (index != null) { return getter(index); } else { for (i = 0; i < count; i++) { results.push(getter(i)); } return results; } }; } function toInt(argumentForCoercion) { var coercedNumber = +argumentForCoercion, value = 0; if (coercedNumber !== 0 && isFinite(coercedNumber)) { if (coercedNumber >= 0) { value = Math.floor(coercedNumber); } else { value = Math.ceil(coercedNumber); } } return value; } function daysInMonth(year, month) { return new Date(Date.UTC(year, month + 1, 0)).getUTCDate(); } function daysInYear(year) { return isLeapYear(year) ? 366 : 365; } function isLeapYear(year) { return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0; } function checkOverflow(m) { var overflow; if (m._a && m._pf.overflow === -2) { overflow = m._a[MONTH] < 0 || m._a[MONTH] > 11 ? MONTH : m._a[DATE] < 1 || m._a[DATE] > daysInMonth(m._a[YEAR], m._a[MONTH]) ? DATE : m._a[HOUR] < 0 || m._a[HOUR] > 23 ? HOUR : m._a[MINUTE] < 0 || m._a[MINUTE] > 59 ? MINUTE : m._a[SECOND] < 0 || m._a[SECOND] > 59 ? SECOND : m._a[MILLISECOND] < 0 || m._a[MILLISECOND] > 999 ? MILLISECOND : -1; if (m._pf._overflowDayOfYear && (overflow < YEAR || overflow > DATE)) { overflow = DATE; } m._pf.overflow = overflow; } } function initializeParsingFlags(config) { config._pf = { empty : false, unusedTokens : [], unusedInput : [], overflow : -2, charsLeftOver : 0, nullInput : false, invalidMonth : null, invalidFormat : false, userInvalidated : false, iso: false }; } function isValid(m) { if (m._isValid == null) { m._isValid = !isNaN(m._d.getTime()) && m._pf.overflow < 0 && !m._pf.empty && !m._pf.invalidMonth && !m._pf.nullInput && !m._pf.invalidFormat && !m._pf.userInvalidated; if (m._strict) { m._isValid = m._isValid && m._pf.charsLeftOver === 0 && m._pf.unusedTokens.length === 0; } } return m._isValid; } function normalizeLanguage(key) { return key ? key.toLowerCase().replace('_', '-') : key; } // Return a moment from input, that is local/utc/zone equivalent to model. function makeAs(input, model) { return model._isUTC ? moment(input).zone(model._offset || 0) : moment(input).local(); } /************************************ Languages ************************************/ extend(Language.prototype, { set : function (config) { var prop, i; for (i in config) { prop = config[i]; if (typeof prop === 'function') { this[i] = prop; } else { this['_' + i] = prop; } } }, _months : "January_February_March_April_May_June_July_August_September_October_November_December".split("_"), months : function (m) { return this._months[m.month()]; }, _monthsShort : "Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"), monthsShort : function (m) { return this._monthsShort[m.month()]; }, monthsParse : function (monthName) { var i, mom, regex; if (!this._monthsParse) { this._monthsParse = []; } for (i = 0; i < 12; i++) { // make the regex if we don't have it already if (!this._monthsParse[i]) { mom = moment.utc([2000, i]); regex = '^' + this.months(mom, '') + '|^' + this.monthsShort(mom, ''); this._monthsParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (this._monthsParse[i].test(monthName)) { return i; } } }, _weekdays : "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), weekdays : function (m) { return this._weekdays[m.day()]; }, _weekdaysShort : "Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"), weekdaysShort : function (m) { return this._weekdaysShort[m.day()]; }, _weekdaysMin : "Su_Mo_Tu_We_Th_Fr_Sa".split("_"), weekdaysMin : function (m) { return this._weekdaysMin[m.day()]; }, weekdaysParse : function (weekdayName) { var i, mom, regex; if (!this._weekdaysParse) { this._weekdaysParse = []; } for (i = 0; i < 7; i++) { // make the regex if we don't have it already if (!this._weekdaysParse[i]) { mom = moment([2000, 1]).day(i); regex = '^' + this.weekdays(mom, '') + '|^' + this.weekdaysShort(mom, '') + '|^' + this.weekdaysMin(mom, ''); this._weekdaysParse[i] = new RegExp(regex.replace('.', ''), 'i'); } // test the regex if (this._weekdaysParse[i].test(weekdayName)) { return i; } } }, _longDateFormat : { LT : "h:mm A", L : "MM/DD/YYYY", LL : "MMMM D YYYY", LLL : "MMMM D YYYY LT", LLLL : "dddd, MMMM D YYYY LT" }, longDateFormat : function (key) { var output = this._longDateFormat[key]; if (!output && this._longDateFormat[key.toUpperCase()]) { output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { return val.slice(1); }); this._longDateFormat[key] = output; } return output; }, isPM : function (input) { // IE8 Quirks Mode & IE7 Standards Mode do not allow accessing strings like arrays // Using charAt should be more compatible. return ((input + '').toLowerCase().charAt(0) === 'p'); }, _meridiemParse : /[ap]\.?m?\.?/i, meridiem : function (hours, minutes, isLower) { if (hours > 11) { return isLower ? 'pm' : 'PM'; } else { return isLower ? 'am' : 'AM'; } }, _calendar : { sameDay : '[Today at] LT', nextDay : '[Tomorrow at] LT', nextWeek : 'dddd [at] LT', lastDay : '[Yesterday at] LT', lastWeek : '[Last] dddd [at] LT', sameElse : 'L' }, calendar : function (key, mom) { var output = this._calendar[key]; return typeof output === 'function' ? output.apply(mom) : output; }, _relativeTime : { future : "in %s", past : "%s ago", s : "a few seconds", m : "a minute", mm : "%d minutes", h : "an hour", hh : "%d hours", d : "a day", dd : "%d days", M : "a month", MM : "%d months", y : "a year", yy : "%d years" }, relativeTime : function (number, withoutSuffix, string, isFuture) { var output = this._relativeTime[string]; return (typeof output === 'function') ? output(number, withoutSuffix, string, isFuture) : output.replace(/%d/i, number); }, pastFuture : function (diff, output) { var format = this._relativeTime[diff > 0 ? 'future' : 'past']; return typeof format === 'function' ? format(output) : format.replace(/%s/i, output); }, ordinal : function (number) { return this._ordinal.replace("%d", number); }, _ordinal : "%d", preparse : function (string) { return string; }, postformat : function (string) { return string; }, week : function (mom) { return weekOfYear(mom, this._week.dow, this._week.doy).week; }, _week : { dow : 0, // Sunday is the first day of the week. doy : 6 // The week that contains Jan 1st is the first week of the year. }, _invalidDate: 'Invalid date', invalidDate: function () { return this._invalidDate; } }); // Loads a language definition into the `languages` cache. The function // takes a key and optionally values. If not in the browser and no values // are provided, it will load the language file module. As a convenience, // this function also returns the language values. function loadLang(key, values) { values.abbr = key; if (!languages[key]) { languages[key] = new Language(); } languages[key].set(values); return languages[key]; } // Remove a language from the `languages` cache. Mostly useful in tests. function unloadLang(key) { delete languages[key]; } // Determines which language definition to use and returns it. // // With no parameters, it will return the global language. If you // pass in a language key, such as 'en', it will return the // definition for 'en', so long as 'en' has already been loaded using // moment.lang. function getLangDefinition(key) { var i = 0, j, lang, next, split, get = function (k) { if (!languages[k] && hasModule) { try { require('./lang/' + k); } catch (e) { } } return languages[k]; }; if (!key) { return moment.fn._lang; } if (!isArray(key)) { //short-circuit everything else lang = get(key); if (lang) { return lang; } key = [key]; } //pick the language from the array //try ['en-au', 'en-gb'] as 'en-au', 'en-gb', 'en', as in move through the list trying each //substring from most specific to least, but move to the next array item if it's a more specific variant than the current root while (i < key.length) { split = normalizeLanguage(key[i]).split('-'); j = split.length; next = normalizeLanguage(key[i + 1]); next = next ? next.split('-') : null; while (j > 0) { lang = get(split.slice(0, j).join('-')); if (lang) { return lang; } if (next && next.length >= j && compareArrays(split, next, true) >= j - 1) { //the next array item is better than a shallower substring of this one break; } j--; } i++; } return moment.fn._lang; } /************************************ Formatting ************************************/ function removeFormattingTokens(input) { if (input.match(/\[[\s\S]/)) { return input.replace(/^\[|\]$/g, ""); } return input.replace(/\\/g, ""); } function makeFormatFunction(format) { var array = format.match(formattingTokens), i, length; for (i = 0, length = array.length; i < length; i++) { if (formatTokenFunctions[array[i]]) { array[i] = formatTokenFunctions[array[i]]; } else { array[i] = removeFormattingTokens(array[i]); } } return function (mom) { var output = ""; for (i = 0; i < length; i++) { output += array[i] instanceof Function ? array[i].call(mom, format) : array[i]; } return output; }; } // format date using native date object function formatMoment(m, format) { if (!m.isValid()) { return m.lang().invalidDate(); } format = expandFormat(format, m.lang()); if (!formatFunctions[format]) { formatFunctions[format] = makeFormatFunction(format); } return formatFunctions[format](m); } function expandFormat(format, lang) { var i = 5; function replaceLongDateFormatTokens(input) { return lang.longDateFormat(input) || input; } localFormattingTokens.lastIndex = 0; while (i >= 0 && localFormattingTokens.test(format)) { format = format.replace(localFormattingTokens, replaceLongDateFormatTokens); localFormattingTokens.lastIndex = 0; i -= 1; } return format; } /************************************ Parsing ************************************/ // get the regex to find the next token function getParseRegexForToken(token, config) { var a, strict = config._strict; switch (token) { case 'DDDD': return parseTokenThreeDigits; case 'YYYY': case 'GGGG': case 'gggg': return strict ? parseTokenFourDigits : parseTokenOneToFourDigits; case 'YYYYYY': case 'YYYYY': case 'GGGGG': case 'ggggg': return strict ? parseTokenSixDigits : parseTokenOneToSixDigits; case 'S': if (strict) { return parseTokenOneDigit; } /* falls through */ case 'SS': if (strict) { return parseTokenTwoDigits; } /* falls through */ case 'SSS': case 'DDD': return strict ? parseTokenThreeDigits : parseTokenOneToThreeDigits; case 'MMM': case 'MMMM': case 'dd': case 'ddd': case 'dddd': return parseTokenWord; case 'a': case 'A': return getLangDefinition(config._l)._meridiemParse; case 'X': return parseTokenTimestampMs; case 'Z': case 'ZZ': return parseTokenTimezone; case 'T': return parseTokenT; case 'SSSS': return parseTokenDigits; case 'MM': case 'DD': case 'YY': case 'GG': case 'gg': case 'HH': case 'hh': case 'mm': case 'ss': case 'ww': case 'WW': return strict ? parseTokenTwoDigits : parseTokenOneOrTwoDigits; case 'M': case 'D': case 'd': case 'H': case 'h': case 'm': case 's': case 'w': case 'W': case 'e': case 'E': return strict ? parseTokenOneDigit : parseTokenOneOrTwoDigits; default : a = new RegExp(regexpEscape(unescapeFormat(token.replace('\\', '')), "i")); return a; } } function timezoneMinutesFromString(string) { string = string || ""; var possibleTzMatches = (string.match(parseTokenTimezone) || []), tzChunk = possibleTzMatches[possibleTzMatches.length - 1] || [], parts = (tzChunk + '').match(parseTimezoneChunker) || ['-', 0, 0], minutes = +(parts[1] * 60) + toInt(parts[2]); return parts[0] === '+' ? -minutes : minutes; } // function to convert string input to date function addTimeToArrayFromToken(token, input, config) { var a, datePartArray = config._a; switch (token) { // MONTH case 'M' : // fall through to MM case 'MM' : if (input != null) { datePartArray[MONTH] = toInt(input) - 1; } break; case 'MMM' : // fall through to MMMM case 'MMMM' : a = getLangDefinition(config._l).monthsParse(input); // if we didn't find a month name, mark the date as invalid. if (a != null) { datePartArray[MONTH] = a; } else { config._pf.invalidMonth = input; } break; // DAY OF MONTH case 'D' : // fall through to DD case 'DD' : if (input != null) { datePartArray[DATE] = toInt(input); } break; // DAY OF YEAR case 'DDD' : // fall through to DDDD case 'DDDD' : if (input != null) { config._dayOfYear = toInt(input); } break; // YEAR case 'YY' : datePartArray[YEAR] = toInt(input) + (toInt(input) > 68 ? 1900 : 2000); break; case 'YYYY' : case 'YYYYY' : case 'YYYYYY' : datePartArray[YEAR] = toInt(input); break; // AM / PM case 'a' : // fall through to A case 'A' : config._isPm = getLangDefinition(config._l).isPM(input); break; // 24 HOUR case 'H' : // fall through to hh case 'HH' : // fall through to hh case 'h' : // fall through to hh case 'hh' : datePartArray[HOUR] = toInt(input); break; // MINUTE case 'm' : // fall through to mm case 'mm' : datePartArray[MINUTE] = toInt(input); break; // SECOND case 's' : // fall through to ss case 'ss' : datePartArray[SECOND] = toInt(input); break; // MILLISECOND case 'S' : case 'SS' : case 'SSS' : case 'SSSS' : datePartArray[MILLISECOND] = toInt(('0.' + input) * 1000); break; // UNIX TIMESTAMP WITH MS case 'X': config._d = new Date(parseFloat(input) * 1000); break; // TIMEZONE case 'Z' : // fall through to ZZ case 'ZZ' : config._useUTC = true; config._tzm = timezoneMinutesFromString(input); break; case 'w': case 'ww': case 'W': case 'WW': case 'd': case 'dd': case 'ddd': case 'dddd': case 'e': case 'E': token = token.substr(0, 1); /* falls through */ case 'gg': case 'gggg': case 'GG': case 'GGGG': case 'GGGGG': token = token.substr(0, 2); if (input) { config._w = config._w || {}; config._w[token] = input; } break; } } // convert an array to a date. // the array should mirror the parameters below // note: all values past the year are optional and will default to the lowest possible value. // [year, month, day , hour, minute, second, millisecond] function dateFromConfig(config) { var i, date, input = [], currentDate, yearToUse, fixYear, w, temp, lang, weekday, week; if (config._d) { return; } currentDate = currentDateArray(config); //compute day of the year from weeks and weekdays if (config._w && config._a[DATE] == null && config._a[MONTH] == null) { fixYear = function (val) { var int_val = parseInt(val, 10); return val ? (val.length < 3 ? (int_val > 68 ? 1900 + int_val : 2000 + int_val) : int_val) : (config._a[YEAR] == null ? moment().weekYear() : config._a[YEAR]); }; w = config._w; if (w.GG != null || w.W != null || w.E != null) { temp = dayOfYearFromWeeks(fixYear(w.GG), w.W || 1, w.E, 4, 1); } else { lang = getLangDefinition(config._l); weekday = w.d != null ? parseWeekday(w.d, lang) : (w.e != null ? parseInt(w.e, 10) + lang._week.dow : 0); week = parseInt(w.w, 10) || 1; //if we're parsing 'd', then the low day numbers may be next week if (w.d != null && weekday < lang._week.dow) { week++; } temp = dayOfYearFromWeeks(fixYear(w.gg), week, weekday, lang._week.doy, lang._week.dow); } config._a[YEAR] = temp.year; config._dayOfYear = temp.dayOfYear; } //if the day of the year is set, figure out what it is if (config._dayOfYear) { yearToUse = config._a[YEAR] == null ? currentDate[YEAR] : config._a[YEAR]; if (config._dayOfYear > daysInYear(yearToUse)) { config._pf._overflowDayOfYear = true; } date = makeUTCDate(yearToUse, 0, config._dayOfYear); config._a[MONTH] = date.getUTCMonth(); config._a[DATE] = date.getUTCDate(); } // Default to current date. // * if no year, month, day of month are given, default to today // * if day of month is given, default month and year // * if month is given, default only year // * if year is given, don't default anything for (i = 0; i < 3 && config._a[i] == null; ++i) { config._a[i] = input[i] = currentDate[i]; } // Zero out whatever was not defaulted, including time for (; i < 7; i++) { config._a[i] = input[i] = (config._a[i] == null) ? (i === 2 ? 1 : 0) : config._a[i]; } // add the offsets to the time to be parsed so that we can have a clean array for checking isValid input[HOUR] += toInt((config._tzm || 0) / 60); input[MINUTE] += toInt((config._tzm || 0) % 60); config._d = (config._useUTC ? makeUTCDate : makeDate).apply(null, input); } function dateFromObject(config) { var normalizedInput; if (config._d) { return; } normalizedInput = normalizeObjectUnits(config._i); config._a = [ normalizedInput.year, normalizedInput.month, normalizedInput.day, normalizedInput.hour, normalizedInput.minute, normalizedInput.second, normalizedInput.millisecond ]; dateFromConfig(config); } function currentDateArray(config) { var now = new Date(); if (config._useUTC) { return [ now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() ]; } else { return [now.getFullYear(), now.getMonth(), now.getDate()]; } } // date from string and format string function makeDateFromStringAndFormat(config) { config._a = []; config._pf.empty = true; // This array is used to make a Date, either with `new Date` or `Date.UTC` var lang = getLangDefinition(config._l), string = '' + config._i, i, parsedInput, tokens, token, skipped, stringLength = string.length, totalParsedInputLength = 0; tokens = expandFormat(config._f, lang).match(formattingTokens) || []; for (i = 0; i < tokens.length; i++) { token = tokens[i]; parsedInput = (string.match(getParseRegexForToken(token, config)) || [])[0]; if (parsedInput) { skipped = string.substr(0, string.indexOf(parsedInput)); if (skipped.length > 0) { config._pf.unusedInput.push(skipped); } string = string.slice(string.indexOf(parsedInput) + parsedInput.length); totalParsedInputLength += parsedInput.length; } // don't parse if it's not a known token if (formatTokenFunctions[token]) { if (parsedInput) { config._pf.empty = false; } else { config._pf.unusedTokens.push(token); } addTimeToArrayFromToken(token, parsedInput, config); } else if (config._strict && !parsedInput) { config._pf.unusedTokens.push(token); } } // add remaining unparsed input length to the string config._pf.charsLeftOver = stringLength - totalParsedInputLength; if (string.length > 0) { config._pf.unusedInput.push(string); } // handle am pm if (config._isPm && config._a[HOUR] < 12) { config._a[HOUR] += 12; } // if is 12 am, change hours to 0 if (config._isPm === false && config._a[HOUR] === 12) { config._a[HOUR] = 0; } dateFromConfig(config); checkOverflow(config); } function unescapeFormat(s) { return s.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g, function (matched, p1, p2, p3, p4) { return p1 || p2 || p3 || p4; }); } // Code from http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript function regexpEscape(s) { return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); } // date from string and array of format strings function makeDateFromStringAndArray(config) { var tempConfig, bestMoment, scoreToBeat, i, currentScore; if (config._f.length === 0) { config._pf.invalidFormat = true; config._d = new Date(NaN); return; } for (i = 0; i < config._f.length; i++) { currentScore = 0; tempConfig = extend({}, config); initializeParsingFlags(tempConfig); tempConfig._f = config._f[i]; makeDateFromStringAndFormat(tempConfig); if (!isValid(tempConfig)) { continue; } // if there is any input that was not parsed add a penalty for that format currentScore += tempConfig._pf.charsLeftOver; //or tokens currentScore += tempConfig._pf.unusedTokens.length * 10; tempConfig._pf.score = currentScore; if (scoreToBeat == null || currentScore < scoreToBeat) { scoreToBeat = currentScore; bestMoment = tempConfig; } } extend(config, bestMoment || tempConfig); } // date from iso format function makeDateFromString(config) { var i, string = config._i, match = isoRegex.exec(string); if (match) { config._pf.iso = true; for (i = 4; i > 0; i--) { if (match[i]) { // match[5] should be "T" or undefined config._f = isoDates[i - 1] + (match[6] || " "); break; } } for (i = 0; i < 4; i++) { if (isoTimes[i][1].exec(string)) { config._f += isoTimes[i][0]; break; } } if (string.match(parseTokenTimezone)) { config._f += "Z"; } makeDateFromStringAndFormat(config); } else { config._d = new Date(string); } } function makeDateFromInput(config) { var input = config._i, matched = aspNetJsonRegex.exec(input); if (input === undefined) { config._d = new Date(); } else if (matched) { config._d = new Date(+matched[1]); } else if (typeof input === 'string') { makeDateFromString(config); } else if (isArray(input)) { config._a = input.slice(0); dateFromConfig(config); } else if (isDate(input)) { config._d = new Date(+input); } else if (typeof(input) === 'object') { dateFromObject(config); } else { config._d = new Date(input); } } function makeDate(y, m, d, h, M, s, ms) { //can't just apply() to create a date: //http://stackoverflow.com/questions/181348/instantiating-a-javascript-object-by-calling-prototype-constructor-apply var date = new Date(y, m, d, h, M, s, ms); //the date constructor doesn't accept years < 1970 if (y < 1970) { date.setFullYear(y); } return date; } function makeUTCDate(y) { var date = new Date(Date.UTC.apply(null, arguments)); if (y < 1970) { date.setUTCFullYear(y); } return date; } function parseWeekday(input, language) { if (typeof input === 'string') { if (!isNaN(input)) { input = parseInt(input, 10); } else { input = language.weekdaysParse(input); if (typeof input !== 'number') { return null; } } } return input; } /************************************ Relative Time ************************************/ // helper function for moment.fn.from, moment.fn.fromNow, and moment.duration.fn.humanize function substituteTimeAgo(string, number, withoutSuffix, isFuture, lang) { return lang.relativeTime(number || 1, !!withoutSuffix, string, isFuture); } function relativeTime(milliseconds, withoutSuffix, lang) { var seconds = round(Math.abs(milliseconds) / 1000), minutes = round(seconds / 60), hours = round(minutes / 60), days = round(hours / 24), years = round(days / 365), args = seconds < 45 && ['s', seconds] || minutes === 1 && ['m'] || minutes < 45 && ['mm', minutes] || hours === 1 && ['h'] || hours < 22 && ['hh', hours] || days === 1 && ['d'] || days <= 25 && ['dd', days] || days <= 45 && ['M'] || days < 345 && ['MM', round(days / 30)] || years === 1 && ['y'] || ['yy', years]; args[2] = withoutSuffix; args[3] = milliseconds > 0; args[4] = lang; return substituteTimeAgo.apply({}, args); } /************************************ Week of Year ************************************/ // firstDayOfWeek 0 = sun, 6 = sat // the day of the week that starts the week // (usually sunday or monday) // firstDayOfWeekOfYear 0 = sun, 6 = sat // the first week is the week that contains the first // of this day of the week // (eg. ISO weeks use thursday (4)) function weekOfYear(mom, firstDayOfWeek, firstDayOfWeekOfYear) { var end = firstDayOfWeekOfYear - firstDayOfWeek, daysToDayOfWeek = firstDayOfWeekOfYear - mom.day(), adjustedMoment; if (daysToDayOfWeek > end) { daysToDayOfWeek -= 7; } if (daysToDayOfWeek < end - 7) { daysToDayOfWeek += 7; } adjustedMoment = moment(mom).add('d', daysToDayOfWeek); return { week: Math.ceil(adjustedMoment.dayOfYear() / 7), year: adjustedMoment.year() }; } //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { // The only solid way to create an iso date from year is to use // a string format (Date.UTC handles only years > 1900). Don't ask why // it doesn't need Z at the end. var d = new Date(leftZeroFill(year, 6, true) + '-01-01').getUTCDay(), daysToAdd, dayOfYear; weekday = weekday != null ? weekday : firstDayOfWeek; daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0); dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; return { year: dayOfYear > 0 ? year : year - 1, dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear }; } /************************************ Top Level Functions ************************************/ function makeMoment(config) { var input = config._i, format = config._f; if (typeof config._pf === 'undefined') { initializeParsingFlags(config); } if (input === null) { return moment.invalid({nullInput: true}); } if (typeof input === 'string') { config._i = input = getLangDefinition().preparse(input); } if (moment.isMoment(input)) { config = extend({}, input); config._d = new Date(+input._d); } else if (format) { if (isArray(format)) { makeDateFromStringAndArray(config); } else { makeDateFromStringAndFormat(config); } } else { makeDateFromInput(config); } return new Moment(config); } moment = function (input, format, lang, strict) { if (typeof(lang) === "boolean") { strict = lang; lang = undefined; } return makeMoment({ _i : input, _f : format, _l : lang, _strict : strict, _isUTC : false }); }; // creating with utc moment.utc = function (input, format, lang, strict) { var m; if (typeof(lang) === "boolean") { strict = lang; lang = undefined; } m = makeMoment({ _useUTC : true, _isUTC : true, _l : lang, _i : input, _f : format, _strict : strict }).utc(); return m; }; // creating with unix timestamp (in seconds) moment.unix = function (input) { return moment(input * 1000); }; // duration moment.duration = function (input, key) { var duration = input, // matching against regexp is expensive, do it on demand match = null, sign, ret, parseIso; if (moment.isDuration(input)) { duration = { ms: input._milliseconds, d: input._days, M: input._months }; } else if (typeof input === 'number') { duration = {}; if (key) { duration[key] = input; } else { duration.milliseconds = input; } } else if (!!(match = aspNetTimeSpanJsonRegex.exec(input))) { sign = (match[1] === "-") ? -1 : 1; duration = { y: 0, d: toInt(match[DATE]) * sign, h: toInt(match[HOUR]) * sign, m: toInt(match[MINUTE]) * sign, s: toInt(match[SECOND]) * sign, ms: toInt(match[MILLISECOND]) * sign }; } else if (!!(match = isoDurationRegex.exec(input))) { sign = (match[1] === "-") ? -1 : 1; parseIso = function (inp) { // We'd normally use ~~inp for this, but unfortunately it also // converts floats to ints. // inp may be undefined, so careful calling replace on it. var res = inp && parseFloat(inp.replace(',', '.')); // apply sign while we're at it return (isNaN(res) ? 0 : res) * sign; }; duration = { y: parseIso(match[2]), M: parseIso(match[3]), d: parseIso(match[4]), h: parseIso(match[5]), m: parseIso(match[6]), s: parseIso(match[7]), w: parseIso(match[8]) }; } ret = new Duration(duration); if (moment.isDuration(input) && input.hasOwnProperty('_lang')) { ret._lang = input._lang; } return ret; }; // version number moment.version = VERSION; // default format moment.defaultFormat = isoFormat; // This function will be called whenever a moment is mutated. // It is intended to keep the offset in sync with the timezone. moment.updateOffset = function () {}; // This function will load languages and then set the global language. If // no arguments are passed in, it will simply return the current global // language key. moment.lang = function (key, values) { var r; if (!key) { return moment.fn._lang._abbr; } if (values) { loadLang(normalizeLanguage(key), values); } else if (values === null) { unloadLang(key); key = 'en'; } else if (!languages[key]) { getLangDefinition(key); } r = moment.duration.fn._lang = moment.fn._lang = getLangDefinition(key); return r._abbr; }; // returns language data moment.langData = function (key) { if (key && key._lang && key._lang._abbr) { key = key._lang._abbr; } return getLangDefinition(key); }; // compare moment object moment.isMoment = function (obj) { return obj instanceof Moment; }; // for typechecking Duration objects moment.isDuration = function (obj) { return obj instanceof Duration; }; for (i = lists.length - 1; i >= 0; --i) { makeList(lists[i]); } moment.normalizeUnits = function (units) { return normalizeUnits(units); }; moment.invalid = function (flags) { var m = moment.utc(NaN); if (flags != null) { extend(m._pf, flags); } else { m._pf.userInvalidated = true; } return m; }; moment.parseZone = function (input) { return moment(input).parseZone(); }; /************************************ Moment Prototype ************************************/ extend(moment.fn = Moment.prototype, { clone : function () { return moment(this); }, valueOf : function () { return +this._d + ((this._offset || 0) * 60000); }, unix : function () { return Math.floor(+this / 1000); }, toString : function () { return this.clone().lang('en').format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ"); }, toDate : function () { return this._offset ? new Date(+this) : this._d; }, toISOString : function () { var m = moment(this).utc(); if (0 < m.year() && m.year() <= 9999) { return formatMoment(m, 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } else { return formatMoment(m, 'YYYYYY-MM-DD[T]HH:mm:ss.SSS[Z]'); } }, toArray : function () { var m = this; return [ m.year(), m.month(), m.date(), m.hours(), m.minutes(), m.seconds(), m.milliseconds() ]; }, isValid : function () { return isValid(this); }, isDSTShifted : function () { if (this._a) { return this.isValid() && compareArrays(this._a, (this._isUTC ? moment.utc(this._a) : moment(this._a)).toArray()) > 0; } return false; }, parsingFlags : function () { return extend({}, this._pf); }, invalidAt: function () { return this._pf.overflow; }, utc : function () { return this.zone(0); }, local : function () { this.zone(0); this._isUTC = false; return this; }, format : function (inputString) { var output = formatMoment(this, inputString || moment.defaultFormat); return this.lang().postformat(output); }, add : function (input, val) { var dur; // switch args to support add('s', 1) and add(1, 's') if (typeof input === 'string') { dur = moment.duration(+val, input); } else { dur = moment.duration(input, val); } addOrSubtractDurationFromMoment(this, dur, 1); return this; }, subtract : function (input, val) { var dur; // switch args to support subtract('s', 1) and subtract(1, 's') if (typeof input === 'string') { dur = moment.duration(+val, input); } else { dur = moment.duration(input, val); } addOrSubtractDurationFromMoment(this, dur, -1); return this; }, diff : function (input, units, asFloat) { var that = makeAs(input, this), zoneDiff = (this.zone() - that.zone()) * 6e4, diff, output; units = normalizeUnits(units); if (units === 'year' || units === 'month') { // average number of days in the months in the given dates diff = (this.daysInMonth() + that.daysInMonth()) * 432e5; // 24 * 60 * 60 * 1000 / 2 // difference in months output = ((this.year() - that.year()) * 12) + (this.month() - that.month()); // adjust by taking difference in days, average number of days // and dst in the given months. output += ((this - moment(this).startOf('month')) - (that - moment(that).startOf('month'))) / diff; // same as above but with zones, to negate all dst output -= ((this.zone() - moment(this).startOf('month').zone()) - (that.zone() - moment(that).startOf('month').zone())) * 6e4 / diff; if (units === 'year') { output = output / 12; } } else { diff = (this - that); output = units === 'second' ? diff / 1e3 : // 1000 units === 'minute' ? diff / 6e4 : // 1000 * 60 units === 'hour' ? diff / 36e5 : // 1000 * 60 * 60 units === 'day' ? (diff - zoneDiff) / 864e5 : // 1000 * 60 * 60 * 24, negate dst units === 'week' ? (diff - zoneDiff) / 6048e5 : // 1000 * 60 * 60 * 24 * 7, negate dst diff; } return asFloat ? output : absRound(output); }, from : function (time, withoutSuffix) { return moment.duration(this.diff(time)).lang(this.lang()._abbr).humanize(!withoutSuffix); }, fromNow : function (withoutSuffix) { return this.from(moment(), withoutSuffix); }, calendar : function () { // We want to compare the start of today, vs this. // Getting start-of-today depends on whether we're zone'd or not. var sod = makeAs(moment(), this).startOf('day'), diff = this.diff(sod, 'days', true), format = diff < -6 ? 'sameElse' : diff < -1 ? 'lastWeek' : diff < 0 ? 'lastDay' : diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse'; return this.format(this.lang().calendar(format, this)); }, isLeapYear : function () { return isLeapYear(this.year()); }, isDST : function () { return (this.zone() < this.clone().month(0).zone() || this.zone() < this.clone().month(5).zone()); }, day : function (input) { var day = this._isUTC ? this._d.getUTCDay() : this._d.getDay(); if (input != null) { input = parseWeekday(input, this.lang()); return this.add({ d : input - day }); } else { return day; } }, month : function (input) { var utc = this._isUTC ? 'UTC' : '', dayOfMonth; if (input != null) { if (typeof input === 'string') { input = this.lang().monthsParse(input); if (typeof input !== 'number') { return this; } } dayOfMonth = this.date(); this.date(1); this._d['set' + utc + 'Month'](input); this.date(Math.min(dayOfMonth, this.daysInMonth())); moment.updateOffset(this); return this; } else { return this._d['get' + utc + 'Month'](); } }, startOf: function (units) { units = normalizeUnits(units); // the following switch intentionally omits break keywords // to utilize falling through the cases. switch (units) { case 'year': this.month(0); /* falls through */ case 'month': this.date(1); /* falls through */ case 'week': case 'isoWeek': case 'day': this.hours(0); /* falls through */ case 'hour': this.minutes(0); /* falls through */ case 'minute': this.seconds(0); /* falls through */ case 'second': this.milliseconds(0); /* falls through */ } // weeks are a special case if (units === 'week') { this.weekday(0); } else if (units === 'isoWeek') { this.isoWeekday(1); } return this; }, endOf: function (units) { units = normalizeUnits(units); return this.startOf(units).add((units === 'isoWeek' ? 'week' : units), 1).subtract('ms', 1); }, isAfter: function (input, units) { units = typeof units !== 'undefined' ? units : 'millisecond'; return +this.clone().startOf(units) > +moment(input).startOf(units); }, isBefore: function (input, units) { units = typeof units !== 'undefined' ? units : 'millisecond'; return +this.clone().startOf(units) < +moment(input).startOf(units); }, isSame: function (input, units) { units = units || 'ms'; return +this.clone().startOf(units) === +makeAs(input, this).startOf(units); }, min: function (other) { other = moment.apply(null, arguments); return other < this ? this : other; }, max: function (other) { other = moment.apply(null, arguments); return other > this ? this : other; }, zone : function (input) { var offset = this._offset || 0; if (input != null) { if (typeof input === "string") { input = timezoneMinutesFromString(input); } if (Math.abs(input) < 16) { input = input * 60; } this._offset = input; this._isUTC = true; if (offset !== input) { addOrSubtractDurationFromMoment(this, moment.duration(offset - input, 'm'), 1, true); } } else { return this._isUTC ? offset : this._d.getTimezoneOffset(); } return this; }, zoneAbbr : function () { return this._isUTC ? "UTC" : ""; }, zoneName : function () { return this._isUTC ? "Coordinated Universal Time" : ""; }, parseZone : function () { if (this._tzm) { this.zone(this._tzm); } else if (typeof this._i === 'string') { this.zone(this._i); } return this; }, hasAlignedHourOffset : function (input) { if (!input) { input = 0; } else { input = moment(input).zone(); } return (this.zone() - input) % 60 === 0; }, daysInMonth : function () { return daysInMonth(this.year(), this.month()); }, dayOfYear : function (input) { var dayOfYear = round((moment(this).startOf('day') - moment(this).startOf('year')) / 864e5) + 1; return input == null ? dayOfYear : this.add("d", (input - dayOfYear)); }, quarter : function () { return Math.ceil((this.month() + 1.0) / 3.0); }, weekYear : function (input) { var year = weekOfYear(this, this.lang()._week.dow, this.lang()._week.doy).year; return input == null ? year : this.add("y", (input - year)); }, isoWeekYear : function (input) { var year = weekOfYear(this, 1, 4).year; return input == null ? year : this.add("y", (input - year)); }, week : function (input) { var week = this.lang().week(this); return input == null ? week : this.add("d", (input - week) * 7); }, isoWeek : function (input) { var week = weekOfYear(this, 1, 4).week; return input == null ? week : this.add("d", (input - week) * 7); }, weekday : function (input) { var weekday = (this.day() + 7 - this.lang()._week.dow) % 7; return input == null ? weekday : this.add("d", input - weekday); }, isoWeekday : function (input) { // behaves the same as moment#day except // as a getter, returns 7 instead of 0 (1-7 range instead of 0-6) // as a setter, sunday should belong to the previous week. return input == null ? this.day() || 7 : this.day(this.day() % 7 ? input : input - 7); }, get : function (units) { units = normalizeUnits(units); return this[units](); }, set : function (units, value) { units = normalizeUnits(units); if (typeof this[units] === 'function') { this[units](value); } return this; }, // If passed a language key, it will set the language for this // instance. Otherwise, it will return the language configuration // variables for this instance. lang : function (key) { if (key === undefined) { return this._lang; } else { this._lang = getLangDefinition(key); return this; } } }); // helper for adding shortcuts function makeGetterAndSetter(name, key) { moment.fn[name] = moment.fn[name + 's'] = function (input) { var utc = this._isUTC ? 'UTC' : ''; if (input != null) { this._d['set' + utc + key](input); moment.updateOffset(this); return this; } else { return this._d['get' + utc + key](); } }; } // loop through and add shortcuts (Month, Date, Hours, Minutes, Seconds, Milliseconds) for (i = 0; i < proxyGettersAndSetters.length; i ++) { makeGetterAndSetter(proxyGettersAndSetters[i].toLowerCase().replace(/s$/, ''), proxyGettersAndSetters[i]); } // add shortcut for year (uses different syntax than the getter/setter 'year' == 'FullYear') makeGetterAndSetter('year', 'FullYear'); // add plural methods moment.fn.days = moment.fn.day; moment.fn.months = moment.fn.month; moment.fn.weeks = moment.fn.week; moment.fn.isoWeeks = moment.fn.isoWeek; // add aliased format methods moment.fn.toJSON = moment.fn.toISOString; /************************************ Duration Prototype ************************************/ extend(moment.duration.fn = Duration.prototype, { _bubble : function () { var milliseconds = this._milliseconds, days = this._days, months = this._months, data = this._data, seconds, minutes, hours, years; // The following code bubbles up values, see the tests for // examples of what that means. data.milliseconds = milliseconds % 1000; seconds = absRound(milliseconds / 1000); data.seconds = seconds % 60; minutes = absRound(seconds / 60); data.minutes = minutes % 60; hours = absRound(minutes / 60); data.hours = hours % 24; days += absRound(hours / 24); data.days = days % 30; months += absRound(days / 30); data.months = months % 12; years = absRound(months / 12); data.years = years; }, weeks : function () { return absRound(this.days() / 7); }, valueOf : function () { return this._milliseconds + this._days * 864e5 + (this._months % 12) * 2592e6 + toInt(this._months / 12) * 31536e6; }, humanize : function (withSuffix) { var difference = +this, output = relativeTime(difference, !withSuffix, this.lang()); if (withSuffix) { output = this.lang().pastFuture(difference, output); } return this.lang().postformat(output); }, add : function (input, val) { // supports only 2.0-style add(1, 's') or add(moment) var dur = moment.duration(input, val); this._milliseconds += dur._milliseconds; this._days += dur._days; this._months += dur._months; this._bubble(); return this; }, subtract : function (input, val) { var dur = moment.duration(input, val); this._milliseconds -= dur._milliseconds; this._days -= dur._days; this._months -= dur._months; this._bubble(); return this; }, get : function (units) { units = normalizeUnits(units); return this[units.toLowerCase() + 's'](); }, as : function (units) { units = normalizeUnits(units); return this['as' + units.charAt(0).toUpperCase() + units.slice(1) + 's'](); }, lang : moment.fn.lang, toIsoString : function () { // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js var years = Math.abs(this.years()), months = Math.abs(this.months()), days = Math.abs(this.days()), hours = Math.abs(this.hours()), minutes = Math.abs(this.minutes()), seconds = Math.abs(this.seconds() + this.milliseconds() / 1000); if (!this.asSeconds()) { // this is the same as C#'s (Noda) and python (isodate)... // but not other JS (goog.date) return 'P0D'; } return (this.asSeconds() < 0 ? '-' : '') + 'P' + (years ? years + 'Y' : '') + (months ? months + 'M' : '') + (days ? days + 'D' : '') + ((hours || minutes || seconds) ? 'T' : '') + (hours ? hours + 'H' : '') + (minutes ? minutes + 'M' : '') + (seconds ? seconds + 'S' : ''); } }); function makeDurationGetter(name) { moment.duration.fn[name] = function () { return this._data[name]; }; } function makeDurationAsGetter(name, factor) { moment.duration.fn['as' + name] = function () { return +this / factor; }; } for (i in unitMillisecondFactors) { if (unitMillisecondFactors.hasOwnProperty(i)) { makeDurationAsGetter(i, unitMillisecondFactors[i]); makeDurationGetter(i.toLowerCase()); } } makeDurationAsGetter('Weeks', 6048e5); moment.duration.fn.asMonths = function () { return (+this - this.years() * 31536e6) / 2592e6 + this.years() * 12; }; /************************************ Default Lang ************************************/ // Set default language, other languages will inherit from English. moment.lang('en', { ordinal : function (number) { var b = number % 10, output = (toInt(number % 100 / 10) === 1) ? 'th' : (b === 1) ? 'st' : (b === 2) ? 'nd' : (b === 3) ? 'rd' : 'th'; return number + output; } }); /* EMBED_LANGUAGES */ /************************************ Exposing Moment ************************************/ function makeGlobal(deprecate) { var warned = false, local_moment = moment; /*global ender:false */ if (typeof ender !== 'undefined') { return; } // here, `this` means `window` in the browser, or `global` on the server // add `moment` as a global object via a string identifier, // for Closure Compiler "advanced" mode if (deprecate) { global.moment = function () { if (!warned && console && console.warn) { warned = true; console.warn( "Accessing Moment through the global scope is " + "deprecated, and will be removed in an upcoming " + "release."); } return local_moment.apply(null, arguments); }; extend(global.moment, local_moment); } else { global['moment'] = moment; } } // CommonJS module is defined if (hasModule) { module.exports = moment; makeGlobal(true); } else if (typeof define === "function" && define.amd) { define("moment", function (require, exports, module) { if (module.config && module.config() && module.config().noGlobal !== true) { // If user provided noGlobal, he is aware of global makeGlobal(module.config().noGlobal === undefined); } return moment; }); } else { makeGlobal(); } }).call(this); ;/*jslint indent: 2, maxlen: 80, sloppy: true */ var query_class_dict = {}; ;/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*global parseStringToObject: true, emptyFunction: true, sortOn: true, limit: true, select: true, window, stringEscapeRegexpCharacters: true, deepClone, RSVP*/ /** * The query to use to filter a list of objects. * This is an abstract class. * * @class Query * @constructor */ function Query() { /** * Called before parsing the query. Must be overridden! * * @method onParseStart * @param {Object} object The object shared in the parse process * @param {Object} option Some option gave in parse() */ this.onParseStart = emptyFunction; /** * Called when parsing a simple query. Must be overridden! * * @method onParseSimpleQuery * @param {Object} object The object shared in the parse process * @param {Object} option Some option gave in parse() */ this.onParseSimpleQuery = emptyFunction; /** * Called when parsing a complex query. Must be overridden! * * @method onParseComplexQuery * @param {Object} object The object shared in the parse process * @param {Object} option Some option gave in parse() */ this.onParseComplexQuery = emptyFunction; /** * Called after parsing the query. Must be overridden! * * @method onParseEnd * @param {Object} object The object shared in the parse process * @param {Object} option Some option gave in parse() */ this.onParseEnd = emptyFunction; } /** * Filter the item list with matching item only * * @method exec * @param {Array} item_list The list of object * @param {Object} [option] Some operation option * @param {Array} [option.select_list] A object keys to retrieve * @param {Array} [option.sort_on] Couples of object keys and "ascending" * or "descending" * @param {Array} [option.limit] Couple of integer, first is an index and * second is the length. */ Query.prototype.exec = function (item_list, option) { var i, promises = []; if (!Array.isArray(item_list)) { throw new TypeError("Query().exec(): Argument 1 is not of type 'array'"); } if (option === undefined) { option = {}; } if (typeof option !== 'object') { throw new TypeError("Query().exec(): " + "Optional argument 2 is not of type 'object'"); } for (i = 0; i < item_list.length; i += 1) { if (!item_list[i]) { promises.push(RSVP.resolve(false)); } else { promises.push(this.match(item_list[i])); } } return new RSVP.Queue() .push(function () { return RSVP.all(promises); }) .push(function (answers) { var j; for (j = answers.length - 1; j >= 0; j -= 1) { if (!answers[j]) { item_list.splice(j, 1); } } if (option.sort_on) { return sortOn(option.sort_on, item_list); } }) .push(function () { if (option.limit) { return limit(option.limit, item_list); } }) .push(function () { return select(option.select_list || [], item_list); }) .push(function () { return item_list; }); }; /** * Test if an item matches this query * * @method match * @param {Object} item The object to test * @return {Boolean} true if match, false otherwise */ Query.prototype.match = function () { return RSVP.resolve(true); }; /** * Browse the Query in deep calling parser method in each step. * * `onParseStart` is called first, on end `onParseEnd` is called. * It starts from the simple queries at the bottom of the tree calling the * parser method `onParseSimpleQuery`, and go up calling the * `onParseComplexQuery` method. * * @method parse * @param {Object} option Any options you want (except 'parsed') * @return {Any} The parse result */ Query.prototype.parse = function (option) { var that = this, object; /** * The recursive parser. * * @param {Object} object The object shared in the parse process * @param {Object} options Some options usable in the parseMethods * @return {Any} The parser result */ function recParse(object, option) { var query = object.parsed, queue = new RSVP.Queue(), i; function enqueue(j) { queue .push(function () { object.parsed = query.query_list[j]; return recParse(object, option); }) .push(function () { query.query_list[j] = object.parsed; }); } if (query.type === "complex") { for (i = 0; i < query.query_list.length; i += 1) { enqueue(i); } return queue .push(function () { object.parsed = query; return that.onParseComplexQuery(object, option); }); } if (query.type === "simple") { return that.onParseSimpleQuery(object, option); } } object = { parsed: JSON.parse(JSON.stringify(that.serialized())) }; return new RSVP.Queue() .push(function () { return that.onParseStart(object, option); }) .push(function () { return recParse(object, option); }) .push(function () { return that.onParseEnd(object, option); }) .push(function () { return object.parsed; }); }; /** * Convert this query to a parsable string. * * @method toString * @return {String} The string version of this query */ Query.prototype.toString = function () { return ""; }; /** * Convert this query to an jsonable object in order to be remake thanks to * QueryFactory class. * * @method serialized * @return {Object} The jsonable object */ Query.prototype.serialized = function () { return undefined; }; window.Query = Query; ;/** * Parse a text request to a json query object tree * * @param {String} string The string to parse * @return {Object} The json query tree */ function parseStringToObject(string) { ; /* Default template driver for JS/CC generated parsers running as browser-based JavaScript/ECMAScript applications. WARNING: This parser template will not run as console and has lesser features for debugging than the console derivates for the various JavaScript platforms. Features: - Parser trace messages - Integrated panic-mode error recovery Written 2007, 2008 by Jan Max Meyer, J.M.K S.F. Software Technologies This is in the public domain. */ var NODEJS__dbg_withtrace = false; var NODEJS__dbg_string = new String(); function __NODEJS_dbg_print( text ) { NODEJS__dbg_string += text + "\n"; } function __NODEJS_lex( info ) { var state = 0; var match = -1; var match_pos = 0; var start = 0; var pos = info.offset + 1; do { pos--; state = 0; match = -2; start = pos; if( info.src.length <= start ) return 19; do { switch( state ) { case 0: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 8 ) || ( info.src.charCodeAt( pos ) >= 10 && info.src.charCodeAt( pos ) <= 31 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || info.src.charCodeAt( pos ) == 59 || ( info.src.charCodeAt( pos ) >= 63 && info.src.charCodeAt( pos ) <= 64 ) || ( info.src.charCodeAt( pos ) >= 66 && info.src.charCodeAt( pos ) <= 77 ) || ( info.src.charCodeAt( pos ) >= 80 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 9 ) state = 2; else if( info.src.charCodeAt( pos ) == 40 ) state = 3; else if( info.src.charCodeAt( pos ) == 41 ) state = 4; else if( info.src.charCodeAt( pos ) == 60 || info.src.charCodeAt( pos ) == 62 ) state = 5; else if( info.src.charCodeAt( pos ) == 33 ) state = 11; else if( info.src.charCodeAt( pos ) == 79 ) state = 12; else if( info.src.charCodeAt( pos ) == 32 ) state = 13; else if( info.src.charCodeAt( pos ) == 61 ) state = 14; else if( info.src.charCodeAt( pos ) == 34 ) state = 15; else if( info.src.charCodeAt( pos ) == 65 ) state = 19; else if( info.src.charCodeAt( pos ) == 78 ) state = 20; else state = -1; break; case 1: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else state = -1; match = 10; match_pos = pos; break; case 2: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else state = -1; match = 1; match_pos = pos; break; case 3: state = -1; match = 3; match_pos = pos; break; case 4: state = -1; match = 4; match_pos = pos; break; case 5: if( info.src.charCodeAt( pos ) == 61 ) state = 14; else state = -1; match = 11; match_pos = pos; break; case 6: state = -1; match = 8; match_pos = pos; break; case 7: state = -1; match = 9; match_pos = pos; break; case 8: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else state = -1; match = 6; match_pos = pos; break; case 9: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else state = -1; match = 5; match_pos = pos; break; case 10: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else state = -1; match = 7; match_pos = pos; break; case 11: if( info.src.charCodeAt( pos ) == 61 ) state = 14; else state = -1; break; case 12: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 81 ) || ( info.src.charCodeAt( pos ) >= 83 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else if( info.src.charCodeAt( pos ) == 82 ) state = 8; else state = -1; match = 10; match_pos = pos; break; case 13: state = -1; match = 1; match_pos = pos; break; case 14: state = -1; match = 11; match_pos = pos; break; case 15: if( info.src.charCodeAt( pos ) == 34 ) state = 7; else if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 33 ) || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 91 ) || ( info.src.charCodeAt( pos ) >= 93 && info.src.charCodeAt( pos ) <= 254 ) ) state = 15; else if( info.src.charCodeAt( pos ) == 92 ) state = 17; else state = -1; break; case 16: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 67 ) || ( info.src.charCodeAt( pos ) >= 69 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else if( info.src.charCodeAt( pos ) == 68 ) state = 9; else state = -1; match = 10; match_pos = pos; break; case 17: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 254 ) ) state = 15; else state = -1; break; case 18: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 83 ) || ( info.src.charCodeAt( pos ) >= 85 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else if( info.src.charCodeAt( pos ) == 84 ) state = 10; else state = -1; match = 10; match_pos = pos; break; case 19: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 77 ) || ( info.src.charCodeAt( pos ) >= 79 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else if( info.src.charCodeAt( pos ) == 78 ) state = 16; else state = -1; match = 10; match_pos = pos; break; case 20: if( ( info.src.charCodeAt( pos ) >= 0 && info.src.charCodeAt( pos ) <= 31 ) || info.src.charCodeAt( pos ) == 33 || ( info.src.charCodeAt( pos ) >= 35 && info.src.charCodeAt( pos ) <= 39 ) || ( info.src.charCodeAt( pos ) >= 42 && info.src.charCodeAt( pos ) <= 57 ) || ( info.src.charCodeAt( pos ) >= 59 && info.src.charCodeAt( pos ) <= 78 ) || ( info.src.charCodeAt( pos ) >= 80 && info.src.charCodeAt( pos ) <= 254 ) ) state = 1; else if( info.src.charCodeAt( pos ) == 58 ) state = 6; else if( info.src.charCodeAt( pos ) == 79 ) state = 18; else state = -1; match = 10; match_pos = pos; break; } pos++; } while( state > -1 ); } while( 1 > -1 && match == 1 ); if( match > -1 ) { info.att = info.src.substr( start, match_pos - start ); info.offset = match_pos; } else { info.att = new String(); match = -1; } return match; } function __NODEJS_parse( src, err_off, err_la ) { var sstack = new Array(); var vstack = new Array(); var err_cnt = 0; var act; var go; var la; var rval; var parseinfo = new Function( "", "var offset; var src; var att;" ); var info = new parseinfo(); /* Pop-Table */ var pop_tab = new Array( new Array( 0/* begin' */, 1 ), new Array( 13/* begin */, 1 ), new Array( 12/* search_text */, 1 ), new Array( 12/* search_text */, 2 ), new Array( 12/* search_text */, 3 ), new Array( 14/* and_expression */, 1 ), new Array( 14/* and_expression */, 3 ), new Array( 15/* boolean_expression */, 2 ), new Array( 15/* boolean_expression */, 1 ), new Array( 16/* expression */, 3 ), new Array( 16/* expression */, 2 ), new Array( 16/* expression */, 1 ), new Array( 17/* value */, 2 ), new Array( 17/* value */, 1 ), new Array( 18/* string */, 1 ), new Array( 18/* string */, 1 ) ); /* Action-Table */ var act_tab = new Array( /* State 0 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ), /* State 1 */ new Array( 19/* "$" */,0 ), /* State 2 */ new Array( 19/* "$" */,-1 ), /* State 3 */ new Array( 6/* "OR" */,14 , 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 , 19/* "$" */,-2 , 4/* "RIGHT_PARENTHESE" */,-2 ), /* State 4 */ new Array( 5/* "AND" */,16 , 19/* "$" */,-5 , 7/* "NOT" */,-5 , 3/* "LEFT_PARENTHESE" */,-5 , 8/* "COLUMN" */,-5 , 11/* "OPERATOR" */,-5 , 10/* "WORD" */,-5 , 9/* "STRING" */,-5 , 6/* "OR" */,-5 , 4/* "RIGHT_PARENTHESE" */,-5 ), /* State 5 */ new Array( 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ), /* State 6 */ new Array( 19/* "$" */,-8 , 7/* "NOT" */,-8 , 3/* "LEFT_PARENTHESE" */,-8 , 8/* "COLUMN" */,-8 , 11/* "OPERATOR" */,-8 , 10/* "WORD" */,-8 , 9/* "STRING" */,-8 , 6/* "OR" */,-8 , 5/* "AND" */,-8 , 4/* "RIGHT_PARENTHESE" */,-8 ), /* State 7 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ), /* State 8 */ new Array( 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ), /* State 9 */ new Array( 19/* "$" */,-11 , 7/* "NOT" */,-11 , 3/* "LEFT_PARENTHESE" */,-11 , 8/* "COLUMN" */,-11 , 11/* "OPERATOR" */,-11 , 10/* "WORD" */,-11 , 9/* "STRING" */,-11 , 6/* "OR" */,-11 , 5/* "AND" */,-11 , 4/* "RIGHT_PARENTHESE" */,-11 ), /* State 10 */ new Array( 10/* "WORD" */,12 , 9/* "STRING" */,13 ), /* State 11 */ new Array( 19/* "$" */,-13 , 7/* "NOT" */,-13 , 3/* "LEFT_PARENTHESE" */,-13 , 8/* "COLUMN" */,-13 , 11/* "OPERATOR" */,-13 , 10/* "WORD" */,-13 , 9/* "STRING" */,-13 , 6/* "OR" */,-13 , 5/* "AND" */,-13 , 4/* "RIGHT_PARENTHESE" */,-13 ), /* State 12 */ new Array( 19/* "$" */,-14 , 7/* "NOT" */,-14 , 3/* "LEFT_PARENTHESE" */,-14 , 8/* "COLUMN" */,-14 , 11/* "OPERATOR" */,-14 , 10/* "WORD" */,-14 , 9/* "STRING" */,-14 , 6/* "OR" */,-14 , 5/* "AND" */,-14 , 4/* "RIGHT_PARENTHESE" */,-14 ), /* State 13 */ new Array( 19/* "$" */,-15 , 7/* "NOT" */,-15 , 3/* "LEFT_PARENTHESE" */,-15 , 8/* "COLUMN" */,-15 , 11/* "OPERATOR" */,-15 , 10/* "WORD" */,-15 , 9/* "STRING" */,-15 , 6/* "OR" */,-15 , 5/* "AND" */,-15 , 4/* "RIGHT_PARENTHESE" */,-15 ), /* State 14 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ), /* State 15 */ new Array( 19/* "$" */,-3 , 4/* "RIGHT_PARENTHESE" */,-3 ), /* State 16 */ new Array( 7/* "NOT" */,5 , 3/* "LEFT_PARENTHESE" */,7 , 8/* "COLUMN" */,8 , 11/* "OPERATOR" */,10 , 10/* "WORD" */,12 , 9/* "STRING" */,13 ), /* State 17 */ new Array( 19/* "$" */,-7 , 7/* "NOT" */,-7 , 3/* "LEFT_PARENTHESE" */,-7 , 8/* "COLUMN" */,-7 , 11/* "OPERATOR" */,-7 , 10/* "WORD" */,-7 , 9/* "STRING" */,-7 , 6/* "OR" */,-7 , 5/* "AND" */,-7 , 4/* "RIGHT_PARENTHESE" */,-7 ), /* State 18 */ new Array( 4/* "RIGHT_PARENTHESE" */,23 ), /* State 19 */ new Array( 19/* "$" */,-10 , 7/* "NOT" */,-10 , 3/* "LEFT_PARENTHESE" */,-10 , 8/* "COLUMN" */,-10 , 11/* "OPERATOR" */,-10 , 10/* "WORD" */,-10 , 9/* "STRING" */,-10 , 6/* "OR" */,-10 , 5/* "AND" */,-10 , 4/* "RIGHT_PARENTHESE" */,-10 ), /* State 20 */ new Array( 19/* "$" */,-12 , 7/* "NOT" */,-12 , 3/* "LEFT_PARENTHESE" */,-12 , 8/* "COLUMN" */,-12 , 11/* "OPERATOR" */,-12 , 10/* "WORD" */,-12 , 9/* "STRING" */,-12 , 6/* "OR" */,-12 , 5/* "AND" */,-12 , 4/* "RIGHT_PARENTHESE" */,-12 ), /* State 21 */ new Array( 19/* "$" */,-4 , 4/* "RIGHT_PARENTHESE" */,-4 ), /* State 22 */ new Array( 19/* "$" */,-6 , 7/* "NOT" */,-6 , 3/* "LEFT_PARENTHESE" */,-6 , 8/* "COLUMN" */,-6 , 11/* "OPERATOR" */,-6 , 10/* "WORD" */,-6 , 9/* "STRING" */,-6 , 6/* "OR" */,-6 , 4/* "RIGHT_PARENTHESE" */,-6 ), /* State 23 */ new Array( 19/* "$" */,-9 , 7/* "NOT" */,-9 , 3/* "LEFT_PARENTHESE" */,-9 , 8/* "COLUMN" */,-9 , 11/* "OPERATOR" */,-9 , 10/* "WORD" */,-9 , 9/* "STRING" */,-9 , 6/* "OR" */,-9 , 5/* "AND" */,-9 , 4/* "RIGHT_PARENTHESE" */,-9 ) ); /* Goto-Table */ var goto_tab = new Array( /* State 0 */ new Array( 13/* begin */,1 , 12/* search_text */,2 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ), /* State 1 */ new Array( ), /* State 2 */ new Array( ), /* State 3 */ new Array( 12/* search_text */,15 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ), /* State 4 */ new Array( ), /* State 5 */ new Array( 16/* expression */,17 , 17/* value */,9 , 18/* string */,11 ), /* State 6 */ new Array( ), /* State 7 */ new Array( 12/* search_text */,18 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ), /* State 8 */ new Array( 16/* expression */,19 , 17/* value */,9 , 18/* string */,11 ), /* State 9 */ new Array( ), /* State 10 */ new Array( 18/* string */,20 ), /* State 11 */ new Array( ), /* State 12 */ new Array( ), /* State 13 */ new Array( ), /* State 14 */ new Array( 12/* search_text */,21 , 14/* and_expression */,3 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ), /* State 15 */ new Array( ), /* State 16 */ new Array( 14/* and_expression */,22 , 15/* boolean_expression */,4 , 16/* expression */,6 , 17/* value */,9 , 18/* string */,11 ), /* State 17 */ new Array( ), /* State 18 */ new Array( ), /* State 19 */ new Array( ), /* State 20 */ new Array( ), /* State 21 */ new Array( ), /* State 22 */ new Array( ), /* State 23 */ new Array( ) ); /* Symbol labels */ var labels = new Array( "begin'" /* Non-terminal symbol */, "WHITESPACE" /* Terminal symbol */, "WHITESPACE" /* Terminal symbol */, "LEFT_PARENTHESE" /* Terminal symbol */, "RIGHT_PARENTHESE" /* Terminal symbol */, "AND" /* Terminal symbol */, "OR" /* Terminal symbol */, "NOT" /* Terminal symbol */, "COLUMN" /* Terminal symbol */, "STRING" /* Terminal symbol */, "WORD" /* Terminal symbol */, "OPERATOR" /* Terminal symbol */, "search_text" /* Non-terminal symbol */, "begin" /* Non-terminal symbol */, "and_expression" /* Non-terminal symbol */, "boolean_expression" /* Non-terminal symbol */, "expression" /* Non-terminal symbol */, "value" /* Non-terminal symbol */, "string" /* Non-terminal symbol */, "$" /* Terminal symbol */ ); info.offset = 0; info.src = src; info.att = new String(); if( !err_off ) err_off = new Array(); if( !err_la ) err_la = new Array(); sstack.push( 0 ); vstack.push( 0 ); la = __NODEJS_lex( info ); while( true ) { act = 25; for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 ) { if( act_tab[sstack[sstack.length-1]][i] == la ) { act = act_tab[sstack[sstack.length-1]][i+1]; break; } } if( NODEJS__dbg_withtrace && sstack.length > 0 ) { __NODEJS_dbg_print( "\nState " + sstack[sstack.length-1] + "\n" + "\tLookahead: " + labels[la] + " (\"" + info.att + "\")\n" + "\tAction: " + act + "\n" + "\tSource: \"" + info.src.substr( info.offset, 30 ) + ( ( info.offset + 30 < info.src.length ) ? "..." : "" ) + "\"\n" + "\tStack: " + sstack.join() + "\n" + "\tValue stack: " + vstack.join() + "\n" ); } //Panic-mode: Try recovery when parse-error occurs! if( act == 25 ) { if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "Error detected: There is no reduce or shift on the symbol " + labels[la] ); err_cnt++; err_off.push( info.offset - info.att.length ); err_la.push( new Array() ); for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 ) err_la[err_la.length-1].push( labels[act_tab[sstack[sstack.length-1]][i]] ); //Remember the original stack! var rsstack = new Array(); var rvstack = new Array(); for( var i = 0; i < sstack.length; i++ ) { rsstack[i] = sstack[i]; rvstack[i] = vstack[i]; } while( act == 25 && la != 19 ) { if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "\tError recovery\n" + "Current lookahead: " + labels[la] + " (" + info.att + ")\n" + "Action: " + act + "\n\n" ); if( la == -1 ) info.offset++; while( act == 25 && sstack.length > 0 ) { sstack.pop(); vstack.pop(); if( sstack.length == 0 ) break; act = 25; for( var i = 0; i < act_tab[sstack[sstack.length-1]].length; i+=2 ) { if( act_tab[sstack[sstack.length-1]][i] == la ) { act = act_tab[sstack[sstack.length-1]][i+1]; break; } } } if( act != 25 ) break; for( var i = 0; i < rsstack.length; i++ ) { sstack.push( rsstack[i] ); vstack.push( rvstack[i] ); } la = __NODEJS_lex( info ); } if( act == 25 ) { if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "\tError recovery failed, terminating parse process..." ); break; } if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "\tError recovery succeeded, continuing" ); } /* if( act == 25 ) break; */ //Shift if( act > 0 ) { if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "Shifting symbol: " + labels[la] + " (" + info.att + ")" ); sstack.push( act ); vstack.push( info.att ); la = __NODEJS_lex( info ); if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "\tNew lookahead symbol: " + labels[la] + " (" + info.att + ")" ); } //Reduce else { act *= -1; if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "Reducing by producution: " + act ); rval = void(0); if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "\tPerforming semantic action..." ); switch( act ) { case 0: { rval = vstack[ vstack.length - 1 ]; } break; case 1: { result = vstack[ vstack.length - 1 ]; } break; case 2: { rval = vstack[ vstack.length - 1 ]; } break; case 3: { rval = mkComplexQuery('OR',[vstack[ vstack.length - 2 ],vstack[ vstack.length - 1 ]]); } break; case 4: { rval = mkComplexQuery('OR',[vstack[ vstack.length - 3 ],vstack[ vstack.length - 1 ]]); } break; case 5: { rval = vstack[ vstack.length - 1 ] ; } break; case 6: { rval = mkComplexQuery('AND',[vstack[ vstack.length - 3 ],vstack[ vstack.length - 1 ]]); } break; case 7: { rval = mkNotQuery(vstack[ vstack.length - 1 ]); } break; case 8: { rval = vstack[ vstack.length - 1 ]; } break; case 9: { rval = vstack[ vstack.length - 2 ]; } break; case 10: { simpleQuerySetKey(vstack[ vstack.length - 1 ],vstack[ vstack.length - 2 ].split(':').slice(0,-1).join(':')); rval = vstack[ vstack.length - 1 ]; } break; case 11: { rval = vstack[ vstack.length - 1 ]; } break; case 12: { vstack[ vstack.length - 1 ].operator = vstack[ vstack.length - 2 ] ; rval = vstack[ vstack.length - 1 ]; } break; case 13: { rval = vstack[ vstack.length - 1 ]; } break; case 14: { rval = mkSimpleQuery('',vstack[ vstack.length - 1 ]); } break; case 15: { rval = mkSimpleQuery('',vstack[ vstack.length - 1 ].split('"').slice(1,-1).join('"')); } break; } if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "\tPopping " + pop_tab[act][1] + " off the stack..." ); for( var i = 0; i < pop_tab[act][1]; i++ ) { sstack.pop(); vstack.pop(); } go = -1; for( var i = 0; i < goto_tab[sstack[sstack.length-1]].length; i+=2 ) { if( goto_tab[sstack[sstack.length-1]][i] == pop_tab[act][0] ) { go = goto_tab[sstack[sstack.length-1]][i+1]; break; } } if( act == 0 ) break; if( NODEJS__dbg_withtrace ) __NODEJS_dbg_print( "\tPushing non-terminal " + labels[ pop_tab[act][0] ] ); sstack.push( go ); vstack.push( rval ); } if( NODEJS__dbg_withtrace ) { alert( NODEJS__dbg_string ); NODEJS__dbg_string = new String(); } } if( NODEJS__dbg_withtrace ) { __NODEJS_dbg_print( "\nParse complete." ); alert( NODEJS__dbg_string ); } return err_cnt; } var arrayExtend = function () { var j, i, newlist = [], list_list = arguments; for (j = 0; j < list_list.length; j += 1) { for (i = 0; i < list_list[j].length; i += 1) { newlist.push(list_list[j][i]); } } return newlist; }, mkSimpleQuery = function (key, value, operator) { var object = {"type": "simple", "key": key, "value": value}; if (operator !== undefined) { object.operator = operator; } return object; }, mkNotQuery = function (query) { if (query.operator === "NOT") { return query.query_list[0]; } return {"type": "complex", "operator": "NOT", "query_list": [query]}; }, mkComplexQuery = function (operator, query_list) { var i, query_list2 = []; for (i = 0; i < query_list.length; i += 1) { if (query_list[i].operator === operator) { query_list2 = arrayExtend(query_list2, query_list[i].query_list); } else { query_list2.push(query_list[i]); } } return {type:"complex",operator:operator,query_list:query_list2}; }, simpleQuerySetKey = function (query, key) { var i; if (query.type === "complex") { for (i = 0; i < query.query_list.length; ++i) { simpleQuerySetKey (query.query_list[i],key); } return true; } if (query.type === "simple" && !query.key) { query.key = key; return true; } return false; }, error_offsets = [], error_lookaheads = [], error_count = 0, result; if ((error_count = __NODEJS_parse(string, error_offsets, error_lookaheads)) > 0) { var i; for (i = 0; i < error_count; i += 1) { throw new Error("Parse error near \"" + string.substr(error_offsets[i]) + "\", expecting \"" + error_lookaheads[i].join() + "\""); } } ; return result; } // parseStringToObject Query.parseStringToObject = parseStringToObject; ;/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*global Query: true, query_class_dict: true, inherits: true, window, QueryFactory, RSVP */ /** * The ComplexQuery inherits from Query, and compares one or several metadata * values. * * @class ComplexQuery * @extends Query * @param {Object} [spec={}] The specifications * @param {String} [spec.operator="AND"] The compare method to use * @param {String} spec.key The metadata key * @param {String} spec.value The value of the metadata to compare */ function ComplexQuery(spec, key_schema) { Query.call(this); /** * Logical operator to use to compare object values * * @attribute operator * @type String * @default "AND" * @optional */ this.operator = spec.operator; /** * The sub Query list which are used to query an item. * * @attribute query_list * @type Array * @default [] * @optional */ this.query_list = spec.query_list || []; /*jslint unparam: true*/ this.query_list = this.query_list.map( // decorate the map to avoid sending the index as key_schema argument function (o, i) { return QueryFactory.create(o, key_schema); } ); /*jslint unparam: false*/ } inherits(ComplexQuery, Query); ComplexQuery.prototype.operator = "AND"; ComplexQuery.prototype.type = "complex"; /** * #crossLink "Query/match:method" */ ComplexQuery.prototype.match = function (item) { var operator = this.operator; if (!(/^(?:AND|OR|NOT)$/i.test(operator))) { operator = "AND"; } return this[operator.toUpperCase()](item); }; /** * #crossLink "Query/toString:method" */ ComplexQuery.prototype.toString = function () { var str_list = [], this_operator = this.operator; if (this.operator === "NOT") { str_list.push("NOT ("); str_list.push(this.query_list[0].toString()); str_list.push(")"); return str_list.join(" "); } this.query_list.forEach(function (query) { str_list.push("("); str_list.push(query.toString()); str_list.push(")"); str_list.push(this_operator); }); str_list.length -= 1; return str_list.join(" "); }; /** * #crossLink "Query/serialized:method" */ ComplexQuery.prototype.serialized = function () { var s = { "type": "complex", "operator": this.operator, "query_list": [] }; this.query_list.forEach(function (query) { s.query_list.push( typeof query.toJSON === "function" ? query.toJSON() : query ); }); return s; }; ComplexQuery.prototype.toJSON = ComplexQuery.prototype.serialized; /** * Comparison operator, test if all sub queries match the * item value * * @method AND * @param {Object} item The item to match * @return {Boolean} true if all match, false otherwise */ ComplexQuery.prototype.AND = function (item) { var queue = new RSVP.Queue(), context = this, i = 0; function executeNextIfNotFalse(result) { if (result === false) { // No need to evaluate the other elements, as one is false return result; } if (context.query_list.length === i) { // No new element to loop on return true; } queue .push(function () { var sub_result = context.query_list[i].match(item); i += 1; return sub_result; }) .push(executeNextIfNotFalse); } executeNextIfNotFalse(true); return queue; }; /** * Comparison operator, test if one of the sub queries matches the * item value * * @method OR * @param {Object} item The item to match * @return {Boolean} true if one match, false otherwise */ ComplexQuery.prototype.OR = function (item) { var queue = new RSVP.Queue(), context = this, i = 0; function executeNextIfNotTrue(result) { if (result === true) { // No need to evaluate the other elements, as one is true return result; } if (context.query_list.length === i) { // No new element to loop on return false; } queue .push(function () { var sub_result = context.query_list[i].match(item); i += 1; return sub_result; }) .push(executeNextIfNotTrue); } executeNextIfNotTrue(false); return queue; }; /** * Comparison operator, test if the sub query does not match the * item value * * @method NOT * @param {Object} item The item to match * @return {Boolean} true if one match, false otherwise */ ComplexQuery.prototype.NOT = function (item) { return new RSVP.Queue() .push(function () { return this.query_list[0].match(item); }) .push(function (answer) { return !answer; }); }; query_class_dict.complex = ComplexQuery; window.ComplexQuery = ComplexQuery; ;/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*global window, ComplexQuery, SimpleQuery, Query, parseStringToObject, query_class_dict */ /** * Provides static methods to create Query object * * @class QueryFactory */ function QueryFactory() { return; } /** * Creates Query object from a search text string or a serialized version * of a Query. * * @method create * @static * @param {Object,String} object The search text or the serialized version * of a Query * @return {Query} A Query object */ QueryFactory.create = function (object, key_schema) { if (object === "") { return new Query(); } if (typeof object === "string") { object = parseStringToObject(object); } if (typeof (object || {}).type === "string" && query_class_dict[object.type]) { return new query_class_dict[object.type](object, key_schema); } throw new TypeError("QueryFactory.create(): " + "Argument 1 is not a search text or a parsable object"); }; window.QueryFactory = QueryFactory; ;/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*global Query, exports */ function objectToSearchText(query) { var str_list = []; if (query.type === "complex") { str_list.push("("); (query.query_list || []).forEach(function (sub_query) { str_list.push(objectToSearchText(sub_query)); str_list.push(query.operator); }); str_list.length -= 1; str_list.push(")"); return str_list.join(" "); } if (query.type === "simple") { return (query.key ? query.key + ": " : "") + (query.operator || "") + ' "' + query.value + '"'; } throw new TypeError("This object is not a query"); } Query.objectToSearchText = objectToSearchText; ;/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*global Query, inherits, query_class_dict, window, searchTextToRegExp, RSVP */ var checkKeySchema = function (key_schema) { var prop; if (key_schema !== undefined) { if (typeof key_schema !== 'object') { throw new TypeError("SimpleQuery().create(): " + "key_schema is not of type 'object'"); } // key_set is mandatory if (key_schema.key_set === undefined) { throw new TypeError("SimpleQuery().create(): " + "key_schema has no 'key_set' property"); } for (prop in key_schema) { if (key_schema.hasOwnProperty(prop)) { switch (prop) { case 'key_set': case 'cast_lookup': case 'match_lookup': break; default: throw new TypeError("SimpleQuery().create(): " + "key_schema has unknown property '" + prop + "'"); } } } } }; /** * The SimpleQuery inherits from Query, and compares one metadata value * * @class SimpleQuery * @extends Query * @param {Object} [spec={}] The specifications * @param {String} [spec.operator="="] The compare method to use * @param {String} spec.key The metadata key * @param {String} spec.value The value of the metadata to compare */ function SimpleQuery(spec, key_schema) { Query.call(this); checkKeySchema(key_schema); this._key_schema = key_schema || {}; /** * Operator to use to compare object values * * @attribute operator * @type String * @optional */ this.operator = spec.operator; /** * Key of the object which refers to the value to compare * * @attribute key * @type String */ this.key = spec.key; /** * Value is used to do the comparison with the object value * * @attribute value * @type String */ this.value = spec.value; } inherits(SimpleQuery, Query); SimpleQuery.prototype.type = "simple"; var checkKey = function (key) { var prop; if (key.read_from === undefined) { throw new TypeError("Custom key is missing the read_from property"); } for (prop in key) { if (key.hasOwnProperty(prop)) { switch (prop) { case 'read_from': case 'cast_to': case 'equal_match': break; default: throw new TypeError("Custom key has unknown property '" + prop + "'"); } } } }; /** * #crossLink "Query/match:method" */ SimpleQuery.prototype.match = function (item) { var object_value = null, equal_match = null, cast_to = null, matchMethod = null, operator = this.operator, value = null, key = this.key; /*jslint regexp: true */ if (!(/^(?:!?=|<=?|>=?)$/i.test(operator))) { // `operator` is not correct, we have to change it to "like" or "=" if (/%/.test(this.value)) { // `value` contains a non escaped `%` operator = "like"; } else { // `value` does not contain non escaped `%` operator = "="; } } matchMethod = this[operator]; if (this._key_schema.key_set && this._key_schema.key_set[key] !== undefined) { key = this._key_schema.key_set[key]; } if (typeof key === 'object') { checkKey(key); object_value = item[key.read_from]; equal_match = key.equal_match; // equal_match can be a string if (typeof equal_match === 'string') { // XXX raise error if equal_match not in match_lookup equal_match = this._key_schema.match_lookup[equal_match]; } // equal_match overrides the default '=' operator if (equal_match !== undefined) { matchMethod = (operator === "=" || operator === "like" ? equal_match : matchMethod); } value = this.value; cast_to = key.cast_to; if (cast_to) { // cast_to can be a string if (typeof cast_to === 'string') { // XXX raise error if cast_to not in cast_lookup cast_to = this._key_schema.cast_lookup[cast_to]; } try { value = cast_to(value); } catch (e) { value = undefined; } try { object_value = cast_to(object_value); } catch (e) { object_value = undefined; } } } else { object_value = item[key]; value = this.value; } if (object_value === undefined || value === undefined) { return RSVP.resolve(false); } return matchMethod(object_value, value); }; /** * #crossLink "Query/toString:method" */ SimpleQuery.prototype.toString = function () { return (this.key ? this.key + ":" : "") + (this.operator ? " " + this.operator : "") + ' "' + this.value + '"'; }; /** * #crossLink "Query/serialized:method" */ SimpleQuery.prototype.serialized = function () { var object = { "type": "simple", "key": this.key, "value": this.value }; if (this.operator !== undefined) { object.operator = this.operator; } return object; }; SimpleQuery.prototype.toJSON = SimpleQuery.prototype.serialized; /** * Comparison operator, test if this query value matches the item value * * @method = * @param {String} object_value The value to compare * @param {String} comparison_value The comparison value * @return {Boolean} true if match, false otherwise */ SimpleQuery.prototype["="] = function (object_value, comparison_value) { var value, i; if (!Array.isArray(object_value)) { object_value = [object_value]; } for (i = 0; i < object_value.length; i += 1) { value = object_value[i]; if (typeof value === 'object' && value.hasOwnProperty('content')) { value = value.content; } if (typeof value.cmp === "function") { return RSVP.resolve(value.cmp(comparison_value) === 0); } if ( searchTextToRegExp(comparison_value.toString(), false). test(value.toString()) ) { return RSVP.resolve(true); } } return RSVP.resolve(false); }; /** * Comparison operator, test if this query value matches the item value * * @method like * @param {String} object_value The value to compare * @param {String} comparison_value The comparison value * @return {Boolean} true if match, false otherwise */ SimpleQuery.prototype.like = function (object_value, comparison_value) { var value, i; if (!Array.isArray(object_value)) { object_value = [object_value]; } for (i = 0; i < object_value.length; i += 1) { value = object_value[i]; if (typeof value === 'object' && value.hasOwnProperty('content')) { value = value.content; } if (typeof value.cmp === "function") { return RSVP.resolve(value.cmp(comparison_value) === 0); } if ( searchTextToRegExp(comparison_value.toString()).test(value.toString()) ) { return RSVP.resolve(true); } } return RSVP.resolve(false); }; /** * Comparison operator, test if this query value does not match the item value * * @method != * @param {String} object_value The value to compare * @param {String} comparison_value The comparison value * @return {Boolean} true if not match, false otherwise */ SimpleQuery.prototype["!="] = function (object_value, comparison_value) { var value, i; if (!Array.isArray(object_value)) { object_value = [object_value]; } for (i = 0; i < object_value.length; i += 1) { value = object_value[i]; if (typeof value === 'object' && value.hasOwnProperty('content')) { value = value.content; } if (typeof value.cmp === "function") { return RSVP.resolve(value.cmp(comparison_value) !== 0); } if ( searchTextToRegExp(comparison_value.toString(), false). test(value.toString()) ) { return RSVP.resolve(false); } } return RSVP.resolve(true); }; /** * Comparison operator, test if this query value is lower than the item value * * @method < * @param {Number, String} object_value The value to compare * @param {Number, String} comparison_value The comparison value * @return {Boolean} true if lower, false otherwise */ SimpleQuery.prototype["<"] = function (object_value, comparison_value) { var value; if (!Array.isArray(object_value)) { object_value = [object_value]; } value = object_value[0]; if (typeof value === 'object' && value.hasOwnProperty('content')) { value = value.content; } if (typeof value.cmp === "function") { return RSVP.resolve(value.cmp(comparison_value) < 0); } return RSVP.resolve(value < comparison_value); }; /** * Comparison operator, test if this query value is equal or lower than the * item value * * @method <= * @param {Number, String} object_value The value to compare * @param {Number, String} comparison_value The comparison value * @return {Boolean} true if equal or lower, false otherwise */ SimpleQuery.prototype["<="] = function (object_value, comparison_value) { var value; if (!Array.isArray(object_value)) { object_value = [object_value]; } value = object_value[0]; if (typeof value === 'object' && value.hasOwnProperty('content')) { value = value.content; } if (typeof value.cmp === "function") { return RSVP.resolve(value.cmp(comparison_value) <= 0); } return RSVP.resolve(value <= comparison_value); }; /** * Comparison operator, test if this query value is greater than the item * value * * @method > * @param {Number, String} object_value The value to compare * @param {Number, String} comparison_value The comparison value * @return {Boolean} true if greater, false otherwise */ SimpleQuery.prototype[">"] = function (object_value, comparison_value) { var value; if (!Array.isArray(object_value)) { object_value = [object_value]; } value = object_value[0]; if (typeof value === 'object' && value.hasOwnProperty('content')) { value = value.content; } if (typeof value.cmp === "function") { return RSVP.resolve(value.cmp(comparison_value) > 0); } return RSVP.resolve(value > comparison_value); }; /** * Comparison operator, test if this query value is equal or greater than the * item value * * @method >= * @param {Number, String} object_value The value to compare * @param {Number, String} comparison_value The comparison value * @return {Boolean} true if equal or greater, false otherwise */ SimpleQuery.prototype[">="] = function (object_value, comparison_value) { var value; if (!Array.isArray(object_value)) { object_value = [object_value]; } value = object_value[0]; if (typeof value === 'object' && value.hasOwnProperty('content')) { value = value.content; } if (typeof value.cmp === "function") { return RSVP.resolve(value.cmp(comparison_value) >= 0); } return RSVP.resolve(value >= comparison_value); }; query_class_dict.simple = SimpleQuery; window.SimpleQuery = SimpleQuery; ;/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */ /*global Query, RSVP, deepClone */ /** * Escapes regexp special chars from a string. * * @param {String} string The string to escape * @return {String} The escaped string */ function stringEscapeRegexpCharacters(string) { if (typeof string === "string") { return string.replace(/([\\\.\$\[\]\(\)\{\}\^\?\*\+\-])/g, "\\$1"); } throw new TypeError("Query.stringEscapeRegexpCharacters(): " + "Argument no 1 is not of type 'string'"); } Query.stringEscapeRegexpCharacters = stringEscapeRegexpCharacters; /** * Convert metadata values to array of strings. ex: * * "a" -> ["a"], * {"content": "a"} -> ["a"] * * @param {Any} value The metadata value * @return {Array} The value in string array format */ function metadataValueToStringArray(value) { var i, new_value = []; if (value === undefined) { return undefined; } if (!Array.isArray(value)) { value = [value]; } for (i = 0; i < value.length; i += 1) { if (typeof value[i] === 'object') { new_value[i] = value[i].content; } else { new_value[i] = value[i]; } } return new_value; } /** * A sort function to sort items by key * * @param {String} key The key to sort on * @param {String} [way="ascending"] 'ascending' or 'descending' * @return {Function} The sort function */ function sortFunction(key, way) { var result; if (way === 'descending') { result = 1; } else if (way === 'ascending') { result = -1; } else { throw new TypeError("Query.sortFunction(): " + "Argument 2 must be 'ascending' or 'descending'"); } return function (a, b) { // this comparison is 5 times faster than json comparison var i, l; a = metadataValueToStringArray(a[key]) || []; b = metadataValueToStringArray(b[key]) || []; l = a.length > b.length ? a.length : b.length; for (i = 0; i < l; i += 1) { if (a[i] === undefined) { return result; } if (b[i] === undefined) { return -result; } if (a[i] > b[i]) { return -result; } if (a[i] < b[i]) { return result; } } return 0; }; } /** * Inherits the prototype methods from one constructor into another. The * prototype of `constructor` will be set to a new object created from * `superConstructor`. * * @param {Function} constructor The constructor which inherits the super one * @param {Function} superConstructor The super constructor */ function inherits(constructor, superConstructor) { constructor.super_ = superConstructor; constructor.prototype = Object.create(superConstructor.prototype, { "constructor": { "configurable": true, "enumerable": false, "writable": true, "value": constructor } }); } /** * Does nothing */ function emptyFunction() { return; } /** * Filter a list of items, modifying them to select only wanted keys. If * `clone` is true, then the method will act on a cloned list. * * @param {Array} select_option Key list to keep * @param {Array} list The item list to filter * @param {Boolean} [clone=false] If true, modifies a clone of the list * @return {Array} The filtered list */ function select(select_option, list, clone) { var i, j, new_item; if (!Array.isArray(select_option)) { throw new TypeError("jioquery.select(): " + "Argument 1 is not of type Array"); } if (!Array.isArray(list)) { throw new TypeError("jioquery.select(): " + "Argument 2 is not of type Array"); } if (clone === true) { list = deepClone(list); } for (i = 0; i < list.length; i += 1) { new_item = {}; for (j = 0; j < select_option.length; j += 1) { if (list[i].hasOwnProperty([select_option[j]])) { new_item[select_option[j]] = list[i][select_option[j]]; } } for (j in new_item) { if (new_item.hasOwnProperty(j)) { list[i] = new_item; break; } } } return list; } Query.select = select; /** * Sort a list of items, according to keys and directions. If `clone` is true, * then the method will act on a cloned list. * * @param {Array} sort_on_option List of couples [key, direction] * @param {Array} list The item list to sort * @param {Boolean} [clone=false] If true, modifies a clone of the list * @return {Array} The filtered list */ function sortOn(sort_on_option, list, clone) { var sort_index; if (!Array.isArray(sort_on_option)) { throw new TypeError("jioquery.sortOn(): " + "Argument 1 is not of type 'array'"); } if (clone) { list = deepClone(list); } for (sort_index = sort_on_option.length - 1; sort_index >= 0; sort_index -= 1) { list.sort(sortFunction( sort_on_option[sort_index][0], sort_on_option[sort_index][1] )); } return list; } Query.sortOn = sortOn; /** * Limit a list of items, according to index and length. If `clone` is true, * then the method will act on a cloned list. * * @param {Array} limit_option A couple [from, length] * @param {Array} list The item list to limit * @param {Boolean} [clone=false] If true, modifies a clone of the list * @return {Array} The filtered list */ function limit(limit_option, list, clone) { if (!Array.isArray(limit_option)) { throw new TypeError("jioquery.limit(): " + "Argument 1 is not of type 'array'"); } if (!Array.isArray(list)) { throw new TypeError("jioquery.limit(): " + "Argument 2 is not of type 'array'"); } if (clone) { list = deepClone(list); } list.splice(0, limit_option[0]); if (limit_option[1]) { list.splice(limit_option[1]); } return list; } Query.limit = limit; /** * Convert a search text to a regexp. * * @param {String} string The string to convert * @param {Boolean} [use_wildcard_character=true] Use wildcard "%" and "_" * @return {RegExp} The search text regexp */ function searchTextToRegExp(string, use_wildcard_characters) { if (typeof string !== 'string') { throw new TypeError("jioquery.searchTextToRegExp(): " + "Argument 1 is not of type 'string'"); } if (use_wildcard_characters === false) { return new RegExp("^" + stringEscapeRegexpCharacters(string) + "$"); } return new RegExp("^" + stringEscapeRegexpCharacters(string).replace( /%/g, ".*" ).replace( /_/g, "." ) + "$"); } Query.searchTextToRegExp = searchTextToRegExp; ;/*global window, moment */ /*jslint nomen: true, maxlen: 200*/ (function (window, moment) { "use strict"; // /** // * Add a secured (write permission denied) property to an object. // * // * @param {Object} object The object to fill // * @param {String} key The object key where to store the property // * @param {Any} value The value to store // */ // function _export(key, value) { // Object.defineProperty(to_export, key, { // "configurable": false, // "enumerable": true, // "writable": false, // "value": value // }); // } var YEAR = 'year', MONTH = 'month', DAY = 'day', HOUR = 'hour', MIN = 'minute', SEC = 'second', MSEC = 'millisecond', precision_grade = { 'year': 0, 'month': 1, 'day': 2, 'hour': 3, 'minute': 4, 'second': 5, 'millisecond': 6 }, lesserPrecision = function (p1, p2) { return (precision_grade[p1] < precision_grade[p2]) ? p1 : p2; }, JIODate; JIODate = function (str) { // in case of forgotten 'new' if (!(this instanceof JIODate)) { return new JIODate(str); } if (str instanceof JIODate) { this.mom = str.mom.clone(); this._precision = str._precision; return; } if (str === undefined) { this.mom = moment(); this.setPrecision(MSEC); return; } this.mom = null; this._str = str; // http://www.w3.org/TR/NOTE-datetime // http://dotat.at/tmp/ISO_8601-2004_E.pdf // XXX these regexps fail to detect many invalid dates. if (str.match(/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+\-][0-2]\d:[0-5]\d|Z)/) || str.match(/\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d\.\d\d\d/)) { // ISO, milliseconds this.mom = moment(str); this.setPrecision(MSEC); } else if (str.match(/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+\-][0-2]\d:[0-5]\d|Z)/) || str.match(/\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d/)) { // ISO, seconds this.mom = moment(str); this.setPrecision(SEC); } else if (str.match(/\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+\-][0-2]\d:[0-5]\d|Z)/) || str.match(/\d\d\d\d-\d\d-\d\d \d\d:\d\d/)) { // ISO, minutes this.mom = moment(str); this.setPrecision(MIN); } else if (str.match(/\d\d\d\d-\d\d-\d\d \d\d/)) { this.mom = moment(str); this.setPrecision(HOUR); } else if (str.match(/\d\d\d\d-\d\d-\d\d/)) { this.mom = moment(str); this.setPrecision(DAY); } else if (str.match(/\d\d\d\d-\d\d/)) { this.mom = moment(str); this.setPrecision(MONTH); } else if (str.match(/\d\d\d\d/)) { this.mom = moment(str); this.setPrecision(YEAR); } if (!this.mom) { throw new Error("Cannot parse: " + str); } }; JIODate.prototype.setPrecision = function (prec) { this._precision = prec; }; JIODate.prototype.getPrecision = function () { return this._precision; }; JIODate.prototype.cmp = function (other) { var m1 = this.mom, m2 = other.mom, p = lesserPrecision(this._precision, other._precision); return m1.isBefore(m2, p) ? -1 : (m1.isSame(m2, p) ? 0 : +1); }; JIODate.prototype.toPrecisionString = function (precision) { var fmt; precision = precision || this._precision; fmt = { 'millisecond': 'YYYY-MM-DD HH:mm:ss.SSS', 'second': 'YYYY-MM-DD HH:mm:ss', 'minute': 'YYYY-MM-DD HH:mm', 'hour': 'YYYY-MM-DD HH', 'day': 'YYYY-MM-DD', 'month': 'YYYY-MM', 'year': 'YYYY' }[precision]; if (!fmt) { throw new TypeError("Unsupported precision value '" + precision + "'"); } return this.mom.format(fmt); }; JIODate.prototype.toString = function () { return this._str; }; // _export('JIODate', JIODate); // // _export('YEAR', YEAR); // _export('MONTH', MONTH); // _export('DAY', DAY); // _export('HOUR', HOUR); // _export('MIN', MIN); // _export('SEC', SEC); // _export('MSEC', MSEC); window.jiodate = { JIODate: JIODate, YEAR: YEAR, MONTH: MONTH, DAY: DAY, HOUR: HOUR, MIN: MIN, SEC: SEC, MSEC: MSEC }; }(window, moment)); ;/*jslint maxlen: 200*/ /*global window, RSVP, Blob, XMLHttpRequest, QueryFactory, Query, console, FileReader */ (function (window, RSVP, Blob, QueryFactory, Query, FileReader) { "use strict"; var util = {}, jIO; function jIOError(message, status_code) { if ((message !== undefined) && (typeof message !== "string")) { throw new TypeError('You must pass a string.'); } this.message = message || "Default Message"; this.status_code = status_code || 500; } jIOError.prototype = new Error(); jIOError.prototype.constructor = jIOError; util.jIOError = jIOError; /** * Send request with XHR and return a promise. xhr.onload: The promise is * resolved when the status code is lower than 400 with the xhr object as first * parameter. xhr.onerror: reject with xhr object as first * parameter. xhr.onprogress: notifies the xhr object. * * @param {Object} param The parameters * @param {String} [param.type="GET"] The request method * @param {String} [param.dataType=""] The data type to retrieve * @param {String} param.url The url * @param {Any} [param.data] The data to send * @param {Function} [param.beforeSend] A function called just before the send * request. The first parameter of this function is the XHR object. * @return {Promise} The promise */ function ajax(param) { var xhr = new XMLHttpRequest(); return new RSVP.Promise(function (resolve, reject, notify) { var k; xhr.open(param.type || "GET", param.url, true); xhr.responseType = param.dataType || ""; if (typeof param.headers === 'object' && param.headers !== null) { for (k in param.headers) { if (param.headers.hasOwnProperty(k)) { xhr.setRequestHeader(k, param.headers[k]); } } } xhr.addEventListener("load", function (e) { if (e.target.status >= 400) { return reject(e); } resolve(e); }); xhr.addEventListener("error", reject); xhr.addEventListener("progress", notify); if (typeof param.xhrFields === 'object' && param.xhrFields !== null) { for (k in param.xhrFields) { if (param.xhrFields.hasOwnProperty(k)) { xhr[k] = param.xhrFields[k]; } } } if (typeof param.beforeSend === 'function') { param.beforeSend(xhr); } xhr.send(param.data); }, function () { xhr.abort(); }); } util.ajax = ajax; /** * Clones all native object in deep. Managed types: Object, Array, String, * Number, Boolean, Function, null. * * It can also clone object which are serializable, like Date. * * To make a class serializable, you need to implement the `toJSON` function * which returns a JSON representation of the object. The returned value is used * as first parameter of the object constructor. * * @param {A} object The object to clone * @return {A} The cloned object */ function deepClone(object) { var i, cloned; if (Array.isArray(object)) { cloned = []; for (i = 0; i < object.length; i += 1) { cloned[i] = deepClone(object[i]); } return cloned; } if (object === null) { return null; } if (typeof object === 'object') { if (Object.getPrototypeOf(object) === Object.prototype) { cloned = {}; for (i in object) { if (object.hasOwnProperty(i)) { cloned[i] = deepClone(object[i]); } } return cloned; } if (object instanceof Date) { // XXX this block is to enable phantomjs and browsers compatibility with // Date.prototype.toJSON when it is an invalid date. In phantomjs, it // returns `"Invalid Date"` but in browsers it returns `null`. In // browsers, giving `null` as parameter to `new Date()` doesn't return an // invalid date. // Cloning a date with `return new Date(object)` has problems on Firefox. // I don't know why... (Tested on Firefox 23) if (isFinite(object.getTime())) { return new Date(object.toJSON()); } return new Date("Invalid Date"); } // clone serializable objects if (typeof object.toJSON === 'function') { return new (Object.getPrototypeOf(object).constructor)(object.toJSON()); } // cannot clone return object; } return object; } util.deepClone = deepClone; /** * An Universal Unique ID generator * * @return {String} The new UUID. */ function generateUuid() { function S4() { return ('0000' + Math.floor( Math.random() * 0x10000 /* 65536 */ ).toString(16)).slice(-4); } return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4(); } util.generateUuid = generateUuid; function readBlobAsText(blob) { var fr = new FileReader(); return new RSVP.Promise(function (resolve, reject, notify) { fr.addEventListener("load", resolve); fr.addEventListener("error", reject); fr.addEventListener("progress", notify); fr.readAsText(blob); }, function () { fr.abort(); }); } util.readBlobAsText = readBlobAsText; function readBlobAsArrayBuffer(blob) { var fr = new FileReader(); return new RSVP.Promise(function (resolve, reject, notify) { fr.addEventListener("load", resolve); fr.addEventListener("error", reject); fr.addEventListener("progress", notify); fr.readAsArrayBuffer(blob); }, function () { fr.abort(); }); } util.readBlobAsArrayBuffer = readBlobAsArrayBuffer; // // // // XXX What is "jio"? // // var rest_method_names = [ // // "remove", // // "allDocs", // // "removeAttachment", // // "check", // // "repair" // // ], // // i, // // len = rest_method_names.length; // // // // for (i = 0; i < len; i += 1) { // // declareMethod(rest_method_names[i]); // // } // // ["removeAttachment"].forEach(function (method) { // // shared.on(method, function (param) { // // if (!checkId(param)) { // // checkAttachmentId(param); // // } // // }); // // }); // // // // // // ["check", "repair"].forEach(function (method) { // // shared.on(method, function (param) { // // if (param.kwargs._id !== undefined) { // // if (!checkId(param)) { // // return; // // } // // } // // }); // // }); // tools function checkId(param, storage, method_name) { if (typeof param._id !== 'string' || param._id === '') { throw new jIO.util.jIOError("Document id must be a non empty string on '" + storage.__type + "." + method_name + "'.", 400); } } function checkAttachmentId(param, storage, method_name) { if (typeof param._attachment !== 'string' || param._attachment === '') { throw new jIO.util.jIOError( "Attachment id must be a non empty string on '" + storage.__type + "." + method_name + "'.", 400 ); } } function declareMethod(klass, name, precondition_function, post_function) { klass.prototype[name] = function () { var argument_list = arguments, context = this; return new RSVP.Queue() .push(function () { if (precondition_function !== undefined) { return precondition_function.apply( context.__storage, [argument_list[0], context, name] ); } }) .push(function () { var storage_method = context.__storage[name]; if (storage_method === undefined) { throw new jIO.util.jIOError( "Capacity '" + name + "' is not implemented on '" + context.__type + "'", 501 ); } return storage_method.apply( context.__storage, argument_list ); }) .push(function (result) { if (post_function !== undefined) { return post_function.call( context, argument_list, result ); } return result; }); }; // Allow chain return this; } ///////////////////////////////////////////////////////////////// // jIO Storage Proxy ///////////////////////////////////////////////////////////////// function JioProxyStorage(type, storage) { if (!(this instanceof JioProxyStorage)) { return new JioProxyStorage(); } this.__type = type; this.__storage = storage; } declareMethod(JioProxyStorage, "put", checkId, function (argument_list) { return argument_list[0]._id; }); declareMethod(JioProxyStorage, "get", checkId, function (argument_list, result) { // XXX Drop all _ properties // Put _id properties to the result result._id = argument_list[0]._id; return result; }); declareMethod(JioProxyStorage, "remove", checkId, function (argument_list) { return argument_list[0]._id; }); // listeners declareMethod(JioProxyStorage, "post", function (param, storage, method_name) { if (param._id !== undefined) { return checkId(param, storage, method_name); } }); declareMethod(JioProxyStorage, 'putAttachment', function (param, storage, method_name) { checkId(param, storage, method_name); checkAttachmentId(param, storage, method_name); if (!(param._blob instanceof Blob) && typeof param._data === 'string') { param._blob = new Blob([param._data], { "type": param._content_type || param._mimetype || "" }); delete param._data; delete param._mimetype; delete param._content_type; } else if (param._blob instanceof Blob) { delete param._data; delete param._mimetype; delete param._content_type; } else if (param._data instanceof Blob) { param._blob = param._data; delete param._data; delete param._mimetype; delete param._content_type; } else { throw new jIO.util.jIOError( 'Attachment information must be like {"_id": document id, ' + '"_attachment": attachment name, "_data": string, ["_mimetype": ' + 'content type]} or {"_id": document id, "_attachment": ' + 'attachment name, "_blob": Blob}', 400 ); } }); declareMethod(JioProxyStorage, 'removeAttachment', function (param, storage, method_name) { checkId(param, storage, method_name); checkAttachmentId(param, storage, method_name); }); declareMethod(JioProxyStorage, 'getAttachment', function (param, storage, method_name) { // if (param.storage_spec.type !== "indexeddb" && // param.storage_spec.type !== "dav" && // (param.kwargs._start !== undefined // || param.kwargs._end !== undefined)) { // restCommandRejecter(param, [ // 'bad_request', // 'unsupport', // '_start, _end not support' // ]); // return false; // } checkId(param, storage, method_name); checkAttachmentId(param, storage, method_name); }, function (argument_list, result) { if (!(result.data instanceof Blob)) { throw new jIO.util.jIOError( "'getAttachment' (" + argument_list[0]._id + " , " + argument_list[0]._attachment + ") on '" + this.__type + "' does not return a Blob.", 501 ); } return result; }); JioProxyStorage.prototype.buildQuery = function () { var storage_method = this.__storage.buildQuery, context = this, argument_list = arguments; if (storage_method === undefined) { throw new jIO.util.jIOError( "Capacity 'buildQuery' is not implemented on '" + this.__type + "'", 501 ); } return new RSVP.Queue() .push(function () { return storage_method.apply( context.__storage, argument_list ); }); }; JioProxyStorage.prototype.hasCapacity = function (name) { var storage_method = this.__storage.hasCapacity; if ((storage_method === undefined) || !storage_method.apply(this.__storage, arguments)) { throw new jIO.util.jIOError( "Capacity '" + name + "' is not implemented on '" + this.__type + "'", 501 ); } return true; }; JioProxyStorage.prototype.allDocs = function (options) { var context = this; if (options === undefined) { options = {}; } return new RSVP.Queue() .push(function () { if (context.hasCapacity("list") && ((options.query === undefined) || context.hasCapacity("query")) && ((options.sort_on === undefined) || context.hasCapacity("sort")) && ((options.select_list === undefined) || context.hasCapacity("select")) && ((options.include_docs === undefined) || context.hasCapacity("include")) && ((options.limit === undefined) || context.hasCapacity("limit"))) { return context.buildQuery(options); } }) .push(function (result) { return { data: { rows: result, total_rows: result.length } }; }); }; ///////////////////////////////////////////////////////////////// // Storage builder ///////////////////////////////////////////////////////////////// function JioBuilder() { if (!(this instanceof JioBuilder)) { return new JioBuilder(); } this.__storage_types = {}; } JioBuilder.prototype.createJIO = function (storage_spec, util) { if (typeof storage_spec.type !== 'string') { throw new TypeError("Invalid storage description"); } if (!this.__storage_types[storage_spec.type]) { throw new TypeError("Unknown storage '" + storage_spec.type + "'"); } return new JioProxyStorage( storage_spec.type, new this.__storage_types[storage_spec.type](storage_spec, util) ); }; JioBuilder.prototype.addStorage = function (type, Constructor) { if (typeof type !== 'string') { throw new TypeError( "jIO.addStorage(): Argument 1 is not of type 'string'" ); } if (typeof Constructor !== 'function') { throw new TypeError("jIO.addStorage(): " + "Argument 2 is not of type 'function'"); } if (this.__storage_types[type] !== undefined) { throw new TypeError("jIO.addStorage(): Storage type already exists"); } this.__storage_types[type] = Constructor; }; JioBuilder.prototype.util = util; JioBuilder.prototype.QueryFactory = QueryFactory; JioBuilder.prototype.Query = Query; ///////////////////////////////////////////////////////////////// // global ///////////////////////////////////////////////////////////////// jIO = new JioBuilder(); window.jIO = jIO; }(window, RSVP, Blob, QueryFactory, Query, FileReader)); ;/* * Copyright 2013, Nexedi SA * Released under the LGPL license. * http://www.gnu.org/licenses/lgpl.html */ /*jslint nomen: true*/ /*global jIO*/ /** * JIO Memory Storage. Type = 'memory'. * Memory browser "database" storage. * * Storage Description: * * { * "type": "memory" * } * * @class MemoryStorage */ (function (jIO) { "use strict"; /** * The JIO MemoryStorage extension * * @class MemoryStorage * @constructor */ function MemoryStorage() { this._database = {}; } MemoryStorage.prototype.put = function (metadata) { if (!this._database.hasOwnProperty(metadata._id)) { this._database[metadata._id] = { attachments: {} }; } this._database[metadata._id].doc = JSON.stringify(metadata); return metadata._id; }; MemoryStorage.prototype.get = function (param) { var doc, key, found = false, attachments = {}; try { doc = JSON.parse(this._database[param._id].doc); } catch (error) { if (error instanceof TypeError) { throw new jIO.util.jIOError( "Cannot find document: " + param._id, 404 ); } throw error; } // XXX NotImplemented: list all attachments for (key in this._database[param._id].attachments) { if (this._database[param._id].attachments.hasOwnProperty(key)) { found = true; attachments[key] = {}; } } if (found) { doc._attachments = attachments; } return doc; }; MemoryStorage.prototype.remove = function (param) { delete this._database[param._id]; return param._id; }; MemoryStorage.prototype.getAttachment = function (param) { try { return {data: this._database[param._id].attachments[param._attachment]}; } catch (error) { if (error instanceof TypeError) { throw new jIO.util.jIOError( "Cannot find attachment: " + param._id + " , " + param._attachment, 404 ); } throw error; } }; MemoryStorage.prototype.putAttachment = function (param) { var attachment_dict; try { attachment_dict = this._database[param._id].attachments; } catch (error) { if (error instanceof TypeError) { throw new jIO.util.jIOError("Cannot find document: " + param._id, 404); } throw error; } attachment_dict[param._attachment] = param._blob; }; MemoryStorage.prototype.removeAttachment = function (param) { try { delete this._database[param._id].attachments[param._attachment]; } catch (error) { if (error instanceof TypeError) { throw new jIO.util.jIOError( "Cannot find document: " + param._id, 404 ); } throw error; } }; MemoryStorage.prototype.hasCapacity = function (name) { return (name === "list"); }; MemoryStorage.prototype.buildQuery = function () { var rows = [], i; for (i in this._database) { if (this._database.hasOwnProperty(i)) { rows.push({ id: i, value: {} }); } } return rows; }; jIO.addStorage('memory', MemoryStorage); }(jIO)); ;/* * Copyright 2013, Nexedi SA * Released under the LGPL license. * http://www.gnu.org/licenses/lgpl.html */ /*jslint nomen: true */ /*global jIO, sessionStorage, localStorage, Blob, RSVP */ /** * JIO Local Storage. Type = 'local'. * Local browser "database" storage. * * Storage Description: * * { * "type": "local", * "sessiononly": false * } * * @class LocalStorage */ (function (jIO, sessionStorage, localStorage, Blob, RSVP) { "use strict"; function LocalStorage(spec) { if (spec.sessiononly === true) { this._storage = sessionStorage; } else { this._storage = localStorage; } } function restrictDocumentId(id) { if (id !== "/") { throw new jIO.util.jIOError("id " + id + " is forbidden (!== /)", 400); } } LocalStorage.prototype.get = function (param) { restrictDocumentId(param._id); var doc = {}, attachments = {}, found = false, key; for (key in this._storage) { if (this._storage.hasOwnProperty(key)) { attachments[key] = {}; found = true; } } if (found) { doc._attachments = attachments; } return doc; }; LocalStorage.prototype.getAttachment = function (param) { restrictDocumentId(param._id); var textstring = this._storage.getItem(param._attachment); if (textstring === null) { throw new jIO.util.jIOError( "Cannot find attachment " + param._attachment, 404 ); } return {data: new Blob([textstring])}; }; LocalStorage.prototype.putAttachment = function (param) { var context = this; restrictDocumentId(param._id); // the document already exists // download data return new RSVP.Queue() .push(function () { return jIO.util.readBlobAsText(param._blob); }) .push(function (e) { context._storage.setItem(param._attachment, e.target.result); }); }; LocalStorage.prototype.removeAttachment = function (param) { restrictDocumentId(param._id); return this._storage.removeItem(param._attachment); }; LocalStorage.prototype.hasCapacity = function (name) { return (name === "list"); }; LocalStorage.prototype.buildQuery = function () { return [{ id: "/", value: {} }]; }; jIO.addStorage('local', LocalStorage); }(jIO, sessionStorage, localStorage, Blob, RSVP)); ;/* * Copyright 2013, Nexedi SA * Released under the LGPL license. * http://www.gnu.org/licenses/lgpl.html */ /*jslint nomen: true*/ /*global jIO, RSVP, DOMParser, Blob */ // JIO Dav Storage Description : // { // type: "dav", // url: {string}, // basic_login: {string} // Basic authentication // } // NOTE: to get the authentication type -> // curl --verbose -X OPTION http://domain/ // In the headers: "WWW-Authenticate: Basic realm="DAV-upload" (function (jIO, RSVP, DOMParser, Blob) { "use strict"; function ajax(storage, options) { if (options === undefined) { options = {}; } if (storage._authorization !== undefined) { if (options.headers === undefined) { options.headers = {}; } options.headers.Authorization = storage._authorization; } // if (start !== undefined) { // if (end !== undefined) { // headers.Range = "bytes=" + start + "-" + end; // } else { // headers.Range = "bytes=" + start + "-"; // } // } return new RSVP.Queue() .push(function () { return jIO.util.ajax(options); }); } function restrictDocumentId(id) { if (id.indexOf("/") !== 0) { throw new jIO.util.jIOError("id " + id + " is forbidden (no begin /)", 400); } if (id.lastIndexOf("/") !== (id.length - 1)) { throw new jIO.util.jIOError("id " + id + " is forbidden (no end /)", 400); } return id; } function restrictAttachmentId(id) { if (id.indexOf("/") !== -1) { throw new jIO.util.jIOError("attachment " + id + " is forbidden", 400); } } /** * The JIO WebDAV Storage extension * * @class DavStorage * @constructor */ function DavStorage(spec) { if (typeof spec.url !== 'string') { throw new TypeError("DavStorage 'url' is not of type string"); } this._url = spec.url; // XXX digest login if (typeof spec.basic_login === 'string') { this._authorization = "Basic " + spec.basic_login; } } DavStorage.prototype.put = function (param) { var id = restrictDocumentId(param._id); delete param._id; if (Object.getOwnPropertyNames(param).length > 0) { // Reject if param has other properties than _id throw new jIO.util.jIOError("Can not store properties: " + Object.getOwnPropertyNames(param), 400); } return ajax(this, { type: "MKCOL", url: this._url + id }); }; DavStorage.prototype.remove = function (param) { var id = restrictDocumentId(param._id); return ajax(this, { type: "DELETE", url: this._url + id }); }; DavStorage.prototype.get = function (param) { var id = restrictDocumentId(param._id), context = this; return new RSVP.Queue() .push(function () { return ajax(context, { type: "PROPFIND", url: context._url + id, dataType: "text", headers: { // Increasing this value is a performance killer Depth: "1" } }); }) .push(function (response) { // Extract all meta informations and return them to JSON var i, result = {}, attachment = {}, id, attachment_list = new DOMParser().parseFromString( response.target.responseText, "text/xml" ).querySelectorAll( "D\\:response, response" ); // exclude parent folder and browse for (i = 1; i < attachment_list.length; i += 1) { // XXX Only get files for now id = attachment_list[i].querySelector("D\\:href, href"). textContent.split('/').slice(-1)[0]; // XXX Ugly if ((id !== undefined) && (id !== "")) { attachment[id] = {}; } } if (Object.getOwnPropertyNames(attachment).length > 0) { result._attachments = attachment; } return result; }, function (error) { if ((error.target !== undefined) && (error.target.status === 404)) { throw new jIO.util.jIOError("Cannot find document", 404); } throw error; }); }; DavStorage.prototype.putAttachment = function (param) { var id = restrictDocumentId(param._id); restrictAttachmentId(param._attachment); return ajax(this, { type: "PUT", url: this._url + id + param._attachment, data: param._blob }); }; DavStorage.prototype.getAttachment = function (param) { var id = restrictDocumentId(param._id), context = this; restrictAttachmentId(param._attachment); return new RSVP.Queue() .push(function () { return ajax(context, { type: "GET", url: context._url + id + param._attachment, dataType: "blob" }); }) .push(function (response) { return {data: new Blob( [response.target.response || response.target.responseText], {"type": response.target.getResponseHeader('Content-Type') || "application/octet-stream"} )}; }, function (error) { if ((error.target !== undefined) && (error.target.status === 404)) { throw new jIO.util.jIOError("Cannot find attachment: " + param._id + " , " + param._attachment, 404); } throw error; }); }; DavStorage.prototype.removeAttachment = function (param) { var id = restrictDocumentId(param._id), context = this; restrictAttachmentId(param._attachment); return new RSVP.Queue() .push(function () { return ajax(context, { type: "DELETE", url: context._url + id + param._attachment }); }) .push(undefined, function (error) { if ((error.target !== undefined) && (error.target.status === 404)) { throw new jIO.util.jIOError("Cannot find attachment: " + param._id + " , " + param._attachment, 404); } throw error; }); }; // JIO COMMANDS // // wedDav methods rfc4918 (short summary) // COPY Reproduces single resources (files) and collections (directory // trees). Will overwrite files (if specified by request) but will // respond 209 (Conflict) if it would overwrite a tree // DELETE deletes files and directory trees // GET just the vanilla HTTP/1.1 behaviour // HEAD ditto // LOCK locks a resources // MKCOL creates a directory // MOVE Moves (rename or copy) a file or a directory tree. Will // 'overwrite' files (if specified by the request) but will respond // 209 (Conflict) if it would overwrite a tree. // OPTIONS If WebDAV is enabled and available for the path this reports the // WebDAV extension methods // PROPFIND Retrieves the requested file characteristics, DAV lock status // and 'dead' properties for individual files, a directory and its // child files, or a directory tree // PROPPATCHset and remove 'dead' meta-data properties // PUT Update or create resource or collections // UNLOCK unlocks a resource // Notes: all Ajax requests should be CORS (cross-domain) // adding custom headers triggers preflight OPTIONS request! // http://remysharp.com/2011/04/21/getting-cors-working/ jIO.addStorage('dav', DavStorage); }(jIO, RSVP, DOMParser, Blob)); ;/*jslint nomen: true */ /*global RSVP*/ /** * JIO Union Storage. Type = 'union'. * This provide a unified access other multiple storage. * New document are created in the first sub storage. * Document are searched in each sub storage until it is found. * * * Storage Description: * * { * "type": "union", * "storage_list": [ * sub_storage_description_1, * sub_storage_description_2, * * sub_storage_description_X, * ] * } * * @class UnionStorage */ (function (jIO, RSVP) { "use strict"; /** * The JIO UnionStorage extension * * @class UnionStorage * @constructor */ function UnionStorage(spec) { if (!Array.isArray(spec.storage_list)) { throw new jIO.util.jIOError("storage_list is not an Array", 400); } var i; this._storage_list = []; for (i = 0; i < spec.storage_list.length; i += 1) { this._storage_list.push(jIO.createJIO(spec.storage_list[i])); } } UnionStorage.prototype._getWithStorageIndex = function () { var i, index = 0, context = this, arg = arguments, result = this._storage_list[0].get.apply(this._storage_list[0], arg); function handle404(j) { result .push(undefined, function (error) { if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) { return context._storage_list[j].get.apply(context._storage_list[j], arg) .push(function (doc) { index = j; return doc; }); } throw error; }); } for (i = 1; i < this._storage_list.length; i += 1) { handle404(i); } return result .push(function (doc) { return [index, doc]; }); }; /* * Get a document * Try on each substorage on after the other */ UnionStorage.prototype.get = function () { return this._getWithStorageIndex.apply(this, arguments) .push(function (result) { return result[1]; }); }; /* * Post a document * Simply store on the first substorage */ UnionStorage.prototype.post = function () { return this._storage_list[0].post.apply(this._storage_list[0], arguments); }; /* * Put a document * Search the document location, and modify it in its storage. */ UnionStorage.prototype.put = function () { var arg = arguments, context = this; return this._getWithStorageIndex({"_id": arg[0]._id}) .push(function (result) { // Storage found, modify in it directly var sub_storage = context._storage_list[result[0]]; return sub_storage.put.apply(sub_storage, arg); }); }; /* * Remove a document * Search the document location, and remove it from its storage. */ UnionStorage.prototype.remove = function () { var arg = arguments, context = this; return this._getWithStorageIndex({"_id": arg[0]._id}) .push(function (result) { // Storage found, remove from it directly var sub_storage = context._storage_list[result[0]]; return sub_storage.remove.apply(sub_storage, arg); }); }; UnionStorage.prototype.buildQuery = function () { var promise_list = [], i, id_dict = {}, len = this._storage_list.length, sub_storage; for (i = 0; i < len; i += 1) { sub_storage = this._storage_list[i]; promise_list.push(sub_storage.buildQuery.apply(sub_storage, arguments)); } return new RSVP.Queue() .push(function () { return RSVP.all(promise_list); }) .push(function (result_list) { var result = [], sub_result, sub_result_len, j; len = result_list.length; for (i = 0; i < len; i += 1) { sub_result = result_list[i]; sub_result_len = sub_result.length; for (j = 0; j < sub_result_len; j += 1) { if (!id_dict.hasOwnProperty(sub_result[j].id)) { id_dict[sub_result[j].id] = null; result.push(sub_result[j]); } } } return result; }); }; UnionStorage.prototype.hasCapacity = function (name) { var i, len, result, sub_storage; if ((name === "list") || (name === "query") || (name === "select")) { result = true; len = this._storage_list.length; for (i = 0; i < len; i += 1) { sub_storage = this._storage_list[i]; result = result && sub_storage.hasCapacity(name); } return result; } return false; }; jIO.addStorage('union', UnionStorage); }(jIO, RSVP)); ;/* * Copyright 2013, Nexedi SA * Released under the LGPL license. * http://www.gnu.org/licenses/lgpl.html */ // JIO ERP5 Storage Description : // { // type: "erp5" // url: {string} // } /*jslint nomen: true */ /*global jIO, UriTemplate, FormData, RSVP, URI, Blob*/ (function (jIO, UriTemplate, FormData, RSVP, URI, Blob) { "use strict"; function getSiteDocument(storage) { return new RSVP.Queue() .push(function () { return jIO.util.ajax({ "type": "GET", "url": storage._url, "xhrFields": { withCredentials: true } }); }) .push(function (event) { return JSON.parse(event.target.responseText); }); } function getDocumentAndHateoas(storage, param, options) { if (options === undefined) { options = {}; } return getSiteDocument(storage) .push(function (site_hal) { // XXX need to get modified metadata return jIO.util.ajax({ "type": "GET", "url": UriTemplate.parse(site_hal._links.traverse.href) .expand({ relative_url: param._id, view: options._view }), "xhrFields": { withCredentials: true } }); }); } // XXX docstring function ERP5Storage(spec) { if (typeof spec.url !== "string" || !spec.url) { throw new TypeError("ERP5 'url' must be a string " + "which contains more than one character."); } this._url = spec.url; } ERP5Storage.prototype.get = function (param) { return getDocumentAndHateoas(this, param) .push(function (response) { var result = JSON.parse(response.target.responseText), attachments = { view: {}, links: {} }, key; // action_type; result._id = param._id; result.portal_type = result._links.type.name; // Remove all ERP5 hateoas links / convert them into jIO ID for (key in result) { if (result.hasOwnProperty(key)) { if (key.indexOf("_") === 0) { delete result[key]; } } } result._attachments = attachments; return result; }); }; ERP5Storage.prototype.getAttachment = function (param) { var action = param._attachment; if (action === "view") { return getDocumentAndHateoas(this, param, {"_view": param._attachment}) .push(function (response) { var result = JSON.parse(response.target.responseText); result._id = param._id; result.portal_type = result._links.type.name; // Remove all ERP5 hateoas links / convert them into jIO ID // XXX Change default action to an jio urn with attachment name inside // if Base_edit, do put URN // if others, do post URN (ie, unique new attachment name) // XXX Except this attachment name should be generated when return {data: new Blob( [JSON.stringify(result)], {"type": 'application/hal+json'} )}; }); } if (action === "links") { return getDocumentAndHateoas(this, param) .push(function (response) { return {data: new Blob( [JSON.stringify(JSON.parse(response.target.responseText))], {"type": 'application/hal+json'} )}; }); } if (action.indexOf(this._url) === 0) { return new RSVP.Queue() .push(function () { return jIO.util.ajax({ "type": "GET", "url": action, "xhrFields": { withCredentials: true } }); }) .push(function (evt) { var result = JSON.parse(evt.target.responseText); result._id = param._id; return {data: new Blob( [JSON.stringify(result)], {"type": evt.target.getResponseHeader("Content-Type")} )}; }); } throw new jIO.util.jIOError("ERP5: not support get attachment: " + action, 400); }; ERP5Storage.prototype.putAttachment = function (metadata) { // Assert we use a callable on a document from the ERP5 site if (metadata._attachment.indexOf(this._url) !== 0) { throw new jIO.util.jIOError("Can not store outside ERP5: " + metadata._attachment, 400); } return new RSVP.Queue() .push(function () { return jIO.util.readBlobAsText(metadata._blob); }) .push(function (evt) { var form_data = JSON.parse(evt.target.result), data = new FormData(), key; for (key in form_data) { if (form_data.hasOwnProperty(key)) { data.append(key, form_data[key]); } } return jIO.util.ajax({ "type": "POST", "url": metadata._attachment, "data": data, "xhrFields": { withCredentials: true } }); }); }; ERP5Storage.prototype.hasCapacity = function (name) { return ((name === "list") || (name === "query") || (name === "select") || (name === "limit")); }; ERP5Storage.prototype.buildQuery = function (options) { // if (typeof options.query !== "string") { // options.query = (options.query ? // jIO.Query.objectToSearchText(options.query) : // undefined); // } return getSiteDocument(this) .push(function (site_hal) { return jIO.util.ajax({ "type": "GET", "url": UriTemplate.parse(site_hal._links.raw_search.href) .expand({ query: options.query, // XXX Force erp5 to return embedded document select_list: options.select_list || ["title", "reference"], limit: options.limit }), "xhrFields": { withCredentials: true } }); }) .push(function (response) { return JSON.parse(response.target.responseText); }) .push(function (catalog_json) { var data = catalog_json._embedded.contents, count = data.length, i, uri, item, result = []; for (i = 0; i < count; i += 1) { item = data[i]; uri = new URI(item._links.self.href); delete item._links; result.push({ id: uri.segment(2), value: item }); } return result; }); }; jIO.addStorage("erp5", ERP5Storage); }(jIO, UriTemplate, FormData, RSVP, URI, Blob)); ;/*jslint nomen: true*/ /*global RSVP*/ (function (jIO, RSVP) { "use strict"; /** * The jIO QueryStorage extension * * @class QueryStorage * @constructor */ function QueryStorage(spec) { this._sub_storage = jIO.createJIO(spec.sub_storage); this._key_schema = spec.key_schema; } QueryStorage.prototype.get = function () { return this._sub_storage.get.apply(this._sub_storage, arguments); }; QueryStorage.prototype.post = function () { return this._sub_storage.post.apply(this._sub_storage, arguments); }; QueryStorage.prototype.put = function () { return this._sub_storage.put.apply(this._sub_storage, arguments); }; QueryStorage.prototype.remove = function () { return this._sub_storage.remove.apply(this._sub_storage, arguments); }; QueryStorage.prototype.getAttachment = function () { return this._sub_storage.getAttachment.apply(this._sub_storage, arguments); }; QueryStorage.prototype.putAttachment = function () { return this._sub_storage.putAttachment.apply(this._sub_storage, arguments); }; QueryStorage.prototype.removeAttachment = function () { return this._sub_storage.removeAttachment.apply(this._sub_storage, arguments); }; QueryStorage.prototype.hasCapacity = function (name) { if (name === "list") { return this._sub_storage.hasCapacity(name); } return true; }; QueryStorage.prototype.buildQuery = function (options) { var substorage = this._sub_storage, context = this, sub_options = {}, is_manual_query_needed = false, is_manual_include_needed = false; if (substorage.hasCapacity("list")) { // Can substorage handle the queries if needed? try { if (((options.query === undefined) || (substorage.hasCapacity("query"))) && ((options.sort_on === undefined) || (substorage.hasCapacity("sort"))) && ((options.select_list === undefined) || (substorage.hasCapacity("select"))) && ((options.limit === undefined) || (substorage.hasCapacity("limit")))) { sub_options.query = options.query; sub_options.sort_on = options.sort_on; sub_options.select_list = options.select_list; sub_options.limit = options.limit; } } catch (error) { if ((error instanceof jIO.util.jIOError) && (error.status_code === 501)) { is_manual_query_needed = true; } else { throw error; } } // Can substorage include the docs if needed? try { if ((is_manual_query_needed || (options.include_docs === true)) && (substorage.hasCapacity("include"))) { sub_options.include_docs = true; } } catch (error) { if ((error instanceof jIO.util.jIOError) && (error.status_code === 501)) { is_manual_include_needed = true; } else { throw error; } } return substorage.buildQuery(sub_options) // Include docs if needed .push(function (result) { var include_query_list = [result], len, i; function safeGet(j) { return substorage.get({"_id": result[j].id}) .push(undefined, function (error) { // Document may have been dropped after listing if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) { return; } throw error; }); } if (is_manual_include_needed) { len = result.length; for (i = 0; i < len; i += 1) { include_query_list.push(safeGet(i)); } result = RSVP.all(include_query_list); } return result; }) .push(function (result) { var original_result, len, i; if (is_manual_include_needed) { original_result = result[0]; len = original_result.length; for (i = 0; i < len; i += 1) { original_result[i].doc = result[i + 1]; } result = original_result; } return result; }) // Manual query if needed .push(function (result) { var data_rows = [], len, i; if (is_manual_query_needed) { len = result.length; for (i = 0; i < len; i += 1) { data_rows.push(result[i].doc); } if (options.select_list) { options.select_list.push("_id"); } result = jIO.QueryFactory.create(options.query || "", context._key_schema). exec(data_rows, options); } return result; }) // reconstruct filtered rows, preserving the order from docs .push(function (result) { var new_result = [], element, len, i; if (is_manual_query_needed) { len = result.length; for (i = 0; i < len; i += 1) { element = { id: result[i]._id, value: options.select_list ? result[i] : {}, doc: {} }; if (options.select_list) { // Does not work if user manually request _id delete element.value._id; } if (options.include_docs) { // XXX To implement throw new Error("QueryStorage does not support include docs"); } new_result.push(element); } result = new_result; } return result; }); } }; jIO.addStorage('query', QueryStorage); }(jIO, RSVP)); ;/*jslint nomen: true*/ /*global RSVP, Blob*/ (function (jIO, RSVP, Blob) { "use strict"; /** * The jIO FileSystemBridgeStorage extension * * @class FileSystemBridgeStorage * @constructor */ function FileSystemBridgeStorage(spec) { this._sub_storage = jIO.createJIO(spec.sub_storage); } var DOCUMENT_EXTENSION = ".json", DOCUMENT_REGEXP = new RegExp("^([\\w=]+)" + DOCUMENT_EXTENSION + "$"), DOCUMENT_KEY = "/.jio_documents/", ROOT = "/"; FileSystemBridgeStorage.prototype.get = function (param) { var context = this, json_document, explicit_document = false; return new RSVP.Queue() // First, try to get explicit reference to the document .push(function () { // First get the document itself if it exists return context._sub_storage.getAttachment({ "_id": DOCUMENT_KEY, "_attachment": param._id + DOCUMENT_EXTENSION }); }) .push(function (blob) { return new RSVP.Queue() .push(function () { return jIO.util.readBlobAsText(blob.data); }) .push(function (text) { explicit_document = true; return JSON.parse(text.target.result); }); }, function (error) { if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) { return {}; } throw error; }) // Second, try to get default attachment .push(function (result) { json_document = result; return context._sub_storage.get({ "_id": ROOT }); }) .push(function (directory_document) { if ((directory_document.hasOwnProperty("_attachments")) && (directory_document._attachments.hasOwnProperty(param._id))) { json_document._attachments = { enclosure: {} }; } else { if (!explicit_document) { throw new jIO.util.jIOError("Cannot find document " + param._id, 404); } } return json_document; }); }; FileSystemBridgeStorage.prototype.put = function (param) { var context = this, doc_id = param._id; // XXX Handle conflict! return context._sub_storage.putAttachment({ "_id": DOCUMENT_KEY, "_attachment": doc_id + DOCUMENT_EXTENSION, "_blob": new Blob([JSON.stringify(param)], {type: "application/json"}) }) .push(undefined, function (error) { if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) { return context._sub_storage.put({ "_id": DOCUMENT_KEY }) .push(function () { return context._sub_storage.putAttachment({ "_id": DOCUMENT_KEY, "_attachment": doc_id + DOCUMENT_EXTENSION, "_blob": new Blob([JSON.stringify(param)], {type: "application/json"}) }); }); } throw error; }) .push(function () { return doc_id; }); }; FileSystemBridgeStorage.prototype.remove = function (param) { var context = this, got_error = false, doc_id = param._id; return new RSVP.Queue() // First, try to remove enclosure .push(function () { return context._sub_storage.removeAttachment({ "_id": ROOT, "_attachment": doc_id }); }) .push(undefined, function (error) { if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) { got_error = true; return; } throw error; }) // Second, try to remove explicit doc .push(function () { return context._sub_storage.removeAttachment({ "_id": DOCUMENT_KEY, "_attachment": doc_id + DOCUMENT_EXTENSION }); }) .push(undefined, function (error) { if ((!got_error) && (error instanceof jIO.util.jIOError) && (error.status_code === 404)) { return doc_id; } throw error; }); }; FileSystemBridgeStorage.prototype.hasCapacity = function (capacity) { return (capacity === "list"); }; FileSystemBridgeStorage.prototype.buildQuery = function () { var result_dict = {}, context = this; return new RSVP.Queue() // First, get list of explicit documents .push(function () { return context._sub_storage.get({ "_id": DOCUMENT_KEY }); }) .push(function (result) { var key; for (key in result._attachments) { if (result._attachments.hasOwnProperty(key)) { if (DOCUMENT_REGEXP.test(key)) { result_dict[DOCUMENT_REGEXP.exec(key)[1]] = null; } } } }, function (error) { if ((error instanceof jIO.util.jIOError) && (error.status_code === 404)) { return; } throw error; }) // Second, get list of enclosure .push(function () { return context._sub_storage.get({ "_id": ROOT }); }) .push(function (result) { var key; for (key in result._attachments) { if (result._attachments.hasOwnProperty(key)) { result_dict[key] = null; } } }) // Finally, build the result .push(function () { var result = [], key; for (key in result_dict) { if (result_dict.hasOwnProperty(key)) { result.push({ id: key, value: {} }); } } return result; }); }; FileSystemBridgeStorage.prototype.getAttachment = function (param) { if (param._attachment !== "enclosure") { throw new jIO.util.jIOError("Only support 'enclosure' attachment", 400); } return this._sub_storage.getAttachment({ "_id": ROOT, "_attachment": param._id }); }; FileSystemBridgeStorage.prototype.putAttachment = function (param) { if (param._attachment !== "enclosure") { throw new jIO.util.jIOError("Only support 'enclosure' attachment", 400); } return this._sub_storage.putAttachment({ "_id": ROOT, "_attachment": param._id, "_blob": param._blob }); }; FileSystemBridgeStorage.prototype.removeAttachment = function (param) { if (param._attachment !== "enclosure") { throw new jIO.util.jIOError("Only support 'enclosure' attachment", 400); } return this._sub_storage.removeAttachment({ "_id": ROOT, "_attachment": param._id }); }; jIO.addStorage('drivetojiomapping', FileSystemBridgeStorage); }(jIO, RSVP, Blob)); ;/*jslint nomen: true*/ /*global Blob, atob, btoa*/ (function (jIO, Blob, atob, btoa) { "use strict"; /** * The jIO DocumentStorage extension * * @class DocumentStorage * @constructor */ function DocumentStorage(spec) { this._sub_storage = jIO.createJIO(spec.sub_storage); this._document_id = spec.document_id; } var DOCUMENT_EXTENSION = ".json", DOCUMENT_REGEXP = new RegExp("^jio_document/([\\w=]+)" + DOCUMENT_EXTENSION + "$"), ATTACHMENT_REGEXP = new RegExp("^jio_attachment/([\\w=]+)/([\\w=]+)$"); function getSubAttachmentIdFromParam(param) { if (param._attachment === undefined) { return 'jio_document/' + btoa(param._id) + DOCUMENT_EXTENSION; } return 'jio_attachment/' + btoa(param._id) + "/" + btoa(param._attachment); } DocumentStorage.prototype.get = function (param) { var result, context = this; return this._sub_storage.getAttachment({ "_id": this._document_id, "_attachment": getSubAttachmentIdFromParam(param) }) .push(function (blob) { return jIO.util.readBlobAsText(blob.data); }) .push(function (text) { return JSON.parse(text.target.result); }) .push(function (json) { result = json; return context._sub_storage.get({ "_id": context._document_id }); }) .push(function (document) { var attachments = {}, exec, key; for (key in document._attachments) { if (document._attachments.hasOwnProperty(key)) { if (ATTACHMENT_REGEXP.test(key)) { exec = ATTACHMENT_REGEXP.exec(key); try { if (atob(exec[1]) === param._id) { attachments[atob(exec[2])] = {}; } } catch (error) { // Check if unable to decode base64 data if (!error instanceof ReferenceError) { throw error; } } } } } if (Object.getOwnPropertyNames(attachments).length > 0) { result._attachments = attachments; } return result; }); }; DocumentStorage.prototype.put = function (param) { var doc_id = param._id; return this._sub_storage.putAttachment({ "_id": this._document_id, "_attachment": getSubAttachmentIdFromParam(param), "_blob": new Blob([JSON.stringify(param)], {type: "application/json"}) }) .push(function () { return doc_id; }); }; DocumentStorage.prototype.remove = function (param) { return this._sub_storage.removeAttachment({ "_id": this._document_id, "_attachment": getSubAttachmentIdFromParam(param) }) .push(function () { return param._id; }); }; DocumentStorage.prototype.hasCapacity = function (capacity) { return (capacity === "list"); }; DocumentStorage.prototype.buildQuery = function () { return this._sub_storage.get({ "_id": this._document_id }) .push(function (document) { var result = [], key; for (key in document._attachments) { if (document._attachments.hasOwnProperty(key)) { if (DOCUMENT_REGEXP.test(key)) { try { result.push({ id: atob(DOCUMENT_REGEXP.exec(key)[1]), value: {} }); } catch (error) { // Check if unable to decode base64 data if (!error instanceof ReferenceError) { throw error; } } } } } return result; }); }; DocumentStorage.prototype.getAttachment = function (param) { return this._sub_storage.getAttachment({ "_id": this._document_id, "_attachment": getSubAttachmentIdFromParam(param) }); }; DocumentStorage.prototype.putAttachment = function (param) { return this._sub_storage.putAttachment({ "_id": this._document_id, "_attachment": getSubAttachmentIdFromParam(param), "_blob": param._blob }); }; DocumentStorage.prototype.removeAttachment = function (param) { return this._sub_storage.removeAttachment({ "_id": this._document_id, "_attachment": getSubAttachmentIdFromParam(param) }); }; jIO.addStorage('document', DocumentStorage); }(jIO, Blob, atob, btoa)); ;/* * Copyright 2014, Nexedi SA * Released under the LGPL license. * http://www.gnu.org/licenses/lgpl.html */ /** * JIO Indexed Database Storage. * * A local browser "database" storage greatly more powerful than localStorage. * * Description: * * { * "type": "indexeddb", * "database": <string> * } * * The database name will be prefixed by "jio:", so if the database property is * "hello", then you can manually reach this database with * `indexedDB.open("jio:hello");`. (Or * `indexedDB.deleteDatabase("jio:hello");`.) * * For more informations: * * - http://www.w3.org/TR/IndexedDB/ * - https://developer.mozilla.org/en-US/docs/IndexedDB/Using_IndexedDB */ /*jslint nomen: true */ /*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange*/ (function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange) { "use strict"; // Read only as changing it can lead to data corruption var UNITE = 2000000; function IndexedDBStorage(description) { if (typeof description.database !== "string" || description.database === "") { throw new TypeError("IndexedDBStorage 'database' description property " + "must be a non-empty string"); } this._database_name = "jio:" + description.database; } IndexedDBStorage.prototype.hasCapacity = function (name) { return (name === "list"); }; function buildKeyPath(key_list) { return key_list.join("_"); } function handleUpgradeNeeded(evt) { var db = evt.target.result, store; store = db.createObjectStore("metadata", { keyPath: "_id", autoIncrement: false }); // It is not possible to use openKeyCursor on keypath directly // https://www.w3.org/Bugs/Public/show_bug.cgi?id=19955 store.createIndex("_id", "_id", {unique: true}); store = db.createObjectStore("attachment", { keyPath: "_key_path", autoIncrement: false }); store.createIndex("_id", "_id", {unique: false}); store = db.createObjectStore("blob", { keyPath: "_key_path", autoIncrement: false }); store.createIndex("_id_attachment", ["_id", "_attachment"], {unique: false}); store.createIndex("_id", "_id", {unique: false}); } function openIndexedDB(jio_storage) { var db_name = jio_storage._database_name; function resolver(resolve, reject) { // Open DB // var request = indexedDB.open(db_name); request.onerror = function (error) { if (request.result) { request.result.close(); } reject(error); }; request.onabort = function () { request.result.close(); reject("Aborting connection to: " + db_name); }; request.ontimeout = function () { request.result.close(); reject("Connection to: " + db_name + " timeout"); }; request.onblocked = function () { request.result.close(); reject("Connection to: " + db_name + " was blocked"); }; // Create DB if necessary // request.onupgradeneeded = handleUpgradeNeeded; request.onversionchange = function () { request.result.close(); reject(db_name + " was upgraded"); }; request.onsuccess = function () { resolve(request.result); }; } // XXX Canceller??? return new RSVP.Queue() .push(function () { return new RSVP.Promise(resolver); }); } function openTransaction(db, stores, flag, autoclosedb) { var tx = db.transaction(stores, flag); if (autoclosedb !== false) { tx.oncomplete = function () { db.close(); }; } tx.onabort = function () { db.close(); }; return tx; } function handleCursor(request, callback) { function resolver(resolve, reject) { // Open DB // request.onerror = function (error) { if (request.transaction) { request.transaction.abort(); } reject(error); }; request.onsuccess = function (evt) { var cursor = evt.target.result; if (cursor) { // XXX Wait for result try { callback(cursor); } catch (error) { reject(error); } // continue to next iteration cursor["continue"](); } else { resolve(); } }; } // XXX Canceller??? return new RSVP.Promise(resolver); } IndexedDBStorage.prototype.buildQuery = function () { var result_list = []; function pushMetadata(cursor) { result_list.push({ "id": cursor.key, "value": {} }); } return openIndexedDB(this) .push(function (db) { var tx = openTransaction(db, ["metadata"], "readonly"); return handleCursor(tx.objectStore("metadata").index("_id") .openKeyCursor(), pushMetadata); }) .push(function () { return result_list; }); }; function handleGet(request) { function resolver(resolve, reject) { request.onerror = reject; request.onsuccess = function () { if (request.result) { resolve(request.result); } // XXX How to get ID reject(new jIO.util.jIOError("Cannot find document", 404)); }; } return new RSVP.Promise(resolver); } IndexedDBStorage.prototype.get = function (param) { var attachment_dict = {}; function addEntry(cursor) { attachment_dict[cursor.value._attachment] = {}; } return openIndexedDB(this) .push(function (db) { var transaction = openTransaction(db, ["metadata", "attachment"], "readonly"); return RSVP.all([ handleGet(transaction.objectStore("metadata").get(param._id)), handleCursor(transaction.objectStore("attachment").index("_id") .openCursor(IDBKeyRange.only(param._id)), addEntry) ]); }) .push(function (result_list) { var result = result_list[0]; if (Object.getOwnPropertyNames(attachment_dict).length > 0) { result._attachments = attachment_dict; } return result; }); }; function handleRequest(request) { function resolver(resolve, reject) { request.onerror = reject; request.onsuccess = function () { resolve(request.result); }; } return new RSVP.Promise(resolver); } IndexedDBStorage.prototype.put = function (metadata) { return openIndexedDB(this) .push(function (db) { var transaction = openTransaction(db, ["metadata"], "readwrite"); return handleRequest(transaction.objectStore("metadata").put(metadata)); }); }; function deleteEntry(cursor) { cursor["delete"](); } IndexedDBStorage.prototype.remove = function (param) { return openIndexedDB(this) .push(function (db) { var transaction = openTransaction(db, ["metadata", "attachment", "blob"], "readwrite"); return RSVP.all([ handleRequest(transaction .objectStore("metadata")["delete"](param._id)), // XXX Why not possible to delete with KeyCursor? handleCursor(transaction.objectStore("attachment").index("_id") .openCursor(IDBKeyRange.only(param._id)), deleteEntry), handleCursor(transaction.objectStore("blob").index("_id") .openCursor(IDBKeyRange.only(param._id)), deleteEntry) ]); }); }; IndexedDBStorage.prototype.getAttachment = function (param) { var transaction, start, end; return openIndexedDB(this) .push(function (db) { transaction = openTransaction(db, ["attachment", "blob"], "readonly"); // XXX Should raise if key is not good return handleGet(transaction.objectStore("attachment") .get(buildKeyPath([param._id, param._attachment]))); }) .push(function (attachment) { var total_length = attachment.info.length, i, promise_list = [], store = transaction.objectStore("blob"), start_index, end_index; start = param._start || 0; end = param._end || total_length; if (end > total_length) { end = total_length; } if (start < 0 || end < 0) { throw new jIO.util.jIOError("_start and _end must be positive", 400); } if (start > end) { throw new jIO.util.jIOError("_start is greater than _end", 400); } start_index = Math.floor(start / UNITE); end_index = Math.floor(end / UNITE); if (end % UNITE === 0) { end_index -= 1; } for (i = start_index; i <= end_index; i += 1) { promise_list.push( handleGet(store.get(buildKeyPath([param._id, param._attachment, i]))) ); } return RSVP.all(promise_list); }) .push(function (result_list) { var array_buffer_list = [], blob, i, len = result_list.length; for (i = 0; i < len; i += 1) { array_buffer_list.push(result_list[i].blob); } blob = new Blob(array_buffer_list, {type: "application/octet-stream"}); return {data: blob.slice(start, end)}; }); }; function removeAttachment(transaction, param) { return RSVP.all([ // XXX How to get the right attachment handleRequest(transaction.objectStore("attachment")["delete"]( buildKeyPath([param._id, param._attachment]) )), handleCursor(transaction.objectStore("blob").index("_id_attachment") .openCursor(IDBKeyRange.only( [param._id, param._attachment] )), deleteEntry ) ]); } IndexedDBStorage.prototype.putAttachment = function (metadata) { var blob_part = [], transaction, db; return openIndexedDB(this) .push(function (database) { db = database; // Split the blob first return jIO.util.readBlobAsArrayBuffer(metadata._blob); }) .push(function (event) { var array_buffer = event.target.result, total_size = metadata._blob.size, handled_size = 0; while (handled_size < total_size) { blob_part.push(array_buffer.slice(handled_size, handled_size + UNITE)); handled_size += UNITE; } // Remove previous attachment transaction = openTransaction(db, ["attachment", "blob"], "readwrite"); return removeAttachment(transaction, metadata); }) .push(function () { var promise_list = [ handleRequest(transaction.objectStore("attachment").put({ "_key_path": buildKeyPath([metadata._id, metadata._attachment]), "_id": metadata._id, "_attachment": metadata._attachment, "info": { "content_type": metadata._blob.type, "length": metadata._blob.size } })) ], len = blob_part.length, blob_store = transaction.objectStore("blob"), i; for (i = 0; i < len; i += 1) { promise_list.push( handleRequest(blob_store.put({ "_key_path": buildKeyPath([metadata._id, metadata._attachment, i]), "_id" : metadata._id, "_attachment" : metadata._attachment, "_part" : i, "blob": blob_part[i] })) ); } // Store all new data return RSVP.all(promise_list); }); }; IndexedDBStorage.prototype.removeAttachment = function (param) { return openIndexedDB(this) .push(function (db) { var transaction = openTransaction(db, ["attachment", "blob"], "readwrite"); return removeAttachment(transaction, param); }); }; jIO.addStorage("indexeddb", IndexedDBStorage); }(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange));