diff options
60 files changed, 14559 insertions, 0 deletions
| diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5be7e4b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +TODO +apache or MIT probably diff --git a/README.md b/README.md new file mode 100644 index 00000000..41fa9038 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +Angular +====== +TODO diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..9fb7f173 --- /dev/null +++ b/Rakefile @@ -0,0 +1,74 @@ +include FileUtils + +desc 'Compile JavaScript' +task :compile do +  compiled = %x(java -jar lib/shrinksafe/shrinksafe.jar \ +      lib/webtoolkit/webtoolkit.base64.js \ +      lib/underscore/underscore.js \ +      src/Loader.js \ +      src/API.js \ +      src/Binder.js \ +      src/ControlBar.js \ +      src/DataStore.js \ +      src/Filters.js \ +      src/JSON.js \ +      src/Model.js \ +      src/Parser.js \ +      src/Scope.js \ +      src/Server.js \ +      src/Users.js \ +      src/Validators.js \ +      src/Widgets.js \ +      src/angular-bootstrap.js \ +    ) +  f = File.new("angular.js", 'w') +  f.write(compiled) +  f.close +end + +desc 'Compile JavaScript with Google Closure Compiler' +task :compileclosure do +  %x(java -jar lib/compiler-closure/compiler.jar \ +        --compilation_level ADVANCED_OPTIMIZATIONS \ +        --js lib/webtoolkit/webtoolkit.base64.js \ +        --js lib/underscore/underscore.js \ +        --js src/Loader.js \ +        --js src/API.js \ +        --js src/Binder.js \ +        --js src/ControlBar.js \ +        --js src/DataStore.js \ +        --js src/Filters.js \ +        --js src/JSON.js \ +        --js src/Model.js \ +        --js src/Parser.js \ +        --js src/Scope.js \ +        --js src/Server.js \ +        --js src/Users.js \ +        --js src/Validators.js \ +        --js src/Widgets.js \ +        --js src/angular-bootstrap.js \ +        --js_output_file angular.js) +end + +namespace :server do +  desc 'Run JsTestDriver Server' +  task :start do +    sh %x(java -jar lib/jstestdriver/JsTestDriver.jar --browser open --port 9876) +  end + +  desc "Run JavaScript tests against the server" +  task :test do +    sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all) +  end +end + +desc "Run JavaScript tests" +task :test do +  sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all --browser open --port 9876) +end + +desc 'Lint' +task :lint do +  out = %x(lib/jsl/jsl -conf lib/jsl/jsl.default.conf) +  print out +end diff --git a/angular.js b/angular.js new file mode 100644 index 00000000..0c19175d --- /dev/null +++ b/angular.js @@ -0,0 +1,128 @@ +function r(){return function(a){return a}}function v(){return function(){}}function x(a){return function(b){this[a]=b}}function z(a){return function(){return a}}var A; +function aa(a){var b="",c,e,d,f,i,j,n=0;a=a;a=a.replace(/\r\n/g,"\n");e="";for(d=0;d<a.length;d++){f=a.charCodeAt(d);if(f<128)e+=String.fromCharCode(f);else{if(f>127&&f<2048)e+=String.fromCharCode(f>>6|192);else{e+=String.fromCharCode(f>>12|224);e+=String.fromCharCode(f>>6&63|128)}e+=String.fromCharCode(f&63|128)}}for(a=e;n<a.length;){c=a.charCodeAt(n++);e=a.charCodeAt(n++);d=a.charCodeAt(n++);f=c>>2;c=(c&3)<<4|e>>4;i=(e&15)<<2|d>>6;j=d&63;if(isNaN(e))i=j=64;else if(isNaN(d))j=64;b=b+this.eb.charAt(f)+ +this.eb.charAt(c)+this.eb.charAt(i)+this.eb.charAt(j)}return b};(function(){var a=this,b=a.cb;function c(g){this.da=g}var e=typeof StopIteration!=="undefined"?StopIteration:"__break__",d=a.cb=function(g){return new c(g)};if(typeof exports!=="undefined")exports.cb=d;var f=Array.prototype.slice,i=Array.prototype.unshift,j=Object.prototype.toString,n=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable;d.oe="0.5.1";d.a=function(g,h,k){try{if(g.forEach)g.forEach(h,k);else if(d.z(g)||d.dc(g))for(var l=0,p=g.length;l<p;l++)h.call(k,g[l],l,g);else{var s= +d.la(g);p=s.length;for(l=0;l<p;l++)h.call(k,g[s[l]],s[l],g)}}catch(u){if(u!=e)throw u;}return g};d.map=function(g,h,k){if(g&&d.P(g.map))return g.map(h,k);var l=[];d.a(g,function(p,s,u){l.push(h.call(k,p,s,u))});return l};d.reduce=function(g,h,k,l){if(g&&d.P(g.reduce))return g.reduce(d.u(k,l),h);d.a(g,function(p,s,u){h=k.call(l,h,p,s,u)});return h};d.reduceRight=function(g,h,k,l){if(g&&d.P(g.reduceRight))return g.reduceRight(d.u(k,l),h);var p=d.fa(d.D(g)).reverse();d.a(p,function(s,u){h=k.call(l,h, +s,u,g)});return h};d.Ub=function(g,h,k){var l;d.a(g,function(p,s,u){if(h.call(k,p,s,u)){l=p;d.va()}});return l};d.select=function(g,h,k){if(g&&d.P(g.filter))return g.filter(h,k);var l=[];d.a(g,function(p,s,u){h.call(k,p,s,u)&&l.push(p)});return l};d.vf=function(g,h,k){var l=[];d.a(g,function(p,s,u){!h.call(k,p,s,u)&&l.push(p)});return l};d.all=function(g,h,k){h=h||d.ka;if(g&&d.P(g.every))return g.every(h,k);var l=true;d.a(g,function(p,s,u){(l=l&&h.call(k,p,s,u))||d.va()});return l};d.Uc=function(g, +h,k){h=h||d.ka;if(g&&d.P(g.some))return g.some(h,k);var l=false;d.a(g,function(p,s,u){if(l=h.call(k,p,s,u))d.va()});return l};d.Aa=function(g,h){if(d.z(g))return d.indexOf(g,h)!=-1;var k=false;d.a(g,function(l){if(k=l===h)d.va()});return k};d.Ze=function(g,h){var k=d.Q(arguments,2);return d.map(g,function(l){return(h?l[h]:l).apply(l,k)})};d.vb=function(g,h){return d.map(g,function(k){return k[h]})};d.max=function(g,h,k){if(!h&&d.z(g))return Math.max.apply(Math,g);var l={ga:-Infinity};d.a(g,function(p, +s,u){s=h?h.call(k,p,s,u):p;s>=l.ga&&(l={value:p,ga:s})});return l.value};d.min=function(g,h,k){if(!h&&d.z(g))return Math.min.apply(Math,g);var l={ga:Infinity};d.a(g,function(p,s,u){s=h?h.call(k,p,s,u):p;s<l.ga&&(l={value:p,ga:s})});return l.value};d.Rd=function(g,h,k){return d.vb(d.map(g,function(l,p,s){return{value:l,Sb:h.call(k,l,p,s)}}).sort(function(l,p){l=l.Sb;p=p.Sb;return l<p?-1:l>p?1:0}),"value")};d.yf=function(g,h,k){k=k||d.ka;for(var l=0,p=g.length;l<p;){var s=l+p>>1;k(g[s])<k(h)?(l=s+1): +(p=s)}return l};d.D=function(g){if(!g)return[];if(g.D)return g.D();if(d.z(g))return g;if(d.dc(g))return f.call(g);return d.map(g,r())};d.size=function(g){return d.D(g).length};d.jd=function(g,h,k){return h&&!k?f.call(g,0,h):g[0]};d.Q=function(g,h,k){return f.call(g,d.K(h)||k?1:h)};d.ud=function(g){return g[g.length-1]};d.compact=function(g){return d.select(g,function(h){return!!h})};d.kd=function(g){return d.reduce(g,[],function(h,k){if(d.z(k))return h.concat(d.kd(k));h.push(k);return h})};d.Mf=function(g){var h= +d.Q(arguments);return d.select(g,function(k){return!d.Aa(h,k)})};d.Wd=function(g,h){return d.reduce(g,[],function(k,l,p){if(0==p||(h===true?d.ud(k)!=l:!d.Aa(k,l)))k.push(l);return k})};d.Ye=function(g){var h=d.Q(arguments);return d.select(d.Wd(g),function(k){return d.all(h,function(l){return d.indexOf(l,k)>=0})})};d.Pf=function(){for(var g=d.D(arguments),h=d.max(d.vb(g,"length")),k=new Array(h),l=0;l<h;l++)k[l]=d.vb(g,String(l));return k};d.indexOf=function(g,h){if(g.indexOf)return g.indexOf(h);for(var k= +0,l=g.length;k<l;k++)if(g[k]===h)return k;return-1};d.lastIndexOf=function(g,h){if(g.lastIndexOf)return g.lastIndexOf(h);for(var k=g.length;k--;)if(g[k]===h)return k;return-1};d.Md=function(g,h,k){var l=d.D(arguments),p=l.length<=1;g=p?0:l[0];h=p?l[0]:l[1];k=l[2]||1;l=Math.ceil((h-g)/k);if(l<=0)return[];l=new Array(l);p=g;for(var s=0;;p+=k){if((k>0?p-h:h-p)>=0)return l;l[s++]=p}};d.u=function(g,h){var k=d.Q(arguments,2);return function(){return g.apply(h||a,k.concat(d.D(arguments)))}};d.we=function(g){var h= +d.Q(arguments);if(h.length==0)h=d.nb(g);d.a(h,function(k){g[k]=d.u(g[k],g)});return g};d.lb=function(g,h){var k=d.Q(arguments,2);return setTimeout(function(){return g.apply(g,k)},h)};d.defer=function(g){return d.lb.apply(d,[g,1].concat(d.Q(arguments)))};d.Of=function(g,h){return function(){var k=[g].concat(d.D(arguments));return h.apply(h,k)}};d.Ie=function(){var g=d.D(arguments);return function(){for(var h=d.D(arguments),k=g.length-1;k>=0;k--)h=[g[k].apply(this,h)];return h[0]}};d.la=function(g){if(d.z(g))return d.Md(0, +g.length);var h=[];for(var k in g)n.call(g,k)&&h.push(k);return h};d.Ab=function(g){return d.map(g,d.ka)};d.nb=function(g){return d.select(d.la(g),function(h){return d.P(g[h])}).sort()};d.extend=function(g,h){for(var k in h)g[k]=h[k];return g};d.fa=function(g){if(d.z(g))return g.slice(0);return d.extend({},g)};d.isEqual=function(g,h){if(g===h)return true;var k=typeof g;if(k!=typeof h)return false;if(g==h)return true;if(!g&&h||g&&!h)return false;if(g.isEqual)return g.isEqual(h);if(d.pd(g)&&d.pd(h))return g.getTime()=== +h.getTime();if(d.fc(g)&&d.fc(h))return true;if(d.qd(g)&&d.qd(h))return g.source===h.source&&g.global===h.global&&g.ignoreCase===h.ignoreCase&&g.multiline===h.multiline;if(k!=="object")return false;if(g.length&&g.length!==h.length)return false;k=d.la(g);var l=d.la(h);if(k.length!=l.length)return false;for(var p in g)if(!d.isEqual(g[p],h[p]))return false;return true};d.bf=function(g){return d.la(g).length==0};d.af=function(g){return!!(g&&g.nodeType==1)};d.dc=function(g){return g&&"0"<=g.length&&g.length<= +"9"&&!d.z(g)&&!m.call(g,"length")};d.fc=function(g){return"0"<=g&&g<="9"&&isNaN(g)};d.cf=function(g){return g===null};d.K=function(g){return typeof g=="undefined"};for(var o=["Array","Date","Function","Number","RegExp","String"],q=0,w=o.length;q<w;q++)(function(){var g="[object "+o[q]+"]";d["is"+o[q]]=function(h){return j.call(h)==g}})();d.Id=function(){a.cb=b;return this};d.ka=r();d.va=function(){throw e;};var t=0;d.Hf=function(g){var h=t++;return g?g+h:h};d.Z=function(g,h){g=new Function("obj", +"var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+g.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return h?g(h):g};d.forEach=d.a;d.Se=d.We=d.reduce;d.Te=d.reduceRight;d.filter=d.select;d.every=d.all;d.some=d.Uc;d.ac=d.jd;d.Cf=d.Q;d.hf=d.nb;function y(g,h){return h?d(g).Ob():g}d.a(d.nb(d),function(g){var h= +d[g];c.prototype[g]=function(){i.call(arguments,this.da);return y(h.apply(d,arguments),this.db)}});d.a(["pop","push","reverse","shift","sort","splice","unshift"],function(g){var h=Array.prototype[g];c.prototype[g]=function(){h.apply(this.da,arguments);return y(this.da,this.db)}});d.a(["concat","join","slice"],function(g){var h=Array.prototype[g];c.prototype[g]=function(){return y(h.apply(this.da,arguments),this.db)}});c.prototype.Ob=function(){this.db=true;return this};c.prototype.value=function(){return this.da}})();if(typeof document.getAttribute=="undefined")document.getAttribute=v();if(typeof Node=="undefined")Node={ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12};if(_.K(window.jf))nglr={};if(_.K(window.Lb))angular={};if(_.K(angular.k))angular.k={};if(_.K(angular.filter))angular.filter={}; +if(_.K(window.console))window.console={log:v(),error:v()};if(_.K(nglr.alert))nglr.alert=function(){console.log(arguments);window.alert.apply(window,arguments)};nglr.Qb=function(a,b){var c=document.createElement("div");c.className=a;for(var e=a="",d=0;d<b.length;d++){var f=b[d];a+=e+(typeof f=="string"?f:nglr.m(f));e=" "}c.appendChild(document.createTextNode(a));nglr.hb.appendChild(c)};nglr.gc=function(a){return a&&a.tagName&&a.nodeName&&a.ownerDocument&&a.removeAttribute}; +nglr.ec=function(a){switch(a.nodeName){case "OPTION":case "PRE":case "TITLE":return true;default:return false}};nglr.L=v();nglr.yc=function(a,b){if(nglr.ec(a))if(nglr.Fa)a.innerText=b;else a.textContent=b;else a.innerHTML=b};nglr.mb=function(a){if(!a||!a.replace)return a;return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")};nglr.hd=function(a){if(!a||!a.replace)return a;return a.replace(/</g,"<").replace(/>/g,">").replace(/\"/g,""")}; +nglr.u=function(a,b){if(!a)throw"Missing this";if(!_.P(b))throw"Missing function";return function(){return b.apply(a,arguments)}};nglr.Qd=function(a,b){return function(){for(var c=[this],e=0;e<arguments.length;e++)c.push(arguments[e]);return b.apply(a,c)}};nglr.outerHTML=function(a){var b=document.createElement("div");b.appendChild(a);var c=b.innerHTML;b.removeChild(a);return c};nglr.Td=function(a){return a.replace(/^ */,"").replace(/ *$/,"")}; +nglr.xb=function(a){var b=(""+a).toLowerCase();if(b=="f"||b=="0"||b=="false"||b=="no")a=false;return!!a};nglr.Ea=function(a,b){for(var c in a){var e=b[c],d=typeof e;if(d=="undefined")b[c]=nglr.za(nglr.m(a[c]));else d=="object"&&e.constructor!=nglr.Mb&&c.substring(0,1)!="$"&&nglr.Ea(a[c],e)}};nglr.Va=function(a,b,c){this.document=jQuery(a);this.ac=jQuery(b);this.w=c;this.location=window.location}; +nglr.Va.prototype.load=function(){ba(this);B(this,"/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css");B(this,"/stylesheets/nglr.css");console.log("Server: "+this.w.C);jQuery.Id();nglr.Fa=jQuery.xe.Fa;ca(this);var a=this.w;if(!a.X){var b=a.C.match(/https?:\/\/([\w]*)/);a.X=b?b[1]:"$MEMORY"}da(this)}; +function ca(){console.log("Loader.configureJQueryPlugins()");jQuery.b.removeNode=function(){var a=this.i(0);a.parentNode.removeChild(a)};jQuery.b.scope=function(){for(var a=this;a&&a.i(0);){var b=a.data("scope");if(b)return b;a=a.parent()}return null};jQuery.b.Je=function(){return this.data("controller")||nglr.ba.od}}nglr.Va.prototype.Vd=function(){return""+(new Date).getTime()}; +function da(a){function b(){j.Yb(function(q){!q&&e.find("[ng-auth=eager]").length&&j.Da()})}console.log("Loader.bindHtml()");var c=new nglr.Hb(a.location),e=a.document,d=new nglr.Qc(a.w.C,a.w.X),f=new nglr.g(e[0],d,c,a.w);d.Jd=nglr.Qd(f,f.j);d=new nglr.M(e.find("body"),a.w.C);var i=a.w.X=="$MEMORY"?new nglr.ta(a.window):new nglr.Gb(a.w.C,jQuery.$b);i=new nglr.Jb(i,new nglr.$a(jQuery(e.body)),function(){f.c()});var j=new nglr.Ib(i,d),n="/data/"+a.w.X,m=new nglr.p(function(q,w){i.oa("POST",n,q,w)}, +j,f.anchor);f.Cc.push(function(){C(m)});var o=new nglr.h({ce:f.anchor,de:f,ee:a.w,fe:window.console,ge:m,sa:function(q){ea(m,o.I,q,f.anchor)},me:window,ke:a.Vd,le:j},"ROOT");jQuery.a(["get","set","eval","addWatchListener","updateView"],function(q,w){angular[w]=nglr.u(o,o[w])});e.data("scope",o);console.log("$binder.entity()");f.ha(o);console.log("$binder.compile()");f.compile();console.log("ControlBar.bind()");d.u();console.log("$users.fetchCurrentUser()");b();console.log("PopUp.bind()");(new nglr.A(e)).u(); +console.log("$binder.parseAnchor()");D(f);console.log("$binder.executeInit()");fa(f);console.log("$binder.updateView()");f.c();c.ic=nglr.u(f,f.Ld,c);c.kf=function(){nglr.alert("update")};c.watch();e.find("body").wb();console.log("ready()")} +function ba(){var a=window.location.href+"#";a=a.split("#")[1];var b={dd:null};a=a.split("&");for(var c=0;c<a.length;c++){var e=(a[c]+"=").split("=");b[e[0]]=e[1]}if(b.dd=="console"){nglr.hb=document.createElement("div");nglr.hb.id="ng-console";document.getElementsByTagName("body")[0].appendChild(nglr.hb);console.log=function(){nglr.Qb("ng-console-info",arguments)};console.error=function(){nglr.Qb("ng-console-error",arguments)}}} +function B(a,b){var c=document.createElement("link");c.rel="stylesheet";c.type="text/css";b.match(/^http:/)||(b=a.w.C+b);c.href=b;a.ac[0].appendChild(c)}nglr.Hb=function(a){this.location=a;this.lb=25;this.setTimeout=function(b,c){window.setTimeout(b,c)};this.ic=r();this.ia=a.href}; +nglr.Hb.prototype.watch=function(){var a=this;function b(){if(a.ia!==a.location.href){var c=a.location.hash.match(/^#\$iframe_notify=(.*)$/);if(c){a.ia.match(/#/)||(a.ia+="#");a.location.href=a.ia;c="_iframe_notify_"+c[1];var e=nglr[c];delete nglr[c];try{(e||nglr.L)()}catch(d){nglr.alert(d)}}else{a.ic(a.location.href);a.ia=a.location.href}}a.setTimeout(b,a.lb)}b()};angular.aa={Ud:function(a){var b=typeof a;switch(b){case "object":if(a===null)return"null";if(a instanceof Array)return"array";if(a instanceof Date)return"date";if(a.nodeType==1)return"element"}return b}};angular.Sa={};angular.Nc={}; +angular.La={md:function(a,b,c){var e=_.indexOf(a,b);if(c)e==-1&&a.push(b);else a.splice(e,1);return a},Bf:function(a,b){b=angular.$.compile(b);for(var c=0,e=0;e<a.length;e++){var d=1*b(a[e]);isNaN(d)||(c+=d)}return c},remove:function(a,b){var c=_.indexOf(a,b);c>=0&&a.splice(c,1);return b},find:function(a,b,c){if(b){var e=angular.$.compile(b);_.Ub(a,function(d){if(e(d)){c=d;return true}});return c}},Pe:function(a,b){return angular.La.find(a,function(c){return c.R==b},null)},filter:function(a,b){var c= +[];c.Zc=function(m){for(var o=0;o<c.length;o++)if(!c[o](m))return false;return true};var e=nglr.h.ja;function d(m,o){if(o.charAt(0)==="!")return!d(m,o.substr(1));switch(typeof m){case "boolean":case "number":case "string":return(""+m).toLowerCase().indexOf(o)>-1;case "object":for(var q in m)if(q.charAt(0)!=="$"&&d(m[q],o))return true;return false;case "array":for(q=0;q<m.length;q++)if(d(m[q],o))return true;return false;default:return false}}switch(typeof b){case "boolean":case "number":case "string":b= +{be:b};case "object":for(var f in b)f=="$"?function(){var m=(""+b[f]).toLowerCase();m&&c.push(function(o){return d(o,m)})}():function(){var m=f,o=(""+b[f]).toLowerCase();o&&c.push(function(q){return d(e(q,m),o)})}();break;case "function":c.push(b);break;default:return a}for(var i=[],j=0;j<a.length;j++){var n=a[j];c.Zc(n)&&i.push(n)}return i},add:function(a,b){a.push(_.K(b)?{}:b);return a},Ke:function(a,b){if(!b)return a.length;var c=angular.$.compile(b);return _.reduce(a,0,function(e,d){return e+ +(c(d)?1:0)})},lf:function(a,b,c){function e(f,i){return nglr.xb(i)?function(j,n){return f(n,j)}:f}function d(f,i){var j=typeof f,n=typeof i;if(j==n){if(j=="string")f=f.toLowerCase();if(j=="string")i=i.toLowerCase();if(f===i)return 0;return f<i?-1:1}else return j<n?-1:1}b=_.z(b)?b:[b];b=_.map(b,function(f){var i=false;if(typeof f=="string"&&(f.charAt(0)=="+"||f.charAt(0)=="-")){i=f.charAt(0)=="-";f=f.substring(1)}var j=f?angular.$.compile(f):_.ka;return e(function(n,m){return d(j(n),j(m))},i)});return _.fa(a).sort(e(function(f, +i){for(var j=0;j<b.length;j++){var n=b[j](f,i);if(n!=0)return n}return 0},c))},nf:function(a,b){var c=false,e=-1;_.Ub(a,function(d,f){if(d==b){c=true;e=f;return true}if((d.charAt(0)=="+"||d.charAt(0)=="-")&&d.substring(1)==b){c=d.charAt(0)=="+";e=f;return true}});e>=0&&a.splice(e,1);a.unshift((c?"-":"+")+b);return a},mf:function(a,b,c,e){c=c||"ng-ascend";e=e||"ng-descend";a=a[0]||"";var d=true;if(a.charAt(0)=="-"){a=a.substring(1);d=false}else if(a.charAt(0)=="+")a=a.substring(1);return a==b?d?c: +e:""},Ea:function(a,b,c){var e=a[b];if(!e){e={};a[b]=e}nglr.Ea(c,e);return a}}; +angular.U={quote:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/\t/g,"\\t").replace(/\v/g,"\\v")+'"'},tc:function(a){a=angular.U.quote(a);for(var b=[],c=0;c<a.length;c++){var e=a.charCodeAt(c);if(e<128)b.push(a.charAt(c));else{e="000"+e.toString(16);b.push("\\u"+e.substring(e.length-4))}}return b.join("")},Sd:function(a){var b;if(typeof a=="string"&&(b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){a= +new Date(0);a.setUTCFullYear(b[1],b[2]-1,b[3]);a.setUTCHours(b[4],b[5],b[6],0);return a}return a}};angular.Jc={toString:function(a){function b(c){return c<10?"0"+c:c}return a.getUTCFullYear()+"-"+b(a.getUTCMonth()+1)+"-"+b(a.getUTCDate())+"T"+b(a.getUTCHours())+":"+b(a.getUTCMinutes())+":"+b(a.getUTCSeconds())+"Z"}};angular.$={compile:function(a){if(_.P(a))return a;else if(a){var b=new nglr.h;return function(c){b.I=c;return b.eval(a)}}else return r()}}; +(function(){function a(b,c,e){_.extend(b,c);_.a(e||[],function(d){b[d]=_[d]})}a(angular.aa,{},["extend","clone","isEqual","isElement","isArray","isFunction","isUndefined"]);a(angular.Sa,angular.aa,["each","map","reduce","reduceRight","detect","select","reject","all","any","include","invoke","pluck","max","min","sortBy","sortedIndex","toArray","size"]);a(angular.La,angular.Sa,["first","last","compact","flatten","without","uniq","intersect","zip","indexOf","lastIndexOf"]);a(angular.Nc,angular.Sa,["keys", +"values"]);a(angular.U,angular.aa);a(angular.$,angular.aa,["bind","bindAll","delay","defer","wrap","compose"])})();nglr.g=function(a,b,c,e){this.F=a;this.Lf=c;this.anchor={};this.$d=b;this.w=e||{};this.Cc=[]};nglr.g.Ha=function(a){for(var b=[],c=0,e;(e=a.indexOf("{{",c))>-1;){c<e&&b.push(a.substr(c,e-c));c=e;e=a.indexOf("}}",e);e=e<0?a.length:e+2;b.push(a.substr(c,e-c));c=e}c!=a.length&&b.push(a.substr(c,a.length-c));return b.length===0?[a]:b};nglr.g.ld=function(a){a=nglr.g.Ha(a);return a.length>1||nglr.g.ea(a[0])!==null};nglr.g.ea=function(a){return(a=a.replace(/\n/gm," ").match(/^\{\{(.*)\}\}$/))?a[1]:null}; +function ga(a,b){var c={};b.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,function(e,d,f){if(d)c[decodeURIComponent(d)]=decodeURIComponent(f)});return c}function D(a,b){b=b||window.location.href;var c=b.indexOf("#");if(!(c<0)){b=b.substring(c+1);b=ga(a,b);jQuery.a(a.anchor,function(e){delete a.anchor[e]});jQuery.a(b,function(e,d){a.anchor[e]=d})}}nglr.g.prototype.Ld=function(a){console.log("URL change detected",a);D(this,a);this.c()}; +function ha(a){var b=window.location.href,c=b.indexOf("#");if(c>-1)b=b.substring(0,c);b+="#";c="";for(var e in a.anchor){var d=a.anchor[e];if(typeof d==="undefined"||d===null)delete a.anchor[e];else{b+=c+encodeURIComponent(e);if(d!==true)b+="="+encodeURIComponent(d);c="&"}}a=b;e=window.location.href;e.match(/#/)||(e+="#");if(e!=a)window.location.href=a;self.Oe=a;return b} +nglr.g.prototype.c=function(){(new Date).getTime();var a=jQuery(this.F).scope();E(a,"$invalidWidgets",[]);a.c();(new Date).getTime();ha(this);_.a(this.Cc,function(b){b()})};function fa(a){jQuery("[ng-init]",a.F).a(function(){var b=jQuery(this),c=b.scope();try{c.eval(b.e("ng-init"))}catch(e){nglr.alert("EVAL ERROR:\n"+b.e("ng-init")+"\n"+nglr.m(e,true))}})} +nglr.g.prototype.ha=function(a){jQuery("[ng-entity]",this.F).e("ng-watch",function(){try{var b=jQuery(this);return a.ha(b.e("ng-entity"))+(b.e("ng-watch")||"")}catch(c){nglr.alert(c)}})}; +nglr.g.prototype.compile=function(){var a=jQuery(this.F),b=this;if(this.w.Xc){var c=jQuery(":submit",this.F).rc("[ng-action]");c.e("ng-action","$save()");c.rc(":disabled").rc("ng-bind-attr").e("ng-bind-attr",'{disabled:"{{$invalidWidgets}}"}')}F(this,this.F)(this.F,a.scope(),"");jQuery("a[ng-action]",this.F).vd("click",function(){var e=jQuery(this);try{e.scope().eval(e.e("ng-action"));e.vc("ng-error");e.wc("ng-exception")}catch(d){e.fb("ng-exception");e.e("ng-error",nglr.m(d,true))}b.c();return false})}; +function ia(a,b,c,e){a=c.concat();c=a.pop();var d=nglr.g.Ha(b.nodeValue);if(d.length>1||nglr.g.ea(d[0])){var f=b.parentNode;if(nglr.ec(f)){f.setAttribute("ng-bind-template",b.nodeValue);e.push({path:a,b:function(o){return new nglr.S(o,o.getAttribute("ng-bind-template"))}})}else for(var i=0;i<d.length;i++){var j=d[i],n=nglr.g.ea(j),m;if(n){m=document.createElement("span");jQuery(m).e("ng-bind",n);i===0&&e.push({path:a.concat(c+i),b:nglr.g.prototype.qc})}else if(nglr.Fa&&j.charAt(0)==" "){m=document.createElement("span"); +m.innerHTML=" "+j.substring(1)}else m=document.createTextNode(j);f.insertBefore(m,b)}f.removeChild(b)}}function F(a,b){var c=[];G(a,b,[],c);return function(e,d,f){for(var i=c.length,j=0;j<i;j++){for(var n=c[j],m=e,o=n.path,q=0;q<o.length;q++)m=m.childNodes[o[q]];try{var w=n.b(m,d,f);w&&d.Ec.push(w)}catch(t){nglr.alert(t)}}}} +function G(a,b,c,e){var d=b.nodeType;if(d==Node.TEXT_NODE)ia(a,b,c,e);else if(!(d!=Node.ELEMENT_NODE&&d!=Node.DOCUMENT_NODE))if(b.getAttribute){d=b.getAttribute("ng-non-bindable");if(!(d||d==="")){if(d=b.attributes){var f=b.getAttribute("ng-bind-attr");b.removeAttribute("ng-bind-attr");f=f?nglr.za(f):{};for(var i=d.length,j=0;j<i;j++){var n=d[j],m=n.name;n=nglr.Fa&&m=="href"?decodeURI(b.getAttribute(m,2)):n.value;if(nglr.g.ld(n))f[m]=n}d=nglr.m(f);d.length>2&&b.setAttribute("ng-bind-attr",d)}b.getAttribute|| +console.log(b);var o=b.getAttribute("ng-repeat");if(o){b.removeAttribute("ng-repeat");var q=F(a,b);d=document.createComment("ng-repeat: "+o);f=b.parentNode;f.insertBefore(d,b);f.removeChild(b);var w=function(t,y,g){var h=jQuery(b).fa();h.kb("display","");h.e("ng-repeat-index",""+g);h.data("scope",t);q(h[0],t,y+g+":");return h};e.push({path:c,b:function(t,y,g){return new nglr.Xa(jQuery(t),o,w,g)}})}else{b.getAttribute("ng-eval")&&e.push({path:c,b:a.Dd});b.getAttribute("ng-bind")&&e.push({path:c,b:a.qc}); +b.getAttribute("ng-bind-attr")&&e.push({path:c,b:a.zd});b.getAttribute("ng-hide")&&e.push({path:c,b:a.Ed});b.getAttribute("ng-show")&&e.push({path:c,b:a.Fd});b.getAttribute("ng-class")&&e.push({path:c,b:a.Ad});b.getAttribute("ng-class-odd")&&e.push({path:c,b:a.Cd});b.getAttribute("ng-class-even")&&e.push({path:c,b:a.Bd});b.getAttribute("ng-style")&&e.push({path:c,b:a.Gd});b.getAttribute("ng-watch")&&e.push({path:c,b:a.Hd});d=b.nodeName;if(d=="INPUT"||d=="TEXTAREA"||d=="SELECT"||d=="BUTTON")e.push({path:c, +b:function(t,y,g){t.name=g+t.name.split(":").pop();return ja(a.$d,jQuery(t),y)}});if(d=="OPTION")if(!jQuery("<select/>").append(jQuery(b).fa()).B().match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi))b.value=b.text;d=b.childNodes;for(f=0;f<d.length;f++)G(a,d[f],c.concat(f),e)}}}}A=nglr.g.prototype;A.Dd=function(a){return new nglr.Ta(a,a.getAttribute("ng-eval"))};A.qc=function(a){return new nglr.S(a,"{{"+a.getAttribute("ng-bind")+"}}")};A.zd=function(a){return new nglr.Ma(a,nglr.za(a.getAttribute("ng-bind-attr")))}; +A.Ed=function(a){return new nglr.Ua(a,a.getAttribute("ng-hide"))};A.Fd=function(a){return new nglr.Za(a,a.getAttribute("ng-show"))};A.Ad=function(a){return new nglr.Ra(a,a.getAttribute("ng-class"))};A.Bd=function(a){return new nglr.Pa(a,a.getAttribute("ng-class-even"))};A.Cd=function(a){return new nglr.Qa(a,a.getAttribute("ng-class-odd"))};A.Gd=function(a){return new nglr.ab(a,a.getAttribute("ng-style"))};A.Hd=function(a,b){b.watch(a.getAttribute("ng-watch"))};nglr.M=function(a,b){this.document=a;this.Ja=b;this.window=window;this.W=[]};nglr.M.prototype.u=v();nglr.M.ne='<div><div class="ui-widget-overlay"></div><div id="ng-login" ng-non-bindable="true"><div class="ng-login-container"></div></div></div>';nglr.M.prototype.Da=function(a){this.W.push(a);this.W.length==1&&H(this,"/user_session/new.mini?return_url="+encodeURIComponent(this.window.location.href.split("#")[0]))};nglr.M.prototype.oc=function(a){this.W.push(a);this.W.length==1&&H(this,"/user_session/do_destroy.mini")}; +function H(a,b){var c=(new Date).getTime(),e=a.window.location.href.split("#")[0];e+="#$iframe_notify="+c;var d=jQuery('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+e+'" src="'+a.Ja+b+'" width="500" height="330"/></div>');a.document.append(d);d.Vb({height:363,width:500,wf:false,wd:true,title:'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>'});nglr["_iframe_notify_"+c]=function(){d.Vb("destroy");d.remove();jQuery.a(a.W,function(f,i){i()});a.W=[]}} +nglr.M.Lc='<div ng-non-bindable="true" title="Permission Error:">Sorry, you do not have permission for this!</div>';nglr.M.prototype.sb=function(){if(!this.Zb){this.Zb=jQuery(nglr.M.Lc);this.Zb.Vb({ve:true,height:70,wd:true})}};nglr.p=function(a,b,c){this.sc=a;this.Dc=b;this.V={qa:[]};this.anchor=c;this.wa=[]};function I(a,b){if(b.constructor!=nglr.N)throw"Parameter must be an instance of Entity! "+nglr.m(b);var c=b.ra+"/"+b.R,e=a.V[c];if(e)nglr.N.jb(b,e);else e=a.V[c]=b;return e}nglr.p.prototype.load=function(a,b,c,e){if(b&&b!=="*"){var d=this;J(this,["GET",a.ra+"/"+b],function(f){K(a,f);L(a);f=a.pa(a);I(d,f);(c||nglr.L)(a)},e)}return a}; +nglr.p.prototype.pb=function(a,b,c){var e=this,d=[],f=0;jQuery.a(b,function(i,j){d.push(e.load(a(),j,function(){f++;if(f==b.length)(c||nglr.L)(d)}))});return d};nglr.p.prototype.kc=function(a,b,c){return this.load(a,b,c,function(e){if(e.Ka==404){a.R=b;(c||nglr.L)(a)}else throw e;})};function ka(a,b,c){var e=[];e.Cb=function(d){return d.ra==b.title};a.V.qa.push(e);J(a,["GET",b.title],function(d){for(var f=0;f<d.length;f++){var i=b();K(i,d[f]);e.push(I(a,i))}(c||nglr.L)(e)});return e} +nglr.p.prototype.save=function(a,b){var c=this,e={};M(a,e);J(this,["POST","",e],function(d){K(a,d);var f=I(c,a);_.a(c.V.qa,function(i){i.Cb(a)&&angular.La.md(i,f,true)});if(a.Db)c.anchor[a.Db]=a.R;b&&b(a)})};nglr.p.prototype.remove=function(a,b){var c=this,e={};M(a,e);J(this,["DELETE","",e],function(d){delete c.V[a.ra+"/"+a.R];_.a(c.V.qa,function(f){for(var i=0;i<f.length;i++)f[i].R==a.R&&f.splice(i,1)});(b||nglr.L)(d)})};function J(a,b,c,e){b.Fc=c;b.Hc=e||function(d){throw d;};a.wa.push(b)} +function C(a){function b(d,f){console.log("RESPONSE["+d+"]: ",f);if(f.Ka==401)c.Dc.Da(function(){c.sc(e,b)});else if(f.Ka)nglr.alert(nglr.m(f));else for(var i=0;i<f.length;i++){var j=f[i],n=e[i];if(d=j.Ka)d==403?c.Dc.sb():n.Hc(j);else n.Fc(j)}}if(a.wa.length!==0){var c=a,e=a.wa;a.wa=[];console.log("REQUEST:",e);a.sc(e,b)}}function ea(a,b,c){function e(){d--;d===0&&c&&c()}var d=1;for(var f in b)if((a=b[f])&&a.sa==nglr.N.prototype.sa){d++;a.sa(e)}e()} +nglr.p.prototype.ma=function(a,b,c,e){var d=this,f=[];f.Cb=z(false);this.V.qa.push(f);J(this,["GET",a.title+"/"+b+"="+c],function(i){for(var j=0;j<i.length;j++){var n=K(new a,i[j]);f.push(I(d,n))}e&&e(f)});return f};nglr.p.ca=v();nglr.p.ca.all=function(){return[]};nglr.p.ca.ma=function(){return[]};nglr.p.ca.load=function(){return{}};nglr.p.ca.title=undefined; +nglr.p.prototype.ha=function(a,b){if(!a)return nglr.p.ca;var c=this;function e(d){return new nglr.N(e,d)}e.title=a;e.Gc=true;e.Tb=this;e.ed=b||{};e.load=function(d,f){return c.load(e(),d,f)};e.pb=function(d,f){return c.pb(e,d,f)};e.kc=function(d,f){return c.kc(e(),d,f)};e.all=function(d){return ka(c,e,d)};e.ma=function(d,f,i){return c.ma(e,d,f,i)};e.tf=function(d){J(c,["GET",a+"/$properties"],d)};return e}; +nglr.p.prototype.join=function(a){function b(){throw"Joined entities can not be instantiated into a document.";}var c=_(a).Ob().map(function(e,d){return d}).Rd(function(e){var d=[];do{if(_(d).Aa(e))throw"Infinite loop in join: "+d.join(" -> ");d.push(e);if(!a[e])throw _("Named entity '<%=name%>' is undefined.").Z({name:e});e=a[e].Ga?a[e].Ga.substring(0,a[e].Ga.indexOf(".")):undefined}while(e);return d.length}).value();if(_(c).select(function(e){return a[e].Ga}).length!=c.length-1)throw"Exactly one entity needs to be primary."; +b.ma=function(e,d){var f=[],i=e?e.substring(0,e.indexOf(".")):undefined;if(i!=c[0])throw _("Named entity '<%=name%>' is not a primary entity.").Z({name:i});var j=1;a[i].join.ma(e.substring(e.indexOf(".")+1),d,function(n){var m=c[j++],o=a[m],q=o.Ga,w={};_(n).a(function(t){var y={};f.push(y);y[i]=t;t=nglr.h.ja(y,q);w[t]=t});o.join.pb(_.D(w),function(t){var y={};_(t).a(function(g){y[g.R]=g});_(f).a(function(g){var h=nglr.h.ja(g,q);g[m]=y[h]})})});return f};return b};angular.filter.s=function(a){if(a)for(var b in a)this[b]=a[b]};angular.filter.s.i=function(a,b){b=b||"text";switch(typeof a){case "string":return b=="text"?a:undefined;case "object":if(a&&typeof a[b]!=="undefined")return a[b];return;default:return a}};angular.filter.Le=function(a){jQuery(this.element).zb("ng-format-negative",a<0);return"$"+angular.filter.tb.apply(this,[a,2])}; +angular.filter.tb=function(a,b){if(isNaN(a)||!isFinite(a))return"";b=typeof b=="undefined"?2:b;var c=a<0;a=Math.abs(a);var e=Math.pow(10,b);a=""+Math.round(a*e);var d=a.substring(0,a.length-b);d=d||"0";e=a.substring(a.length-b);a=c?"-":"";for(c=0;c<d.length;c++){if((d.length-c)%3===0&&c!==0)a+=",";a+=d.charAt(c)}if(b>0){for(c=e.length;c<b;c++)e+="0";a+="."+e.substring(0,b)}return a};angular.filter.bd=v();angular.filter.rd=function(a){jQuery(this.element).fb("ng-monospace");return nglr.m(a,true)}; +angular.filter.Ac=function(a,b){a=nglr.Td(a);for(var c=a.replace(/ /g,""),e=angular.filter.Ac.Mc,d=0;d<e.length;d++)for(var f=e[d],i=0;i<f.na.length;i++)if(f.na[i].test(c)){b=f.name+": "+a;f=f.url+a;return new angular.filter.s({text:b,url:f,B:'<a href="'+nglr.hd(f)+'">'+b+"</a>",Ef:a})}return a?b||new angular.filter.s({text:a+" is not recognized"}):null}; +angular.filter.Ac.Mc=[{name:"UPS",url:"http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=en_US&track.x=0&track.y=0&InquiryNumber1=",na:[/^1Z[0-9A-Z]{16}$/i]},{name:"FedEx",url:"http://www.fedex.com/Tracking?tracknumbers=",na:[/^96\d{10}?$/i,/^96\d{17}?$/i,/^96\d{20}?$/i,/^\d{15}$/i,/^\d{12}$/i]},{name:"USPS",url:"http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?origTrackNum=",na:[/^(91\d{20})$/i,/^(91\d{18})$/i]}]; +angular.filter.link=function(a,b){b=b||angular.filter.s.i(a);var c=angular.filter.s.i(a,"url")||angular.filter.s.i(a);if(c){if(angular.k.gd(c)===null)c="mailto:"+c;a='<a href="'+nglr.mb(c)+'">'+b+"</a>";return new angular.filter.s({text:b,url:c,B:a})}return a};angular.filter.gb=function(a){if(a===null)return"";for(var b=0;a>1E3;){a/=1024;b++}a=""+a;var c=a.indexOf(".");if(c>-1&&c+2<a.length)a=a.substring(0,c+2);return a+" "+angular.filter.gb.Pc[b]}; +angular.filter.gb.Pc=["bytes","KB","MB","GB","TB","PB"];angular.filter.Ve=function(a,b,c){if(a&&a.url){var e="";if(b)e=' style="max-width: '+b+"px; max-height: "+(c||b)+'px;"';return new angular.filter.s({url:a.url,text:a.url,B:'<img src="'+a.url+'"'+e+"/>"})}return null};angular.filter.ff=function(a){return(a=angular.filter.s.i(a))?(""+a).toLowerCase():a};angular.filter.Kf=function(a){return(a=angular.filter.s.i(a))?(""+a).toUpperCase():a}; +angular.filter.ef=function(a){a=angular.filter.s.i(a);if(a===""||!a)return 1;return a.split(/\n|\f/).length};angular.filter["if"]=function(a,b){return b?a:undefined};angular.filter.If=function(a,b){return b?undefined:a};angular.filter.l=function(a,b,c,e){b=b||{};var d=angular.filter.l;a={$c:a,ze:d.Pb(b,"color"),Fe:d.title(b),Be:d.Pb(b,"label"),Ae:d.Ab(b),Ce:"bg,s,FFFFFF00"};if(_.z(b.ae)){a.He="x";a.Ge="0:|"+b.ae.join("|")}return angular.filter.l.Wb(a,c,e)}; +angular.filter.l.Ab=function(a){var b=[];_.a(a.Pd||[],function(c){var e=[];_.a(c.Ab||[],function(d){e.push(d)});b.push(e.join(","))});a=b.join("|");return a===""?null:"t:"+a};angular.filter.l.title=function(a){var b=[];a=a.title||[];_.a(_.z(a)?a:[a],function(c){b.push(encodeURIComponent(c))});return b.join("|")}; +angular.filter.l.Pb=function(a,b){var c=[],e=0;_.a(a.Pd||[],function(d){var f=[];d=d[b]||[];_.a(_.z(d)?d:[d],function(i){f.push(encodeURIComponent(i));e++});c.push(f.join("|"))});return e?c.join(","):null};angular.filter.l.Wb=function(a,b,c){b=b||200;c=c||b;var e="http://chart.apis.google.com/chart?",d=[];a.Ee=b+"x"+c;for(var f in a){var i=a[f];i&&d.push(f+"="+i)}d.sort();e+=d.join("&");return new angular.filter.s({url:e,text:i,B:'<img width="'+b+'" height="'+c+'" src="'+e+'"/>'})}; +angular.filter.uf=function(a,b,c){return angular.filter.l.Wb({$c:"qr",De:encodeURIComponent(a)},b,c)}; +angular.filter.ye={qf:function(a,b,c){return angular.filter.l("p",a,b,c)},rf:function(a,b,c){return angular.filter.l("p3",a,b,c)},sf:function(a,b,c){return angular.filter.l("pc",a,b,c)},se:function(a,b,c){return angular.filter.l("bhs",a,b,c)},re:function(a,b,c){return angular.filter.l("bhg",a,b,c)},ue:function(a,b,c){return angular.filter.l("bvs",a,b,c)},te:function(a,b,c){return angular.filter.l("bvg",a,b,c)},df:function(a,b,c){return angular.filter.l("lc",a,b,c)},zf:function(a,b,c){return angular.filter.l("ls", +a,b,c)},xf:function(a,b,c){return angular.filter.l("s",a,b,c)}};angular.filter.B=function(a){return new angular.filter.s({B:a})};nglr.Mb=[].constructor;nglr.m=function(a,b){var c=[];nglr.yb(c,a,b?"\n  ":null);return c.join("")};nglr.Df=function(a){return nglr.m(a,true)};nglr.za=function(a){try{var b=new nglr.q(a,true),c=N(b);O(b);return c()}catch(e){console.error("fromJson error: ",a,e);throw e;}}; +nglr.yb=function(a,b,c){var e=typeof b;if(b===null)a.push("null");else if(e!=="function")if(e==="boolean")a.push(""+b);else if(e==="number")isNaN(b)?a.push("null"):a.push(""+b);else if(e==="string")return a.push(angular.U.tc(b));else if(e==="object")if(b instanceof Array){a.push("[");var d=b.length;e=false;for(var f=0;f<d;f++){var i=b[f];e&&a.push(",");typeof i=="function"||typeof i=="undefined"?a.push("null"):nglr.yb(a,i,c);e=true}a.push("]")}else if(b instanceof Date)a.push(angular.U.tc(angular.Jc.toString(b))); +else{a.push("{");c&&a.push(c);e=false;f=c?c+"  ":false;i=[];for(var j in b)j.indexOf("$$")!==0&&i.push(j);i.sort();for(j=0;j<i.length;j++){var n=i[j];try{d=b[n];if(typeof d!="function"){if(e){a.push(",");c&&a.push(c)}a.push(angular.U.quote(n));a.push(":");nglr.yb(a,d,f);e=true}}catch(m){}}a.push("}")}};nglr.N=function(a,b){this.pa=a;K(this,b||{});this.ra=a.title;L(this)};nglr.N.jb=function(a,b){if(!(a===b||!a||!b)){var c=function(d,f,i){return i.substring(0,2)!=="$$"&&typeof d[i]!=="function"&&typeof f[i]!=="function"};for(var e in b)c(a,b,e)&&delete b[e];for(e in a)if(c(a,b,e))b[e]=a[e]}};function L(a){nglr.Ea(a.pa.ed,a);return a}nglr.N.prototype.sa=function(a){this.pa.Tb.save(this,a===true?undefined:a);a===true&&C(this.pa.Tb);return this};function K(a,b){nglr.N.jb(b,a);return a} +function M(a,b){nglr.N.jb(a,b);return a};nglr.J=function(a,b){this.text=a;this.cd=b?20:-1;this.o=[];this.index=0}; +nglr.J.Eb={"null":z(null),"true":z(true),"false":z(false),"+":function(a,b,c){return(b||0)+(c||0)},"-":function(a,b,c){return(b||0)-(c||0)},"*":function(a,b,c){return b*c},"/":function(a,b,c){return b/c},"%":function(a,b,c){return b%c},"^":function(a,b,c){return b^c},"=":function(a,b,c){return E(a.scope,b,c)},"==":function(a,b,c){return b==c},"!=":function(a,b,c){return b!=c},"<":function(a,b,c){return b<c},">":function(a,b,c){return b>c},"<=":function(a,b,c){return b<=c},">=":function(a,b,c){return b>= +c},"&&":function(a,b,c){return b&&c},"||":function(a,b,c){return b||c},"&":function(a,b,c){return b&c},"|":function(a,b,c){return c(a,b)},"!":function(a,b){return!b}};nglr.J.prototype.Y=function(){return this.index+1<this.text.length?this.text.charAt(this.index+1):false}; +nglr.J.prototype.parse=function(){for(var a=this.o,b=nglr.J.Eb,c=true;this.index<this.text.length;){var e=this.text.charAt(this.index);if(e=='"'||e=="'"){la(this,e);c=true}else if(e=="("||e=="["){a.push({index:this.index,text:e});this.index++}else if(e=="{"){c=this.Y();if(c==":"||c=="("){a.push({index:this.index,text:e+c});this.index++}else a.push({index:this.index,text:e});this.index++;c=true}else if(e==")"||e=="]"||e=="}"){a.push({index:this.index,text:e});this.index++;c=false}else if(e==":"||e== +"."||e==","||e==";"){a.push({index:this.index,text:e});this.index++;c=true}else if(c&&e=="/"){ma(this);c=false}else if("0"<=e&&e<="9"){na(this);c=false}else if(P(this,e)){Q(this);c=false}else if(e==" "||e=="\r"||e=="\t"||e=="\n"||e=="\u000b")this.index++;else{c=e+this.Y();var d=b[e],f=b[c];if(f){a.push({index:this.index,text:c,b:f});this.index+=2}else if(d){a.push({index:this.index,text:e,b:d});this.index+=1}else throw"Lexer Error: Unexpected next character ["+this.text.substring(this.index)+"] in expression '"+ +this.text+"' at column '"+(this.index+1)+"'.";c=true}}return a};function P(a,b){return"a"<=b&&b<="z"||"A"<=b&&b<="Z"||"_"==b||b=="$"}function na(a){for(var b="",c=a.index;a.index<a.text.length;){var e=a.text.charAt(a.index);if(e=="."||"0"<=e&&e<="9")b+=e;else break;a.index++}b=1*b;a.o.push({index:c,text:b,b:function(){return b}})} +function Q(a){for(var b="",c=a.index;a.index<a.text.length;){var e=a.text.charAt(a.index);if(e=="."||P(a,e)||"0"<=e&&e<="9")b+=e;else break;a.index++}e=nglr.J.Eb[b];if(!e){e=function(d){return d.scope.i(b)};e.Ba=b}a.o.push({index:c,text:b,b:e})}nglr.J.Kc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'}; +function la(a,b){var c=a.index,e=a.cd;a.index++;for(var d="",f=false;a.index<a.text.length;){var i=a.text.charAt(a.index);if(f){if(i=="u"){i=a.text.substring(a.index+1,a.index+5);a.index+=4;d+=String.fromCharCode(parseInt(i,16))}else{f=nglr.J.Kc[i];d+=f?f:i}f=false}else if(i=="\\")f=true;else if(i==b){a.index++;a.o.push({index:c,text:d,b:function(){return d.length==e?angular.U.Sd(d):d}});return}else d+=i;a.index++}throw"Lexer Error: Unterminated quote ["+a.text.substring(c)+"] starting at column '"+ +(c+1)+"' in expression '"+a.text+"'.";} +function ma(a){var b=a.index;a.index++;for(var c="",e=false;a.index<a.text.length;){var d=a.text.charAt(a.index);if(e){c+=d;e=false}else if(d==="\\"){c+=d;e=true}else if(d==="/"){a.index++;e="";if(P(a,a.text.charAt(a.index))){Q(a);e=a.o.pop().text}var f=new RegExp(c,e);a.o.push({index:b,text:c,Qe:e,b:function(){return f}});return}else c+=d;a.index++}throw"Lexer Error: Unterminated RegExp ["+a.text.substring(b)+"] starting at column '"+(b+1)+"' in expression '"+a.text+"'.";} +nglr.q=function(a,b){this.text=a;this.o=(new nglr.J(a,b)).parse();this.index=0};nglr.q.Rc=z(0);nglr.q.prototype.error=function(a,b){throw"Token '"+b.text+"' is "+a+" at column='"+(b.index+1)+"' of expression '"+this.text+"' starting at '"+this.text.substring(b.index)+"'.";};function R(a){if(a.o.length===0)throw"Unexpected end of expression: "+a.text;return a.o[0]}nglr.q.prototype.Y=function(a,b,c,e){var d=this.o;if(d.length>0){d=d[0];var f=d.text;if(f==a||f==b||f==c||f==e||!a&&!b&&!c&&!e)return d}return false}; +function S(a,b,c,e,d){if(b=a.Y(b,c,e,d)){a.o.shift();return a.Me=b}return false}function T(a,b){if(!S(a,b)){var c=a.Y();throw"Expecting '"+b+"' at column '"+(c.index+1)+"' in '"+a.text+"' got '"+a.text.substring(c.index)+"'.";}}function oa(a,b,c){var e=c.apply(a);return function(d){return b(d,e(d))}}function U(a,b,c,e){var d=e.apply(a);return function(f){return c(f,b(f),d(f))}} +function O(a){if(a.o.length!==0)throw"Did not understand '"+a.text.substring(a.o[0].index)+"' while evaluating '"+a.text+"'.";}function V(a){for(var b=[];;){a.o.length>0&&!a.Y("}",")",";","]")&&b.push(W(a));if(!S(a,";"))return function(c){for(var e,d=0;d<b.length;d++){var f=b[d];if(f)e=f(c)}return e}}}function W(a){for(var b=a.G(),c;;)if(c=S(a,"|"))b=U(a,b,c.b,a.filter);else return b}nglr.q.prototype.filter=function(){return X(this,angular.filter)};nglr.q.prototype.k=function(){return X(this,angular.k)}; +function X(a,b){for(var c=pa(a,b),e=[];;)if(S(a,":"))e.push(a.G());else{var d=function(f,i){i=[i];for(var j=0;j<e.length;j++)i.push(e[j](f));return c.apply(f,i)};return function(){return d}}}nglr.q.prototype.G=function(){return qa(this)};function qa(a){if(S(a,"throw")){var b=Y(a);return function(c){throw b(c);}}else return Y(a)} +function Y(a){var b=a.nc(),c;if(c=S(a,"=")){if(!b.Ba)throw"Left hand side '"+a.text.substring(0,c.index)+"' of assignment '"+a.text.substring(c.index)+"' is not assignable.";return U(a,function(){return b.Ba},c.b,a.nc)}else return b}A=nglr.q.prototype;A.nc=function(){for(var a=this.mc(),b;;)if(b=S(this,"||"))a=U(this,a,b.b,this.mc);else return a};A.mc=function(){for(var a=this.pc(),b;;)if(b=S(this,"&&"))a=U(this,a,b.b,this.pc);else return a}; +A.pc=function(){var a;return(a=S(this,"!"))?oa(this,a.b,this.Xb):this.Xb()};A.Xb=function(){for(var a=this.uc(),b;;)if(b=S(this,"==","!="))a=U(this,a,b.b,this.uc);else return a};A.uc=function(){for(var a=this.Kb(),b;;)if(b=S(this,"<",">","<=",">="))a=U(this,a,b.b,this.Kb);else return a};A.Kb=function(){for(var a=this.rb(),b;b=S(this,"+","-");)a=U(this,a,b.b,this.rb);return a};A.rb=function(){for(var a=this.Bc(),b;b=S(this,"*","/","%");)a=U(this,a,b.b,this.Bc);return a}; +A.Bc=function(){var a;return S(this,"+")?N(this):(a=S(this,"-"))?U(this,nglr.q.Rc,a.b,this.rb):N(this)};function pa(a,b){var c=S(a),e=c.text.split(".");b=b;for(var d,f=0;f<e.length;f++){d=e[f];if(b)b=b[d]}if(typeof b!="function")throw"Function '"+c.text+"' at column '"+(c.index+1)+"' in '"+a.text+"' is not defined.";return b} +function N(a){var b;if(S(a,"(")){b=W(a);T(a,")");b=b}else if(S(a,"["))b=ra(a);else if(S(a,"{"))b=a.object();else if(S(a,"{:"))b=sa(a,false);else if(S(a,"{("))b=sa(a,true);else{var c=S(a);(b=c.b)||a.error("not a primary expression",c)}for(;c=S(a,"(","[",".");)if(c.text==="(")b=ta(a,b);else if(c.text==="[")b=ua(a,b);else if(c.text===".")b=va(a,b);else throw"IMPOSSIBLE";return b} +function sa(a,b){var c=[];if(b){if(!S(a,")")){for(c.push(S(a).text);S(a,",");)c.push(S(a).text);T(a,")")}T(a,":")}var e=V(a);T(a,"}");return function(d){return function(f){var i=new nglr.h(d.scope.I);E(i,"$",f);for(var j=0;j<c.length;j++)E(i,c[j],arguments[j]);return e({scope:i})}}}function va(a,b){var c=S(a).text;function e(d){return nglr.h.ja(b(d),c)}e.Ba=c;return e} +function ua(a,b){var c=a.G();T(a,"]");if(S(a,"=")){var e=a.G();return function(d){return b(d)[c(d)]=e(d)}}else return function(d){var f=b(d);d=c(d);return f?f[d]:undefined}}function ta(a,b){var c=[];if(R(a).text!=")"){do c.push(a.G());while(S(a,","))}T(a,")");return function(e){for(var d=[],f=0;f<c.length;f++)d.push(c[f](e));f=b(e);if(typeof f==="function")return f.apply(e,d);else throw"Expression '"+b.Ba+"' is not a function.";}} +function ra(a){var b=[];if(R(a).text!="]"){do b.push(a.G());while(S(a,","))}T(a,"]");return function(c){for(var e=[],d=0;d<b.length;d++)e.push(b[d](c));return e}}nglr.q.prototype.object=function(){var a=[];if(R(this).text!="}"){do{var b=S(this).text;T(this,":");var c=this.G();a.push({sd:b,value:c})}while(S(this,","))}T(this,"}");return function(e){for(var d={},f=0;f<a.length;f++){var i=a[f],j=i.value(e);d[i.sd]=j}return d}}; +function wa(a){for(var b=[];a.o.length>0;){b.push(xa(a));S(a,";")||O(a)}return function(c){for(var e="",d=0;d<b.length;d++)e+=b[d](c);return e}}function xa(a){var b=S(a).text,c,e;if(S(a,"=")){c=b;b=S(a).text}if(S(a,":"))e=N(a)(null);return function(d){var f=d.scope.i("$datastore").ha(b,e);E(d.scope,b,f);if(c){f=f();f.Db=c;E(d.scope,c,f);return"$anchor."+c+":{"+c+"="+b+".load($anchor."+c+");"+c+".$$anchor="+angular.U.quote(c)+";};"}else return""}} +nglr.q.prototype.watch=function(){for(var a=[];this.o.length>0;){a.push(ya(this));S(this,";")||O(this)}O(this);return function(b){for(var c=0;c<a.length;c++){var e=a[c](b);b.Sc(e.name,e.b)}}};function ya(a){var b=S(a).text;T(a,":");var c;if(R(a).text=="{"){T(a,"{");c=V(a);T(a,"}")}else c=a.G();return function(){return{name:b,b:c}}};nglr.h=function(a,b){this.Ec=[];this.Bb={};this.name=b;a=a||{};function c(){}c.prototype=a;this.I=new c;this.I.ie=a;if(b=="ROOT")this.I.je=this.I};nglr.h.ya={};nglr.h.prototype.c=function(){var a=this;za(this);_.a(this.Ec,function(b){Z(a,b,"",{},function(){this.c(a)})})};function Aa(a,b){for(a=0;a<b.length;a++){var c=b.charAt(a);if(c!="."&&!P(nglr.J.prototype,c))return false}return true} +nglr.h.ja=function(a,b){if(!b)return a;for(var c=b.split("."),e,d=a,f=c.length,i=0;i<f;i++){e=c[i];if(!e.match(/^[\$\w][\$\w\d]*$/))throw"Expression '"+b+"' is not a valid expression for accesing variables.";if(a){d=a;a=a[e]}if(_.K(a)&&e.charAt(0)=="$"){var j=angular.aa.Ud(d);if(e=(j=angular[j.charAt(0).toUpperCase()+j.substring(1)])?j[[e.substring(1)]]:undefined)return a=_.u(e,d,d)}}if(typeof a==="function"&&!a.Gc)return nglr.u(d,a);return a}; +nglr.h.prototype.i=function(a){return nglr.h.ja(this.I,a)};function E(a,b,c){b=b.split(".");a=a.I;for(var e=0;b.length>1;e++){var d=b.shift(),f=a[d];if(!f){f={};a[d]=f}a=f}return a[b.shift()]=c}function $(a,b,c){a.eval(b+"="+nglr.m(c))}nglr.h.prototype.eval=function(a,b){var c=nglr.h.ya[a];if(!c){var e=new nglr.q(a);c=V(e);O(e);nglr.h.ya[a]=c}b=b||{};b.scope=this;return c(b)}; +function Z(a,b,c,e,d,f){try{var i=a.eval(c,e);if(b.O){b.O=false;jQuery(b.view).wc("ng-exception").vc("ng-error")}d&&d.apply(b,[i]);return true}catch(j){console.error("Eval Widget Error:",j);a=nglr.m(j,true);b.O=true;jQuery(b.view).fb("ng-exception").e("ng-error",a);f&&f.apply(b,[j,a]);return false}}nglr.h.prototype.ha=function(a){return wa(new nglr.q(a))({scope:this})}; +nglr.h.prototype.watch=function(a){var b=this;(new nglr.q(a)).watch()({scope:this,Sc:function(c,e){Ba(b,c,function(d,f){try{return e({scope:b},d,f)}catch(i){nglr.alert(i)}})}})};function Ba(a,b,c){var e=a.Bb[b];if(!e){e={jc:[],G:b};a.Bb[b]=e}e.jc.push(c)}function za(a){var b=false;jQuery.a(a.Bb,function(c,e){var d=a.eval(e.G);if(d!==e.d){jQuery.a(e.jc,function(f,i){i(d,e.d);b=true});e.d=d}});return b};nglr.Gb=function(a,b){this.url=a;this.xd=0;this.$b=b;this.Yd="_"+(""+Math.random()).substr(2)+"_";this.qb=1800};function Ca(a,b){return aa(b)}nglr.Gb.prototype.oa=function(a,b,c,e){var d=this.Yd+this.xd++;nglr[d]=function(j){delete nglr[d];e(200,j)};a={Ff:b,gf:a,of:c};a=Ca(this,nglr.m(a));b=Math.ceil(a.length/this.qb);c=this.url+"/$/"+d+"/"+b+"/";for(var f=0;f<b;f++){var i=a.substr(f*this.qb,this.qb);this.$b(c+(f+1)+"?h="+i,nglr.L)}};nglr.ta=x("frame");nglr.ta.Oc="$DATASET:"; +nglr.ta.prototype={write:function(){this.frame.name=nglr.ta.Oc+nglr.m(this.data)},oa:v()};nglr.Jb=function(a,b,c){this.fd=a;this.update=c;this.status=b};nglr.Jb.prototype={oa:function(a,b,c,e){var d=this;Da(this.status,c);this.fd.oa(a,b,c,function(){var f=d.status;f.Ia--;f.Ia===0&&f.lc.bc("fold");try{e.apply(this,arguments)}catch(i){nglr.alert(nglr.m(i))}d.update()})}};nglr.Ib=function(a,b){this.C=a;this.ib=b};nglr.Ib.prototype={Yb:function(a){var b=this;this.C.oa("GET","/account.json",{},function(c,e){b.ad=e.Xd;a(e.Xd)})},oc:function(a){var b=this;this.ib.oc(function(){delete b.ad;(a||nglr.L)()})},Da:function(a){var b=this;this.ib.Da(function(){b.Yb(function(){(a||nglr.L)()})})},sb:function(){this.ib.sb()}};angular.k.na=function(a,b,c){return a.match(b)?null:c||"Value does not match expected format "+b+"."};angular.k.tb=function(a,b,c){var e=1*a;if(e==a){if(typeof b!="undefined"&&e<b)return"Value can not be less than "+b+".";if(typeof b!="undefined"&&e>c)return"Value can not be greater than "+c+".";return null}else return"Value is not a number."};angular.k.Xe=function(a,b,c){b=angular.k.tb(a,b,c);if(b===null&&a!=Math.round(a))return"Value is not a whole number.";return b}; +angular.k.bd=function(a){if(a.match(/^\d\d?\/\d\d?\/\d\d\d\d$/))return null;return"Value is not a date. (Expecting format: 12/31/2009)."};angular.k.Af=function(a){if(a.match(/^\d\d\d-\d\d-\d\d\d\d$/))return null;return"SSN needs to be in 999-99-9999 format."};angular.k.gd=function(a){if(a.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/))return null;return"Email needs to be in username@host.com format."}; +angular.k.pf=function(a){if(a.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/))return null;if(a.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/))return null;return"Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."};angular.k.url=function(a){if(a.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/))return null;return"URL needs to be in http://server[:port]/path format."};angular.k.rd=function(a){try{nglr.za(a);return null}catch(b){return b.toString()}};nglr.Qc=function(a,b){this.yd=0;this.Ja=a;this.X=b;this.Rb=swfobject.Rb;this.Jd=v()}; +function ja(a,b,c){var e,d=b.e("type").toLowerCase(),f=b.e("name");if(f)f=f.split(":").pop();var i="change",j=true;if(d=="button"||d=="submit"||d=="reset"||d=="image"){e=new nglr.Na(b[0],f);i="click";j=false}else if(d=="text"||d=="textarea"||d=="hidden"||d=="password"){e=new nglr.bb(b[0],f);i="keyup change"}else if(d=="checkbox"){e=new nglr.Oa(b[0],f);i="click"}else if(d=="radio"){e=new nglr.Wa(b[0],f);i="click"}else if(d=="select-one")e=new nglr.Ya(b[0],f);else if(d=="select-multiple")e=new nglr.ua(b[0], +f);else if(d=="file")e=Ea(a,b,f);else throw"Unknown type: "+d;b.data("controller",e);var n=c.i("$binder");jQuery(e.view,":input").u(i,function(){if(e.j(c)){var m=jQuery(e.view).e("ng-action")||"";Z(c,e,m)&&n.c(c)}return j});return e} +function Ea(a,b){var c="__uploadWidget_"+a.yd++,e=nglr.T.Z(c);b.Tc(e);c=a.Rb({data:a.Ja+"/admin/ServerAPI.swf",width:"95",height:"20",align:"top",Nf:"transparent"},{Re:"uploadWidgetId="+c,pe:"always"},c);b.remove();a=new nglr.T(e,b[0].name,c,a.Ja+"/data/"+a.X);jQuery(c).data("controller",a);return a}nglr.T=function(a,b,c,e){this.view=a;this.Jf=c;this.xc=b;this.qe=e+"/_attachments";this.value=null;this.d=undefined}; +nglr.T.dispatchEvent=function(a,b,c){a=document.getElementById(a);a=jQuery(a).data("controller");nglr.T.prototype["_on_"+b].apply(a,c)};nglr.T.Z=function(a){return jQuery('<span class="ng-upload-widget"><input type="checkbox" ng-non-bindable="true"/><object id="'+a+'" /><a></a><span/></span>')};nglr.T.prototype.j=function(a){var b=this.view.find("input").e("checked")?this.value:null;if(this.d===b)return false;else{E(a,this.xc,b);return true}}; +nglr.T.prototype.c=function(a){if((a=a.i(this.xc))&&this.value!==a){this.value=a;this.view.find("a").e("href",this.value.url).text(this.value.text);this.view.find("span").text(angular.filter.gb(this.value.size))}this.view.find("input").e("checked",!!a)};nglr.ba=x("view");nglr.ba.prototype.j=z(true);nglr.ba.prototype.c=v();nglr.ba.od=new nglr.ba;nglr.Na=x("view");nglr.Na.prototype.j=z(true);nglr.Na.prototype.c=v(); +nglr.bb=function(a,b){this.view=a;this.exp=b;this.k=a.getAttribute("ng-validate");this.Nd=typeof a.attributes["ng-required"]!="undefined";this.hc=null;this.d=undefined;this.H=a.value;a.getAttribute("ng-widget")==="datepicker"&&jQuery(a).Ne()};nglr.bb.prototype.j=function(a){var b=this.view.value;if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}}; +nglr.bb.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}c=c?c:"";if(this.d!=c)this.d=b.value=c;var e=false;b.removeAttribute("ng-error");if(this.Nd)e=!(c&&c.length>0);var d=e?"Required Value":null;if(!e&&this.k&&c){e=this.k;c=c;d=nglr.h.ya[e];if(!d){d=(new nglr.q(e)).k();nglr.h.ya[e]=d}e={scope:a};d=d(e)(e,c);e=!!d}if(this.hc!==d){this.hc=e;if(d!==null){b.setAttribute("ng-error",d);a.I.he.push(this)}jQuery(b).zb("ng-validation-error",e)}}; +nglr.Oa=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.H=a.checked?a.value:""};nglr.Oa.prototype.j=function(a){var b=this.view;b=b.checked?b.value:"";if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}};nglr.Oa.prototype.c=function(a){var b=this.view,c=a.eval(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}b.checked=b.value==""+c};nglr.Ya=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.H=a.value}; +nglr.Ya.prototype.j=function(a){if(this.view.selectedIndex<0)$(a,this.exp,null);else{var b=this.view.value;if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}}};nglr.Ya.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}if(c!==this.d){b.value=c?c:"";this.d=c}};nglr.ua=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.H=this.selected()}; +nglr.ua.prototype.selected=function(){for(var a=[],b=this.view.options,c=0;c<b.length;c++){var e=b[c];e.selected&&a.push(e.value)}return a};nglr.ua.prototype.j=function(a){var b=this.selected();if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}};nglr.ua.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}if(c!==this.d){a=b.options;for(b=0;b<a.length;b++){var e=a[b];e.selected=_.Aa(c,e.value)}this.d=c}}; +nglr.Wa=function(a,b){this.view=a;this.exp=b;this.d=this.ob=undefined;this.cc=a.value;this.H=a.checked?a.value:null};nglr.Wa.prototype.j=function(a){var b=this.view;if(this.ob)return false;else{b.checked=true;this.d=$(a,this.exp,this.cc);return this.ob=true}};nglr.Wa.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(this.H&&typeof c==="undefined"){c=this.H;$(a,this.exp,c)}if(this.d!=c){this.ob=b.checked=this.cc==""+c;this.d=c}}; +nglr.S=function(a,b){this.view=a;this.exp=nglr.g.Ha(b);this.O=false;this.Od={element:a}}; +nglr.S.zc=function(a){var b=nglr.mb;switch(typeof a){case "string":case "boolean":case "number":return b(a);case "function":return nglr.S.zc(a());case "object":if(nglr.gc(a))return nglr.outerHTML(a);else if(a instanceof angular.filter.s){switch(typeof a.B){case "string":case "number":return a.B;case "function":return a.B();case "object":if(nglr.gc(a.B))return nglr.outerHTML(a.B);default:break}switch(typeof a.text){case "string":case "number":return b(a.text);case "function":return b(a.text());default:break}}if(a=== +null)return"";return b(nglr.m(a,true));default:return""}};nglr.S.prototype.j=v();nglr.S.prototype.c=function(a){for(var b=[],c=this.exp,e=c.length,d=0;d<e;d++){var f=c[d],i=nglr.g.ea(f);if(i){Z(a,this,i,this.Od,function(j){b.push(nglr.S.zc(j))},function(j,n){nglr.yc(this.view,n)});if(this.O)return}else b.push(nglr.mb(f))}nglr.yc(this.view,b.join(""))};nglr.Ma=function(a,b){this.view=a;this.Vc=b};nglr.Ma.prototype.j=v(); +nglr.Ma.prototype.c=function(a){var b=jQuery(this.view),c=this.Vc;if(this.O){this.O=false;b.wc("ng-exception").vc("ng-error")}var e=b.$e("img");for(var d in c){for(var f=nglr.g.Ha(c[d]),i=[],j=0;j<f.length;j++){var n=nglr.g.ea(f[j]);if(n)try{var m=a.eval(n,{element:b[0],attrName:d});if(m&&(m.constructor!==nglr.Mb||m.length!==0))i.push(m)}catch(o){this.O=true;console.error("BindAttrUpdater",o);n=nglr.m(o,true);i.push("["+n+"]");b.fb("ng-exception").e("ng-error",n)}else i.push(f[j])}f=i.length?i.join(""): +null;if(e&&d=="src"&&!f)f=a.i("config.server")+"/images/blank.gif";b.e(d,f)}};nglr.Ta=function(a,b){this.view=a;this.exp=b;this.O=false};nglr.Ta.prototype.j=v();nglr.Ta.prototype.c=function(a){Z(a,this,this.exp)};nglr.Ua=function(a,b){this.view=a;this.exp=b};nglr.Ua.prototype.j=v();nglr.Ua.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=jQuery(this.view);nglr.xb(b)?c.bc():c.wb()})};nglr.Za=function(a,b){this.view=a;this.exp=b};nglr.Za.prototype.j=v(); +nglr.Za.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=jQuery(this.view);nglr.xb(b)?c.wb():c.bc()})};nglr.Ra=function(a,b){this.view=a;this.exp=b};nglr.Ra.prototype.j=v();nglr.Ra.prototype.c=function(a){Z(a,this,this.exp,{},function(b){if(b!==null&&b!==undefined)this.view.className=b})};nglr.Pa=function(a,b){this.view=a;this.exp=b};nglr.Pa.prototype.j=v();nglr.Pa.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=a.i("$index");jQuery(this.view).zb(b,c%2===1)})}; +nglr.Qa=function(a,b){this.view=a;this.exp=b};nglr.Qa.prototype.j=v();nglr.Qa.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=a.i("$index");jQuery(this.view).zb(b,c%2===0)})};nglr.ab=function(a,b){this.view=a;this.exp=b};nglr.ab.prototype.j=v();nglr.ab.prototype.c=function(a){Z(a,this,this.exp,{},function(b){jQuery(this.view).e("style","").kb(b)})}; +nglr.Xa=function(a,b,c,e){this.view=a;this.Z=c;this.prefix=e;this.xa=[];a=b.match(/^\s*(.+)\s+in\s+(.*)\s*$/);if(!a)throw"Expected ng-repeat in form of 'item in collection' but got '"+b+"'.";b=a[1];this.Ca=a[2];a=b.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);if(!a)throw"'item' in 'item in collection' should be identifier or (key, value) but get '"+b+"'.";this.Zd=a[3]||a[1];this.td=a[2]};nglr.Xa.prototype.j=v(); +nglr.Xa.prototype.c=function(a){Z(a,this,this.Ca,{},function(b){var c=this;if(!b){b=[];Aa(a,this.Ca)&&E(a,this.Ca,b)}var e=b.length,d=this.xa.length,f=this.view,i=0,j=null,n=this.td,m=this.Zd,o=0;jQuery.a(b,function(q,w){if(o<d){j=c.xa[o];E(j.scope,m,w)}else{var t=new nglr.h(a.I,c.prefix+m+" in "+c.Ca+"["+o+"]");E(t,"$index",o);n&&E(t,n,q);E(t,m,w);j={scope:t,element:c.Z(t,c.prefix,o)};f.Tc(j.element);c.xa.push(j)}f=j.element;q=(new Date).getTime();j.scope.c();i+=(new Date).getTime()-q;o++});for(b= +d;b>e;--b)this.xa.pop().element.removeNode();if(j&&j.element[0].nodeName==="OPTION")if(e=jQuery(j.element[0].parentNode).data("controller")){e.d=undefined;e.c(a)}})};nglr.A=x("F");nglr.A.Fb="mouseleave mouseout click dblclick keypress keyup";nglr.A.prototype.u=function(){this.F.find(".ng-validation-error,.ng-exception").vd("mouseover",nglr.A.Kd)}; +nglr.A.Kd=function(){nglr.A.ub();var a=jQuery(this);a.u(nglr.A.Fb,nglr.A.ub);var b=a.position(),c=document.documentElement,e=(self.innerWidth||c&&c.clientWidth||document.body.clientWidth)-b.left;c=a.Ue("ng-exception")?"EXCEPTION:":"Validation error...";a=a.e("ng-error");e=e>375?"left":"right";c=jQuery("<div id='ng-callout' style='width:300px'><div class='ng-arrow-"+e+"'/><div class='ng-title'>"+c+"</div><div class='ng-content'>"+a+"</div></div>");jQuery("body").append(c);if(e==="left")a=b.left+this.offsetWidth+ +11;else{a=b.left-315;c.find(".ng-arrow-right").kb({left:301})}c.kb({left:a+"px",top:b.top-3+"px"});return true};nglr.A.ub=function(){jQuery("#ng-callout").Gf(nglr.A.Fb,nglr.A.ub).remove();return true};nglr.$a=function(a){this.lc=a.append(nglr.$a.Ic).find("#ng-loading");this.Ia=0};nglr.$a.Ic='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>';function Da(a){a.Ia===0&&a.lc.wb();a.Ia++};(function(a){for(var b=/(.*)\/angular-(.*).js(#(.*))?/,c=document.getElementsByTagName("script"),e={Xc:true,Wc:true,Nb:false},d=0;d<c.length;d++){var f=c[d].src;if(f&&f.match(b)){f=f.match(b);if(f[2]=="bootstrap")e.Nb=true;e.C=f[1]||"";if(!e.C)e.C=window.location.toString().split(window.location.pathname)[0];if(f[4]){f=f[4].split("&");for(var i=0;i<f.length;i++){var j=f[i].split("="),n=j[0];j=j.length==1?true:j[1];if(j=="false")j=false;if(j=="true")j=true;e[n]=j}}}}function m(o,q){q=q||e.C;document.write('<script type="text/javascript" src="'+ +q+o+'"><\/script>')}if(e.Nb){m("/javascripts/webtoolkit.base64.js");m("/javascripts/swfobject.js");m("/javascripts/jQuery/jquery-1.3.2.js");m("/javascripts/jQuery/jquery-ui-1.7.1.custom.min.js");m("/javascripts/underscore/underscore.js");m("/javascripts/nglr/Loader.js");m("/javascripts/nglr/API.js");m("/javascripts/nglr/Binder.js");m("/javascripts/nglr/ControlBar.js");m("/javascripts/nglr/DataStore.js");m("/javascripts/nglr/Filters.js");m("/javascripts/nglr/JSON.js");m("/javascripts/nglr/Model.js"); +m("/javascripts/nglr/Parser.js");m("/javascripts/nglr/Scope.js");m("/javascripts/nglr/Server.js");m("/javascripts/nglr/Users.js");m("/javascripts/nglr/Validators.js");m("/javascripts/nglr/Widgets.js")}else{m("/ajax/libs/swfobject/2.2/swfobject.js","http://ajax.googleapis.com");m("/ajax/libs/jquery/1.3.2/jquery.min.js","http://ajax.googleapis.com");m("/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js","http://ajax.googleapis.com")}window.onload=function(){window.Lb.nd=function(h,k){var l=_.fa(e||{});_.extend(l, +k);(new nglr.Va(h,jQuery("head"),l)).load()};var o=window.document;if(e.Yc){o=null;for(var q=e.Yc.split("|"),w=0;w<q.length&&!o;w++){var t=q[w].split("?"),y=t[0];if(t.length>1)if(!window.document.getElementById(t[1]))continue;o=window.document.getElementById(y)}}e.Wc&&o&&window.Lb.nd(o);if(typeof a==="function")try{a.apply(this,arguments)}catch(g){}}})(window.onload); diff --git a/jsTestDriver.conf b/jsTestDriver.conf new file mode 100644 index 00000000..77a5f0bf --- /dev/null +++ b/jsTestDriver.conf @@ -0,0 +1,16 @@ +server: http://localhost:9876 + +load: +  - lib/swfobject/swfobject.js +  - lib/webtoolkit/webtoolkit.base64.js +  - lib/jquery/jquery-1.3.2.js +  - lib/jquery/jquery-ui-1.7.1.custom.min.js +  - lib/underscore/underscore.js +  - src/Loader.js +  - src/*.js +  - src/test/_namespace.js +  - src/test/*.js +  - test/testabilityPatch.js +  - test/test/*.js +  - test/*.js +  
\ No newline at end of file diff --git a/lib/compiler-closure/COPYING b/lib/compiler-closure/COPYING new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/lib/compiler-closure/COPYING @@ -0,0 +1,202 @@ + +                                 Apache License +                           Version 2.0, January 2004 +                        http://www.apache.org/licenses/ + +   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +   1. Definitions. + +      "License" shall mean the terms and conditions for use, reproduction, +      and distribution as defined by Sections 1 through 9 of this document. + +      "Licensor" shall mean the copyright owner or entity authorized by +      the copyright owner that is granting the License. + +      "Legal Entity" shall mean the union of the acting entity and all +      other entities that control, are controlled by, or are under common +      control with that entity. For the purposes of this definition, +      "control" means (i) the power, direct or indirect, to cause the +      direction or management of such entity, whether by contract or +      otherwise, or (ii) ownership of fifty percent (50%) or more of the +      outstanding shares, or (iii) beneficial ownership of such entity. + +      "You" (or "Your") shall mean an individual or Legal Entity +      exercising permissions granted by this License. + +      "Source" form shall mean the preferred form for making modifications, +      including but not limited to software source code, documentation +      source, and configuration files. + +      "Object" form shall mean any form resulting from mechanical +      transformation or translation of a Source form, including but +      not limited to compiled object code, generated documentation, +      and conversions to other media types. + +      "Work" shall mean the work of authorship, whether in Source or +      Object form, made available under the License, as indicated by a +      copyright notice that is included in or attached to the work +      (an example is provided in the Appendix below). + +      "Derivative Works" shall mean any work, whether in Source or Object +      form, that is based on (or derived from) the Work and for which the +      editorial revisions, annotations, elaborations, or other modifications +      represent, as a whole, an original work of authorship. For the purposes +      of this License, Derivative Works shall not include works that remain +      separable from, or merely link (or bind by name) to the interfaces of, +      the Work and Derivative Works thereof. + +      "Contribution" shall mean any work of authorship, including +      the original version of the Work and any modifications or additions +      to that Work or Derivative Works thereof, that is intentionally +      submitted to Licensor for inclusion in the Work by the copyright owner +      or by an individual or Legal Entity authorized to submit on behalf of +      the copyright owner. For the purposes of this definition, "submitted" +      means any form of electronic, verbal, or written communication sent +      to the Licensor or its representatives, including but not limited to +      communication on electronic mailing lists, source code control systems, +      and issue tracking systems that are managed by, or on behalf of, the +      Licensor for the purpose of discussing and improving the Work, but +      excluding communication that is conspicuously marked or otherwise +      designated in writing by the copyright owner as "Not a Contribution." + +      "Contributor" shall mean Licensor and any individual or Legal Entity +      on behalf of whom a Contribution has been received by Licensor and +      subsequently incorporated within the Work. + +   2. Grant of Copyright License. Subject to the terms and conditions of +      this License, each Contributor hereby grants to You a perpetual, +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable +      copyright license to reproduce, prepare Derivative Works of, +      publicly display, publicly perform, sublicense, and distribute the +      Work and such Derivative Works in Source or Object form. + +   3. Grant of Patent License. Subject to the terms and conditions of +      this License, each Contributor hereby grants to You a perpetual, +      worldwide, non-exclusive, no-charge, royalty-free, irrevocable +      (except as stated in this section) patent license to make, have made, +      use, offer to sell, sell, import, and otherwise transfer the Work, +      where such license applies only to those patent claims licensable +      by such Contributor that are necessarily infringed by their +      Contribution(s) alone or by combination of their Contribution(s) +      with the Work to which such Contribution(s) was submitted. If You +      institute patent litigation against any entity (including a +      cross-claim or counterclaim in a lawsuit) alleging that the Work +      or a Contribution incorporated within the Work constitutes direct +      or contributory patent infringement, then any patent licenses +      granted to You under this License for that Work shall terminate +      as of the date such litigation is filed. + +   4. Redistribution. You may reproduce and distribute copies of the +      Work or Derivative Works thereof in any medium, with or without +      modifications, and in Source or Object form, provided that You +      meet the following conditions: + +      (a) You must give any other recipients of the Work or +          Derivative Works a copy of this License; and + +      (b) You must cause any modified files to carry prominent notices +          stating that You changed the files; and + +      (c) You must retain, in the Source form of any Derivative Works +          that You distribute, all copyright, patent, trademark, and +          attribution notices from the Source form of the Work, +          excluding those notices that do not pertain to any part of +          the Derivative Works; and + +      (d) If the Work includes a "NOTICE" text file as part of its +          distribution, then any Derivative Works that You distribute must +          include a readable copy of the attribution notices contained +          within such NOTICE file, excluding those notices that do not +          pertain to any part of the Derivative Works, in at least one +          of the following places: within a NOTICE text file distributed +          as part of the Derivative Works; within the Source form or +          documentation, if provided along with the Derivative Works; or, +          within a display generated by the Derivative Works, if and +          wherever such third-party notices normally appear. The contents +          of the NOTICE file are for informational purposes only and +          do not modify the License. You may add Your own attribution +          notices within Derivative Works that You distribute, alongside +          or as an addendum to the NOTICE text from the Work, provided +          that such additional attribution notices cannot be construed +          as modifying the License. + +      You may add Your own copyright statement to Your modifications and +      may provide additional or different license terms and conditions +      for use, reproduction, or distribution of Your modifications, or +      for any such Derivative Works as a whole, provided Your use, +      reproduction, and distribution of the Work otherwise complies with +      the conditions stated in this License. + +   5. Submission of Contributions. Unless You explicitly state otherwise, +      any Contribution intentionally submitted for inclusion in the Work +      by You to the Licensor shall be under the terms and conditions of +      this License, without any additional terms or conditions. +      Notwithstanding the above, nothing herein shall supersede or modify +      the terms of any separate license agreement you may have executed +      with Licensor regarding such Contributions. + +   6. Trademarks. This License does not grant permission to use the trade +      names, trademarks, service marks, or product names of the Licensor, +      except as required for reasonable and customary use in describing the +      origin of the Work and reproducing the content of the NOTICE file. + +   7. Disclaimer of Warranty. Unless required by applicable law or +      agreed to in writing, Licensor provides the Work (and each +      Contributor provides its Contributions) on an "AS IS" BASIS, +      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +      implied, including, without limitation, any warranties or conditions +      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +      PARTICULAR PURPOSE. You are solely responsible for determining the +      appropriateness of using or redistributing the Work and assume any +      risks associated with Your exercise of permissions under this License. + +   8. Limitation of Liability. In no event and under no legal theory, +      whether in tort (including negligence), contract, or otherwise, +      unless required by applicable law (such as deliberate and grossly +      negligent acts) or agreed to in writing, shall any Contributor be +      liable to You for damages, including any direct, indirect, special, +      incidental, or consequential damages of any character arising as a +      result of this License or out of the use or inability to use the +      Work (including but not limited to damages for loss of goodwill, +      work stoppage, computer failure or malfunction, or any and all +      other commercial damages or losses), even if such Contributor +      has been advised of the possibility of such damages. + +   9. Accepting Warranty or Additional Liability. While redistributing +      the Work or Derivative Works thereof, You may choose to offer, +      and charge a fee for, acceptance of support, warranty, indemnity, +      or other liability obligations and/or rights consistent with this +      License. However, in accepting such obligations, You may act only +      on Your own behalf and on Your sole responsibility, not on behalf +      of any other Contributor, and only if You agree to indemnify, +      defend, and hold each Contributor harmless for any liability +      incurred by, or claims asserted against, such Contributor by reason +      of your accepting any such warranty or additional liability. + +   END OF TERMS AND CONDITIONS + +   APPENDIX: How to apply the Apache License to your work. + +      To apply the Apache License to your work, attach the following +      boilerplate notice, with the fields enclosed by brackets "[]" +      replaced with your own identifying information. (Don't include +      the brackets!)  The text should be enclosed in the appropriate +      comment syntax for the file format. We also recommend that a +      file or class name and description of purpose be included on the +      same "printed page" as the copyright notice for easier +      identification within third-party archives. + +   Copyright [yyyy] [name of copyright owner] + +   Licensed under the Apache License, Version 2.0 (the "License"); +   you may not use this file except in compliance with the License. +   You may obtain a copy of the License at + +       http://www.apache.org/licenses/LICENSE-2.0 + +   Unless required by applicable law or agreed to in writing, software +   distributed under the License is distributed on an "AS IS" BASIS, +   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +   See the License for the specific language governing permissions and +   limitations under the License. diff --git a/lib/compiler-closure/README b/lib/compiler-closure/README new file mode 100644 index 00000000..af4e6106 --- /dev/null +++ b/lib/compiler-closure/README @@ -0,0 +1,193 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *     http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Contents +// + +The Closure Compiler performs checking, instrumentation, and +optimizations on JavaScript code. The purpose of this README is to +explain how to build and run the Closure Compiler. + +The Closure Compiler requires Java 6 or higher. +http://www.java.com/ + + +// +// Building The Closure Compiler +// + +There are three ways to get a Closure Compiler executable. + +1) Use one we built for you. + +Pre-built Closure binaries can be found at +http://code.google.com/p/closure-compiler/downloads/list + + +2) Check out the source and build it with Apache Ant. + +First, check out the full source tree of the Closure Compiler. There +are instructions on how to do this at the project site. +http://code.google.com/p/closure-compiler/source/checkout + +Apache Ant is a cross-platform build tool. +http://ant.apache.org/ + +At the root of the source tree, there is an Ant file named +build.xml. To use it, navigate to the same directory and type the +command + +ant jar + +This will produce a jar file called "build/compiler.jar". + + +3) Check out the source and build it with Eclipse. + +Eclipse is a cross-platform IDE. +http://www.eclipse.org/ + +Under Eclipse's File menu, click "New > Project ..." and create a +"Java Project."  You will see an options screen. Give the project a +name, select "Create project from existing source," and choose the +root of the checked-out source tree as the existing directory. Verify +that you are using JRE version 6 or higher. + +Eclipse can use the build.xml file to discover rules. When you +navigate to the build.xml file, you will see all the build rules in +the "Outline" pane. Run the "jar" rule to build the compiler in +build/compiler.jar. + + +// +// Running The Closure Compiler +// + +Once you have the jar binary, running the Closure Compiler is straightforward. + +On the command line, type + +java -jar compiler.jar + +This starts the compiler in interactive mode. Type + +var x = 17 + 25; + +then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) +and "Enter" again. The Compiler will respond: + +var x=42; + +The Closure Compiler has many options for reading input from a file, +writing output to a file, checking your code, and running +optimizations. To learn more, type + +java -jar compiler.jar --help + +You can read more detailed documentation about the many flags at +http://code.google.com/closure/compiler/docs/gettingstarted_app.html + + +// +// Compiling Multiple Scripts +// + +If you have multiple scripts, you should compile them all together with +one compile command. + +java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js + +The Closure Compiler will concatenate the files in the order they're +passed at the command line. + +If you need to compile many, many scripts together, you may start to +run into problems with managing dependencies between scripts. You +should check out the Closure Library. It contains functions for +enforcing dependencies between scripts, and a tool called calcdeps.py +that knows how to give scripts to the Closure Compiler in the right +order. + +http://code.google.com/p/closure-library/ + +// +// Licensing +// + +Unless otherwise stated, all source files are licensed under +the Apache License, Version 2.0. + + +----- +Code under: +src/com/google/javascript/rhino +test/com/google/javascript/rhino + +URL: http://www.mozilla.org/rhino +Version:  1.5R3, with heavy modifications +License:  Netscape Public License and MPL / GPL dual license + +Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an +implementation of JavaScript for the JVM.  The JavaScript parser and +the parse tree data structures were extracted and modified +significantly for use by Google's JavaScript compiler. + +Local Modifications: The packages have been renamespaced. All code not +relavant to parsing has been removed. A JSDoc parser and static typing +system have been added. + + +----- +Code in: +lib/libtrunk_rhino_parser_jarjared.jar + +URL: http://www.mozilla.org/rhino +Version:  Trunk +License:  Netscape Public License and MPL / GPL dual license + +Description: Mozilla Rhino is an implementation of JavaScript for the JVM. + +Local Modifications: None. We've used JarJar to renamespace the code +post-compilation. See: +http://code.google.com/p/jarjar/ + + +----- +Code in: +lib/google_common.jar + +URL: http://code.google.com/p/guava-libraries/ +Version:  Trunk +License: Apache License 2.0 + +Description: Google's core Java libraries. + +Local Modifications: None. + + +---- +Code in: +lib/junit.jar + +URL: http://sourceforge.net/projects/junit/ +Version: 3.8.1 +License: Common Public License 1.0 + +Description: A framework for writing and running automated tests in Java. + +Local Modifications: None. + + diff --git a/lib/compiler-closure/compiler.jar b/lib/compiler-closure/compiler.jarBinary files differ new file mode 100644 index 00000000..da053a7d --- /dev/null +++ b/lib/compiler-closure/compiler.jar diff --git a/lib/jquery/jquery-1.3.2.js b/lib/jquery/jquery-1.3.2.js new file mode 100644 index 00000000..d7093948 --- /dev/null +++ b/lib/jquery/jquery-1.3.2.js @@ -0,0 +1,4376 @@ +/*! + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){ + +var  +	// Will speed up references to window, and allows munging its name. +	window = this, +	// Will speed up references to undefined, and allows munging its name. +	undefined, +	// Map over jQuery in case of overwrite +	_jQuery = window.jQuery, +	// Map over the $ in case of overwrite +	_$ = window.$, + +	jQuery = window.jQuery = window.$ = function( selector, context ) { +		// The jQuery object is actually just the init constructor 'enhanced' +		return new jQuery.fn.init( selector, context ); +	}, + +	// A simple way to check for HTML strings or ID strings +	// (both of which we optimize for) +	quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, +	// Is it a simple selector +	isSimple = /^.[^:#\[\.,]*$/; + +jQuery.fn = jQuery.prototype = { +	init: function( selector, context ) { +		// Make sure that a selection was provided +		selector = selector || document; + +		// Handle $(DOMElement) +		if ( selector.nodeType ) { +			this[0] = selector; +			this.length = 1; +			this.context = selector; +			return this; +		} +		// Handle HTML strings +		if ( typeof selector === "string" ) { +			// Are we dealing with HTML string or an ID? +			var match = quickExpr.exec( selector ); + +			// Verify a match, and that no context was specified for #id +			if ( match && (match[1] || !context) ) { + +				// HANDLE: $(html) -> $(array) +				if ( match[1] ) +					selector = jQuery.clean( [ match[1] ], context ); + +				// HANDLE: $("#id") +				else { +					var elem = document.getElementById( match[3] ); + +					// Handle the case where IE and Opera return items +					// by name instead of ID +					if ( elem && elem.id != match[3] ) +						return jQuery().find( selector ); + +					// Otherwise, we inject the element directly into the jQuery object +					var ret = jQuery( elem || [] ); +					ret.context = document; +					ret.selector = selector; +					return ret; +				} + +			// HANDLE: $(expr, [context]) +			// (which is just equivalent to: $(content).find(expr) +			} else +				return jQuery( context ).find( selector ); + +		// HANDLE: $(function) +		// Shortcut for document ready +		} else if ( jQuery.isFunction( selector ) ) +			return jQuery( document ).ready( selector ); + +		// Make sure that old selector state is passed along +		if ( selector.selector && selector.context ) { +			this.selector = selector.selector; +			this.context = selector.context; +		} + +		return this.setArray(jQuery.isArray( selector ) ? +			selector : +			jQuery.makeArray(selector)); +	}, + +	// Start with an empty selector +	selector: "", + +	// The current version of jQuery being used +	jquery: "1.3.2", + +	// The number of elements contained in the matched element set +	size: function() { +		return this.length; +	}, + +	// Get the Nth element in the matched element set OR +	// Get the whole matched element set as a clean array +	get: function( num ) { +		return num === undefined ? + +			// Return a 'clean' array +			Array.prototype.slice.call( this ) : + +			// Return just the object +			this[ num ]; +	}, + +	// Take an array of elements and push it onto the stack +	// (returning the new matched element set) +	pushStack: function( elems, name, selector ) { +		// Build a new jQuery matched element set +		var ret = jQuery( elems ); + +		// Add the old object onto the stack (as a reference) +		ret.prevObject = this; + +		ret.context = this.context; + +		if ( name === "find" ) +			ret.selector = this.selector + (this.selector ? " " : "") + selector; +		else if ( name ) +			ret.selector = this.selector + "." + name + "(" + selector + ")"; + +		// Return the newly-formed element set +		return ret; +	}, + +	// Force the current matched set of elements to become +	// the specified array of elements (destroying the stack in the process) +	// You should use pushStack() in order to do this, but maintain the stack +	setArray: function( elems ) { +		// Resetting the length to 0, then using the native Array push +		// is a super-fast way to populate an object with array-like properties +		this.length = 0; +		Array.prototype.push.apply( this, elems ); + +		return this; +	}, + +	// Execute a callback for every element in the matched set. +	// (You can seed the arguments with an array of args, but this is +	// only used internally.) +	each: function( callback, args ) { +		return jQuery.each( this, callback, args ); +	}, + +	// Determine the position of an element within +	// the matched set of elements +	index: function( elem ) { +		// Locate the position of the desired element +		return jQuery.inArray( +			// If it receives a jQuery object, the first element is used +			elem && elem.jquery ? elem[0] : elem +		, this ); +	}, + +	attr: function( name, value, type ) { +		var options = name; + +		// Look for the case where we're accessing a style value +		if ( typeof name === "string" ) +			if ( value === undefined ) +				return this[0] && jQuery[ type || "attr" ]( this[0], name ); + +			else { +				options = {}; +				options[ name ] = value; +			} + +		// Check to see if we're setting style values +		return this.each(function(i){ +			// Set all the styles +			for ( name in options ) +				jQuery.attr( +					type ? +						this.style : +						this, +					name, jQuery.prop( this, options[ name ], type, i, name ) +				); +		}); +	}, + +	css: function( key, value ) { +		// ignore negative width and height values +		if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) +			value = undefined; +		return this.attr( key, value, "curCSS" ); +	}, + +	text: function( text ) { +		if ( typeof text !== "object" && text != null ) +			return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + +		var ret = ""; + +		jQuery.each( text || this, function(){ +			jQuery.each( this.childNodes, function(){ +				if ( this.nodeType != 8 ) +					ret += this.nodeType != 1 ? +						this.nodeValue : +						jQuery.fn.text( [ this ] ); +			}); +		}); + +		return ret; +	}, + +	wrapAll: function( html ) { +		if ( this[0] ) { +			// The elements to wrap the target around +			var wrap = jQuery( html, this[0].ownerDocument ).clone(); + +			if ( this[0].parentNode ) +				wrap.insertBefore( this[0] ); + +			wrap.map(function(){ +				var elem = this; + +				while ( elem.firstChild ) +					elem = elem.firstChild; + +				return elem; +			}).append(this); +		} + +		return this; +	}, + +	wrapInner: function( html ) { +		return this.each(function(){ +			jQuery( this ).contents().wrapAll( html ); +		}); +	}, + +	wrap: function( html ) { +		return this.each(function(){ +			jQuery( this ).wrapAll( html ); +		}); +	}, + +	append: function() { +		return this.domManip(arguments, true, function(elem){ +			if (this.nodeType == 1) +				this.appendChild( elem ); +		}); +	}, + +	prepend: function() { +		return this.domManip(arguments, true, function(elem){ +			if (this.nodeType == 1) +				this.insertBefore( elem, this.firstChild ); +		}); +	}, + +	before: function() { +		return this.domManip(arguments, false, function(elem){ +			this.parentNode.insertBefore( elem, this ); +		}); +	}, + +	after: function() { +		return this.domManip(arguments, false, function(elem){ +			this.parentNode.insertBefore( elem, this.nextSibling ); +		}); +	}, + +	end: function() { +		return this.prevObject || jQuery( [] ); +	}, + +	// For internal use only. +	// Behaves like an Array's method, not like a jQuery method. +	push: [].push, +	sort: [].sort, +	splice: [].splice, + +	find: function( selector ) { +		if ( this.length === 1 ) { +			var ret = this.pushStack( [], "find", selector ); +			ret.length = 0; +			jQuery.find( selector, this[0], ret ); +			return ret; +		} else { +			return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ +				return jQuery.find( selector, elem ); +			})), "find", selector ); +		} +	}, + +	clone: function( events ) { +		// Do the clone +		var ret = this.map(function(){ +			if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { +				// IE copies events bound via attachEvent when +				// using cloneNode. Calling detachEvent on the +				// clone will also remove the events from the orignal +				// In order to get around this, we use innerHTML. +				// Unfortunately, this means some modifications to +				// attributes in IE that are actually only stored +				// as properties will not be copied (such as the +				// the name attribute on an input). +				var html = this.outerHTML; +				if ( !html ) { +					var div = this.ownerDocument.createElement("div"); +					div.appendChild( this.cloneNode(true) ); +					html = div.innerHTML; +				} + +				return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; +			} else +				return this.cloneNode(true); +		}); + +		// Copy the events from the original to the clone +		if ( events === true ) { +			var orig = this.find("*").andSelf(), i = 0; + +			ret.find("*").andSelf().each(function(){ +				if ( this.nodeName !== orig[i].nodeName ) +					return; + +				var events = jQuery.data( orig[i], "events" ); + +				for ( var type in events ) { +					for ( var handler in events[ type ] ) { +						jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); +					} +				} + +				i++; +			}); +		} + +		// Return the cloned set +		return ret; +	}, + +	filter: function( selector ) { +		return this.pushStack( +			jQuery.isFunction( selector ) && +			jQuery.grep(this, function(elem, i){ +				return selector.call( elem, i ); +			}) || + +			jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ +				return elem.nodeType === 1; +			}) ), "filter", selector ); +	}, + +	closest: function( selector ) { +		var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, +			closer = 0; + +		return this.map(function(){ +			var cur = this; +			while ( cur && cur.ownerDocument ) { +				if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { +					jQuery.data(cur, "closest", closer); +					return cur; +				} +				cur = cur.parentNode; +				closer++; +			} +		}); +	}, + +	not: function( selector ) { +		if ( typeof selector === "string" ) +			// test special case where just one selector is passed in +			if ( isSimple.test( selector ) ) +				return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); +			else +				selector = jQuery.multiFilter( selector, this ); + +		var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; +		return this.filter(function() { +			return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; +		}); +	}, + +	add: function( selector ) { +		return this.pushStack( jQuery.unique( jQuery.merge( +			this.get(), +			typeof selector === "string" ? +				jQuery( selector ) : +				jQuery.makeArray( selector ) +		))); +	}, + +	is: function( selector ) { +		return !!selector && jQuery.multiFilter( selector, this ).length > 0; +	}, + +	hasClass: function( selector ) { +		return !!selector && this.is( "." + selector ); +	}, + +	val: function( value ) { +		if ( value === undefined ) {			 +			var elem = this[0]; + +			if ( elem ) { +				if( jQuery.nodeName( elem, 'option' ) ) +					return (elem.attributes.value || {}).specified ? elem.value : elem.text; +				 +				// We need to handle select boxes special +				if ( jQuery.nodeName( elem, "select" ) ) { +					var index = elem.selectedIndex, +						values = [], +						options = elem.options, +						one = elem.type == "select-one"; + +					// Nothing was selected +					if ( index < 0 ) +						return null; + +					// Loop through all the selected options +					for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { +						var option = options[ i ]; + +						if ( option.selected ) { +							// Get the specifc value for the option +							value = jQuery(option).val(); + +							// We don't need an array for one selects +							if ( one ) +								return value; + +							// Multi-Selects return an array +							values.push( value ); +						} +					} + +					return values;				 +				} + +				// Everything else, we just grab the value +				return (elem.value || "").replace(/\r/g, ""); + +			} + +			return undefined; +		} + +		if ( typeof value === "number" ) +			value += ''; + +		return this.each(function(){ +			if ( this.nodeType != 1 ) +				return; + +			if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) +				this.checked = (jQuery.inArray(this.value, value) >= 0 || +					jQuery.inArray(this.name, value) >= 0); + +			else if ( jQuery.nodeName( this, "select" ) ) { +				var values = jQuery.makeArray(value); + +				jQuery( "option", this ).each(function(){ +					this.selected = (jQuery.inArray( this.value, values ) >= 0 || +						jQuery.inArray( this.text, values ) >= 0); +				}); + +				if ( !values.length ) +					this.selectedIndex = -1; + +			} else +				this.value = value; +		}); +	}, + +	html: function( value ) { +		return value === undefined ? +			(this[0] ? +				this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : +				null) : +			this.empty().append( value ); +	}, + +	replaceWith: function( value ) { +		return this.after( value ).remove(); +	}, + +	eq: function( i ) { +		return this.slice( i, +i + 1 ); +	}, + +	slice: function() { +		return this.pushStack( Array.prototype.slice.apply( this, arguments ), +			"slice", Array.prototype.slice.call(arguments).join(",") ); +	}, + +	map: function( callback ) { +		return this.pushStack( jQuery.map(this, function(elem, i){ +			return callback.call( elem, i, elem ); +		})); +	}, + +	andSelf: function() { +		return this.add( this.prevObject ); +	}, + +	domManip: function( args, table, callback ) { +		if ( this[0] ) { +			var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), +				scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), +				first = fragment.firstChild; + +			if ( first ) +				for ( var i = 0, l = this.length; i < l; i++ ) +					callback.call( root(this[i], first), this.length > 1 || i > 0 ? +							fragment.cloneNode(true) : fragment ); +		 +			if ( scripts ) +				jQuery.each( scripts, evalScript ); +		} + +		return this; +		 +		function root( elem, cur ) { +			return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? +				(elem.getElementsByTagName("tbody")[0] || +				elem.appendChild(elem.ownerDocument.createElement("tbody"))) : +				elem; +		} +	} +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +function evalScript( i, elem ) { +	if ( elem.src ) +		jQuery.ajax({ +			url: elem.src, +			async: false, +			dataType: "script" +		}); + +	else +		jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + +	if ( elem.parentNode ) +		elem.parentNode.removeChild( elem ); +} + +function now(){ +	return +new Date; +} + +jQuery.extend = jQuery.fn.extend = function() { +	// copy reference to target object +	var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + +	// Handle a deep copy situation +	if ( typeof target === "boolean" ) { +		deep = target; +		target = arguments[1] || {}; +		// skip the boolean and the target +		i = 2; +	} + +	// Handle case when target is a string or something (possible in deep copy) +	if ( typeof target !== "object" && !jQuery.isFunction(target) ) +		target = {}; + +	// extend jQuery itself if only one argument is passed +	if ( length == i ) { +		target = this; +		--i; +	} + +	for ( ; i < length; i++ ) +		// Only deal with non-null/undefined values +		if ( (options = arguments[ i ]) != null ) +			// Extend the base object +			for ( var name in options ) { +				var src = target[ name ], copy = options[ name ]; + +				// Prevent never-ending loop +				if ( target === copy ) +					continue; + +				// Recurse if we're merging object values +				if ( deep && copy && typeof copy === "object" && !copy.nodeType ) +					target[ name ] = jQuery.extend( deep,  +						// Never move original objects, clone them +						src || ( copy.length != null ? [ ] : { } ) +					, copy ); + +				// Don't bring in undefined values +				else if ( copy !== undefined ) +					target[ name ] = copy; + +			} + +	// Return the modified object +	return target; +}; + +// exclude the following css properties to add px +var	exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, +	// cache defaultView +	defaultView = document.defaultView || {}, +	toString = Object.prototype.toString; + +jQuery.extend({ +	noConflict: function( deep ) { +		window.$ = _$; + +		if ( deep ) +			window.jQuery = _jQuery; + +		return jQuery; +	}, + +	// See test/unit/core.js for details concerning isFunction. +	// Since version 1.3, DOM methods and functions like alert +	// aren't supported. They return false on IE (#2968). +	isFunction: function( obj ) { +		return toString.call(obj) === "[object Function]"; +	}, + +	isArray: function( obj ) { +		return toString.call(obj) === "[object Array]"; +	}, + +	// check if an element is in a (or is an) XML document +	isXMLDoc: function( elem ) { +		return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || +			!!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); +	}, + +	// Evalulates a script in a global context +	globalEval: function( data ) { +		if ( data && /\S/.test(data) ) { +			// Inspired by code by Andrea Giammarchi +			// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html +			var head = document.getElementsByTagName("head")[0] || document.documentElement, +				script = document.createElement("script"); + +			script.type = "text/javascript"; +			if ( jQuery.support.scriptEval ) +				script.appendChild( document.createTextNode( data ) ); +			else +				script.text = data; + +			// Use insertBefore instead of appendChild  to circumvent an IE6 bug. +			// This arises when a base node is used (#2709). +			head.insertBefore( script, head.firstChild ); +			head.removeChild( script ); +		} +	}, + +	nodeName: function( elem, name ) { +		return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); +	}, + +	// args is for internal usage only +	each: function( object, callback, args ) { +		var name, i = 0, length = object.length; + +		if ( args ) { +			if ( length === undefined ) { +				for ( name in object ) +					if ( callback.apply( object[ name ], args ) === false ) +						break; +			} else +				for ( ; i < length; ) +					if ( callback.apply( object[ i++ ], args ) === false ) +						break; + +		// A special, fast, case for the most common use of each +		} else { +			if ( length === undefined ) { +				for ( name in object ) +					if ( callback.call( object[ name ], name, object[ name ] ) === false ) +						break; +			} else +				for ( var value = object[0]; +					i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} +		} + +		return object; +	}, + +	prop: function( elem, value, type, i, name ) { +		// Handle executable functions +		if ( jQuery.isFunction( value ) ) +			value = value.call( elem, i ); + +		// Handle passing in a number to a CSS property +		return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? +			value + "px" : +			value; +	}, + +	className: { +		// internal only, use addClass("class") +		add: function( elem, classNames ) { +			jQuery.each((classNames || "").split(/\s+/), function(i, className){ +				if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) +					elem.className += (elem.className ? " " : "") + className; +			}); +		}, + +		// internal only, use removeClass("class") +		remove: function( elem, classNames ) { +			if (elem.nodeType == 1) +				elem.className = classNames !== undefined ? +					jQuery.grep(elem.className.split(/\s+/), function(className){ +						return !jQuery.className.has( classNames, className ); +					}).join(" ") : +					""; +		}, + +		// internal only, use hasClass("class") +		has: function( elem, className ) { +			return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; +		} +	}, + +	// A method for quickly swapping in/out CSS properties to get correct calculations +	swap: function( elem, options, callback ) { +		var old = {}; +		// Remember the old values, and insert the new ones +		for ( var name in options ) { +			old[ name ] = elem.style[ name ]; +			elem.style[ name ] = options[ name ]; +		} + +		callback.call( elem ); + +		// Revert the old values +		for ( var name in options ) +			elem.style[ name ] = old[ name ]; +	}, + +	css: function( elem, name, force, extra ) { +		if ( name == "width" || name == "height" ) { +			var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; + +			function getWH() { +				val = name == "width" ? elem.offsetWidth : elem.offsetHeight; + +				if ( extra === "border" ) +					return; + +				jQuery.each( which, function() { +					if ( !extra ) +						val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; +					if ( extra === "margin" ) +						val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; +					else +						val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; +				}); +			} + +			if ( elem.offsetWidth !== 0 ) +				getWH(); +			else +				jQuery.swap( elem, props, getWH ); + +			return Math.max(0, Math.round(val)); +		} + +		return jQuery.curCSS( elem, name, force ); +	}, + +	curCSS: function( elem, name, force ) { +		var ret, style = elem.style; + +		// We need to handle opacity special in IE +		if ( name == "opacity" && !jQuery.support.opacity ) { +			ret = jQuery.attr( style, "opacity" ); + +			return ret == "" ? +				"1" : +				ret; +		} + +		// Make sure we're using the right name for getting the float value +		if ( name.match( /float/i ) ) +			name = styleFloat; + +		if ( !force && style && style[ name ] ) +			ret = style[ name ]; + +		else if ( defaultView.getComputedStyle ) { + +			// Only "float" is needed here +			if ( name.match( /float/i ) ) +				name = "float"; + +			name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); + +			var computedStyle = defaultView.getComputedStyle( elem, null ); + +			if ( computedStyle ) +				ret = computedStyle.getPropertyValue( name ); + +			// We should always get a number back from opacity +			if ( name == "opacity" && ret == "" ) +				ret = "1"; + +		} else if ( elem.currentStyle ) { +			var camelCase = name.replace(/\-(\w)/g, function(all, letter){ +				return letter.toUpperCase(); +			}); + +			ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; + +			// From the awesome hack by Dean Edwards +			// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + +			// If we're not dealing with a regular pixel number +			// but a number that has a weird ending, we need to convert it to pixels +			if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { +				// Remember the original values +				var left = style.left, rsLeft = elem.runtimeStyle.left; + +				// Put in the new values to get a computed value out +				elem.runtimeStyle.left = elem.currentStyle.left; +				style.left = ret || 0; +				ret = style.pixelLeft + "px"; + +				// Revert the changed values +				style.left = left; +				elem.runtimeStyle.left = rsLeft; +			} +		} + +		return ret; +	}, + +	clean: function( elems, context, fragment ) { +		context = context || document; + +		// !context.createElement fails in IE with an error but returns typeof 'object' +		if ( typeof context.createElement === "undefined" ) +			context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + +		// If a single string is passed in and it's a single tag +		// just do a createElement and skip the rest +		if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { +			var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); +			if ( match ) +				return [ context.createElement( match[1] ) ]; +		} + +		var ret = [], scripts = [], div = context.createElement("div"); + +		jQuery.each(elems, function(i, elem){ +			if ( typeof elem === "number" ) +				elem += ''; + +			if ( !elem ) +				return; + +			// Convert html string into DOM nodes +			if ( typeof elem === "string" ) { +				// Fix "XHTML"-style tags in all browsers +				elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ +					return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? +						all : +						front + "></" + tag + ">"; +				}); + +				// Trim whitespace, otherwise indexOf won't work as expected +				var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); + +				var wrap = +					// option or optgroup +					!tags.indexOf("<opt") && +					[ 1, "<select multiple='multiple'>", "</select>" ] || + +					!tags.indexOf("<leg") && +					[ 1, "<fieldset>", "</fieldset>" ] || + +					tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && +					[ 1, "<table>", "</table>" ] || + +					!tags.indexOf("<tr") && +					[ 2, "<table><tbody>", "</tbody></table>" ] || + +				 	// <thead> matched above +					(!tags.indexOf("<td") || !tags.indexOf("<th")) && +					[ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] || + +					!tags.indexOf("<col") && +					[ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] || + +					// IE can't serialize <link> and <script> tags normally +					!jQuery.support.htmlSerialize && +					[ 1, "div<div>", "</div>" ] || + +					[ 0, "", "" ]; + +				// Go to html and back, then peel off extra wrappers +				div.innerHTML = wrap[1] + elem + wrap[2]; + +				// Move to the right depth +				while ( wrap[0]-- ) +					div = div.lastChild; + +				// Remove IE's autoinserted <tbody> from table fragments +				if ( !jQuery.support.tbody ) { + +					// String was a <table>, *may* have spurious <tbody> +					var hasBody = /<tbody/i.test(elem), +						tbody = !tags.indexOf("<table") && !hasBody ? +							div.firstChild && div.firstChild.childNodes : + +						// String was a bare <thead> or <tfoot> +						wrap[1] == "<table>" && !hasBody ? +							div.childNodes : +							[]; + +					for ( var j = tbody.length - 1; j >= 0 ; --j ) +						if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) +							tbody[ j ].parentNode.removeChild( tbody[ j ] ); + +					} + +				// IE completely kills leading whitespace when innerHTML is used +				if ( !jQuery.support.leadingWhitespace && /^\s/.test( elem ) ) +					div.insertBefore( context.createTextNode( elem.match(/^\s*/)[0] ), div.firstChild ); +				 +				elem = jQuery.makeArray( div.childNodes ); +			} + +			if ( elem.nodeType ) +				ret.push( elem ); +			else +				ret = jQuery.merge( ret, elem ); + +		}); + +		if ( fragment ) { +			for ( var i = 0; ret[i]; i++ ) { +				if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) { +					scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] ); +				} else { +					if ( ret[i].nodeType === 1 ) +						ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) ); +					fragment.appendChild( ret[i] ); +				} +			} +			 +			return scripts; +		} + +		return ret; +	}, + +	attr: function( elem, name, value ) { +		// don't set attributes on text and comment nodes +		if (!elem || elem.nodeType == 3 || elem.nodeType == 8) +			return undefined; + +		var notxml = !jQuery.isXMLDoc( elem ), +			// Whether we are setting (or getting) +			set = value !== undefined; + +		// Try to normalize/fix the name +		name = notxml && jQuery.props[ name ] || name; + +		// Only do all the following if this is a node (faster for style) +		// IE elem.getAttribute passes even for style +		if ( elem.tagName ) { + +			// These attributes require special treatment +			var special = /href|src|style/.test( name ); + +			// Safari mis-reports the default selected property of a hidden option +			// Accessing the parent's selectedIndex property fixes it +			if ( name == "selected" && elem.parentNode ) +				elem.parentNode.selectedIndex; + +			// If applicable, access the attribute via the DOM 0 way +			if ( name in elem && notxml && !special ) { +				if ( set ){ +					// We can't allow the type property to be changed (since it causes problems in IE) +					if ( name == "type" && jQuery.nodeName( elem, "input" ) && elem.parentNode ) +						throw "type property can't be changed"; + +					elem[ name ] = value; +				} + +				// browsers index elements by id/name on nglr, give priority to attributes. +				if( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) +					return elem.getAttributeNode( name ).nodeValue; + +				// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set +				// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ +				if ( name == "tabIndex" ) { +					var attributeNode = elem.getAttributeNode( "tabIndex" ); +					return attributeNode && attributeNode.specified +						? attributeNode.value +						: elem.nodeName.match(/(button|input|object|select|textarea)/i) +							? 0 +							: elem.nodeName.match(/^(a|area)$/i) && elem.href +								? 0 +								: undefined; +				} + +				return elem[ name ]; +			} + +			if ( !jQuery.support.style && notxml &&  name == "style" ) +				return jQuery.attr( elem.style, "cssText", value ); + +			if ( set ) +				// convert the value to a string (all browsers do this but IE) see #1070 +				elem.setAttribute( name, "" + value ); + +			var attr = !jQuery.support.hrefNormalized && notxml && special +					// Some attributes require a special call on IE +					? elem.getAttribute( name, 2 ) +					: elem.getAttribute( name ); + +			// Non-existent attributes return null, we normalize to undefined +			return attr === null ? undefined : attr; +		} + +		// elem is actually elem.style ... set the style + +		// IE uses filters for opacity +		if ( !jQuery.support.opacity && name == "opacity" ) { +			if ( set ) { +				// IE has trouble with opacity if it does not have layout +				// Force it by setting the zoom level +				elem.zoom = 1; + +				// Set the alpha filter to set the opacity +				elem.filter = (elem.filter || "").replace( /alpha\([^)]*\)/, "" ) + +					(parseInt( value ) + '' == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")"); +			} + +			return elem.filter && elem.filter.indexOf("opacity=") >= 0 ? +				(parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100) + '': +				""; +		} + +		name = name.replace(/-([a-z])/ig, function(all, letter){ +			return letter.toUpperCase(); +		}); + +		if ( set ) +			elem[ name ] = value; + +		return elem[ name ]; +	}, + +	trim: function( text ) { +		return (text || "").replace( /^\s+|\s+$/g, "" ); +	}, + +	makeArray: function( array ) { +		var ret = []; + +		if( array != null ){ +			var i = array.length; +			// The window, strings (and functions) also have 'length' +			if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval ) +				ret[0] = array; +			else +				while( i ) +					ret[--i] = array[i]; +		} + +		return ret; +	}, + +	inArray: function( elem, array ) { +		for ( var i = 0, length = array.length; i < length; i++ ) +		// Use === because on IE, window == document +			if ( array[ i ] === elem ) +				return i; + +		return -1; +	}, + +	merge: function( first, second ) { +		// We have to loop this way because IE & Opera overwrite the length +		// expando of getElementsByTagName +		var i = 0, elem, pos = first.length; +		// Also, we need to make sure that the correct elements are being returned +		// (IE returns comment nodes in a '*' query) +		if ( !jQuery.support.getAll ) { +			while ( (elem = second[ i++ ]) != null ) +				if ( elem.nodeType != 8 ) +					first[ pos++ ] = elem; + +		} else +			while ( (elem = second[ i++ ]) != null ) +				first[ pos++ ] = elem; + +		return first; +	}, + +	unique: function( array ) { +		var ret = [], done = {}; + +		try { + +			for ( var i = 0, length = array.length; i < length; i++ ) { +				var id = jQuery.data( array[ i ] ); + +				if ( !done[ id ] ) { +					done[ id ] = true; +					ret.push( array[ i ] ); +				} +			} + +		} catch( e ) { +			ret = array; +		} + +		return ret; +	}, + +	grep: function( elems, callback, inv ) { +		var ret = []; + +		// Go through the array, only saving the items +		// that pass the validator function +		for ( var i = 0, length = elems.length; i < length; i++ ) +			if ( !inv != !callback( elems[ i ], i ) ) +				ret.push( elems[ i ] ); + +		return ret; +	}, + +	map: function( elems, callback ) { +		var ret = []; + +		// Go through the array, translating each of the items to their +		// new value (or values). +		for ( var i = 0, length = elems.length; i < length; i++ ) { +			var value = callback( elems[ i ], i ); + +			if ( value != null ) +				ret[ ret.length ] = value; +		} + +		return ret.concat.apply( [], ret ); +	} +}); + +// Use of jQuery.browser is deprecated. +// It's included for backwards compatibility and plugins, +// although they should work to migrate away. + +var userAgent = navigator.userAgent.toLowerCase(); + +// Figure out what browser is being used +jQuery.browser = { +	version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1], +	safari: /webkit/.test( userAgent ), +	opera: /opera/.test( userAgent ), +	msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ), +	mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent ) +}; + +jQuery.each({ +	parent: function(elem){return elem.parentNode;}, +	parents: function(elem){return jQuery.dir(elem,"parentNode");}, +	next: function(elem){return jQuery.nth(elem,2,"nextSibling");}, +	prev: function(elem){return jQuery.nth(elem,2,"previousSibling");}, +	nextAll: function(elem){return jQuery.dir(elem,"nextSibling");}, +	prevAll: function(elem){return jQuery.dir(elem,"previousSibling");}, +	siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);}, +	children: function(elem){return jQuery.sibling(elem.firstChild);}, +	contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);} +}, function(name, fn){ +	jQuery.fn[ name ] = function( selector ) { +		var ret = jQuery.map( this, fn ); + +		if ( selector && typeof selector == "string" ) +			ret = jQuery.multiFilter( selector, ret ); + +		return this.pushStack( jQuery.unique( ret ), name, selector ); +	}; +}); + +jQuery.each({ +	appendTo: "append", +	prependTo: "prepend", +	insertBefore: "before", +	insertAfter: "after", +	replaceAll: "replaceWith" +}, function(name, original){ +	jQuery.fn[ name ] = function( selector ) { +		var ret = [], insert = jQuery( selector ); + +		for ( var i = 0, l = insert.length; i < l; i++ ) { +			var elems = (i > 0 ? this.clone(true) : this).get(); +			jQuery.fn[ original ].apply( jQuery(insert[i]), elems ); +			ret = ret.concat( elems ); +		} + +		return this.pushStack( ret, name, selector ); +	}; +}); + +jQuery.each({ +	removeAttr: function( name ) { +		jQuery.attr( this, name, "" ); +		if (this.nodeType == 1) +			this.removeAttribute( name ); +	}, + +	addClass: function( classNames ) { +		jQuery.className.add( this, classNames ); +	}, + +	removeClass: function( classNames ) { +		jQuery.className.remove( this, classNames ); +	}, + +	toggleClass: function( classNames, state ) { +		if( typeof state !== "boolean" ) +			state = !jQuery.className.has( this, classNames ); +		jQuery.className[ state ? "add" : "remove" ]( this, classNames ); +	}, + +	remove: function( selector ) { +		if ( !selector || jQuery.filter( selector, [ this ] ).length ) { +			// Prevent memory leaks +			jQuery( "*", this ).add([this]).each(function(){ +				jQuery.event.remove(this); +				jQuery.removeData(this); +			}); +			if (this.parentNode) +				this.parentNode.removeChild( this ); +		} +	}, + +	empty: function() { +		// Remove element nodes and prevent memory leaks +		jQuery(this).children().remove(); + +		// Remove any remaining nodes +		while ( this.firstChild ) +			this.removeChild( this.firstChild ); +	} +}, function(name, fn){ +	jQuery.fn[ name ] = function(){ +		return this.each( fn, arguments ); +	}; +}); + +// Helper function used by the dimensions and offset modules +function num(elem, prop) { +	return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0; +} +var expando = "jQuery" + now(), uuid = 0, windowData = {};
 +
 +jQuery.extend({
 +	cache: {},
 +
 +	data: function( elem, name, data ) {
 +		elem = elem == window ?
 +			windowData :
 +			elem;
 +
 +		var id = elem[ expando ];
 +
 +		// Compute a unique ID for the element
 +		if ( !id )
 +			id = elem[ expando ] = ++uuid;
 +
 +		// Only generate the data cache if we're
 +		// trying to access or manipulate it
 +		if ( name && !jQuery.cache[ id ] )
 +			jQuery.cache[ id ] = {};
 +
 +		// Prevent overriding the named cache with undefined values
 +		if ( data !== undefined )
 +			jQuery.cache[ id ][ name ] = data;
 +
 +		// Return the named cache data, or the ID for the element
 +		return name ?
 +			jQuery.cache[ id ][ name ] :
 +			id;
 +	},
 +
 +	removeData: function( elem, name ) {
 +		elem = elem == window ?
 +			windowData :
 +			elem;
 +
 +		var id = elem[ expando ];
 +
 +		// If we want to remove a specific section of the element's data
 +		if ( name ) {
 +			if ( jQuery.cache[ id ] ) {
 +				// Remove the section of cache data
 +				delete jQuery.cache[ id ][ name ];
 +
 +				// If we've removed all the data, remove the element's cache
 +				name = "";
 +
 +				for ( name in jQuery.cache[ id ] )
 +					break;
 +
 +				if ( !name )
 +					jQuery.removeData( elem );
 +			}
 +
 +		// Otherwise, we want to remove all of the element's data
 +		} else {
 +			// Clean up the element expando
 +			try {
 +				delete elem[ expando ];
 +			} catch(e){
 +				// IE has trouble directly removing the expando
 +				// but it's ok with using removeAttribute
 +				if ( elem.removeAttribute )
 +					elem.removeAttribute( expando );
 +			}
 +
 +			// Completely remove the data cache
 +			delete jQuery.cache[ id ];
 +		}
 +	},
 +	queue: function( elem, type, data ) {
 +		if ( elem ){
 +	
 +			type = (type || "fx") + "queue";
 +	
 +			var q = jQuery.data( elem, type );
 +	
 +			if ( !q || jQuery.isArray(data) )
 +				q = jQuery.data( elem, type, jQuery.makeArray(data) );
 +			else if( data )
 +				q.push( data );
 +	
 +		}
 +		return q;
 +	},
 +
 +	dequeue: function( elem, type ){
 +		var queue = jQuery.queue( elem, type ),
 +			fn = queue.shift();
 +		
 +		if( !type || type === "fx" )
 +			fn = queue[0];
 +			
 +		if( fn !== undefined )
 +			fn.call(elem);
 +	}
 +});
 +
 +jQuery.fn.extend({
 +	data: function( key, value ){
 +		var parts = key.split(".");
 +		parts[1] = parts[1] ? "." + parts[1] : "";
 +
 +		if ( value === undefined ) {
 +			var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
 +
 +			if ( data === undefined && this.length )
 +				data = jQuery.data( this[0], key );
 +
 +			return data === undefined && parts[1] ?
 +				this.data( parts[0] ) :
 +				data;
 +		} else
 +			return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
 +				jQuery.data( this, key, value );
 +			});
 +	},
 +
 +	removeData: function( key ){
 +		return this.each(function(){
 +			jQuery.removeData( this, key );
 +		});
 +	},
 +	queue: function(type, data){
 +		if ( typeof type !== "string" ) {
 +			data = type;
 +			type = "fx";
 +		}
 +
 +		if ( data === undefined )
 +			return jQuery.queue( this[0], type );
 +
 +		return this.each(function(){
 +			var queue = jQuery.queue( this, type, data );
 +			
 +			 if( type == "fx" && queue.length == 1 )
 +				queue[0].call(this);
 +		});
 +	},
 +	dequeue: function(type){
 +		return this.each(function(){
 +			jQuery.dequeue( this, type );
 +		});
 +	}
 +});/*! + * Sizzle CSS Selector Engine - v0.9.3 + *  Copyright 2009, The Dojo Foundation + *  Released under the MIT, BSD, and GPL Licenses. + *  More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?/g, +	done = 0, +	toString = Object.prototype.toString; + +var Sizzle = function(selector, context, results, seed) { +	results = results || []; +	context = context || document; + +	if ( context.nodeType !== 1 && context.nodeType !== 9 ) +		return []; +	 +	if ( !selector || typeof selector !== "string" ) { +		return results; +	} + +	var parts = [], m, set, checkSet, check, mode, extra, prune = true; +	 +	// Reset the position of the chunker regexp (start from head) +	chunker.lastIndex = 0; +	 +	while ( (m = chunker.exec(selector)) !== null ) { +		parts.push( m[1] ); +		 +		if ( m[2] ) { +			extra = RegExp.rightContext; +			break; +		} +	} + +	if ( parts.length > 1 && origPOS.exec( selector ) ) { +		if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { +			set = posProcess( parts[0] + parts[1], context ); +		} else { +			set = Expr.relative[ parts[0] ] ? +				[ context ] : +				Sizzle( parts.shift(), context ); + +			while ( parts.length ) { +				selector = parts.shift(); + +				if ( Expr.relative[ selector ] ) +					selector += parts.shift(); + +				set = posProcess( selector, set ); +			} +		} +	} else { +		var ret = seed ? +			{ expr: parts.pop(), set: makeArray(seed) } : +			Sizzle.find( parts.pop(), parts.length === 1 && context.parentNode ? context.parentNode : context, isXML(context) ); +		set = Sizzle.filter( ret.expr, ret.set ); + +		if ( parts.length > 0 ) { +			checkSet = makeArray(set); +		} else { +			prune = false; +		} + +		while ( parts.length ) { +			var cur = parts.pop(), pop = cur; + +			if ( !Expr.relative[ cur ] ) { +				cur = ""; +			} else { +				pop = parts.pop(); +			} + +			if ( pop == null ) { +				pop = context; +			} + +			Expr.relative[ cur ]( checkSet, pop, isXML(context) ); +		} +	} + +	if ( !checkSet ) { +		checkSet = set; +	} + +	if ( !checkSet ) { +		throw "Syntax error, unrecognized expression: " + (cur || selector); +	} + +	if ( toString.call(checkSet) === "[object Array]" ) { +		if ( !prune ) { +			results.push.apply( results, checkSet ); +		} else if ( context.nodeType === 1 ) { +			for ( var i = 0; checkSet[i] != null; i++ ) { +				if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { +					results.push( set[i] ); +				} +			} +		} else { +			for ( var i = 0; checkSet[i] != null; i++ ) { +				if ( checkSet[i] && checkSet[i].nodeType === 1 ) { +					results.push( set[i] ); +				} +			} +		} +	} else { +		makeArray( checkSet, results ); +	} + +	if ( extra ) { +		Sizzle( extra, context, results, seed ); + +		if ( sortOrder ) { +			hasDuplicate = false; +			results.sort(sortOrder); + +			if ( hasDuplicate ) { +				for ( var i = 1; i < results.length; i++ ) { +					if ( results[i] === results[i-1] ) { +						results.splice(i--, 1); +					} +				} +			} +		} +	} + +	return results; +}; + +Sizzle.matches = function(expr, set){ +	return Sizzle(expr, null, null, set); +}; + +Sizzle.find = function(expr, context, isXML){ +	var set, match; + +	if ( !expr ) { +		return []; +	} + +	for ( var i = 0, l = Expr.order.length; i < l; i++ ) { +		var type = Expr.order[i], match; +		 +		if ( (match = Expr.match[ type ].exec( expr )) ) { +			var left = RegExp.leftContext; + +			if ( left.substr( left.length - 1 ) !== "\\" ) { +				match[1] = (match[1] || "").replace(/\\/g, ""); +				set = Expr.find[ type ]( match, context, isXML ); +				if ( set != null ) { +					expr = expr.replace( Expr.match[ type ], "" ); +					break; +				} +			} +		} +	} + +	if ( !set ) { +		set = context.getElementsByTagName("*"); +	} + +	return {set: set, expr: expr}; +}; + +Sizzle.filter = function(expr, set, inplace, not){ +	var old = expr, result = [], curLoop = set, match, anyFound, +		isXMLFilter = set && set[0] && isXML(set[0]); + +	while ( expr && set.length ) { +		for ( var type in Expr.filter ) { +			if ( (match = Expr.match[ type ].exec( expr )) != null ) { +				var filter = Expr.filter[ type ], found, item; +				anyFound = false; + +				if ( curLoop == result ) { +					result = []; +				} + +				if ( Expr.preFilter[ type ] ) { +					match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + +					if ( !match ) { +						anyFound = found = true; +					} else if ( match === true ) { +						continue; +					} +				} + +				if ( match ) { +					for ( var i = 0; (item = curLoop[i]) != null; i++ ) { +						if ( item ) { +							found = filter( item, match, i, curLoop ); +							var pass = not ^ !!found; + +							if ( inplace && found != null ) { +								if ( pass ) { +									anyFound = true; +								} else { +									curLoop[i] = false; +								} +							} else if ( pass ) { +								result.push( item ); +								anyFound = true; +							} +						} +					} +				} + +				if ( found !== undefined ) { +					if ( !inplace ) { +						curLoop = result; +					} + +					expr = expr.replace( Expr.match[ type ], "" ); + +					if ( !anyFound ) { +						return []; +					} + +					break; +				} +			} +		} + +		// Improper expression +		if ( expr == old ) { +			if ( anyFound == null ) { +				throw "Syntax error, unrecognized expression: " + expr; +			} else { +				break; +			} +		} + +		old = expr; +	} + +	return curLoop; +}; + +var Expr = Sizzle.selectors = { +	order: [ "ID", "NAME", "TAG" ], +	match: { +		ID: /#((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, +		CLASS: /\.((?:[\w\u00c0-\uFFFF_-]|\\.)+)/, +		NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF_-]|\\.)+)['"]*\]/, +		ATTR: /\[\s*((?:[\w\u00c0-\uFFFF_-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, +		TAG: /^((?:[\w\u00c0-\uFFFF\*_-]|\\.)+)/, +		CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, +		POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, +		PSEUDO: /:((?:[\w\u00c0-\uFFFF_-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ +	}, +	attrMap: { +		"class": "className", +		"for": "htmlFor" +	}, +	attrHandle: { +		href: function(elem){ +			return elem.getAttribute("href"); +		} +	}, +	relative: { +		"+": function(checkSet, part, isXML){ +			var isPartStr = typeof part === "string", +				isTag = isPartStr && !/\W/.test(part), +				isPartStrNotTag = isPartStr && !isTag; + +			if ( isTag && !isXML ) { +				part = part.toUpperCase(); +			} + +			for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { +				if ( (elem = checkSet[i]) ) { +					while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + +					checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? +						elem || false : +						elem === part; +				} +			} + +			if ( isPartStrNotTag ) { +				Sizzle.filter( part, checkSet, true ); +			} +		}, +		">": function(checkSet, part, isXML){ +			var isPartStr = typeof part === "string"; + +			if ( isPartStr && !/\W/.test(part) ) { +				part = isXML ? part : part.toUpperCase(); + +				for ( var i = 0, l = checkSet.length; i < l; i++ ) { +					var elem = checkSet[i]; +					if ( elem ) { +						var parent = elem.parentNode; +						checkSet[i] = parent.nodeName === part ? parent : false; +					} +				} +			} else { +				for ( var i = 0, l = checkSet.length; i < l; i++ ) { +					var elem = checkSet[i]; +					if ( elem ) { +						checkSet[i] = isPartStr ? +							elem.parentNode : +							elem.parentNode === part; +					} +				} + +				if ( isPartStr ) { +					Sizzle.filter( part, checkSet, true ); +				} +			} +		}, +		"": function(checkSet, part, isXML){ +			var doneName = done++, checkFn = dirCheck; + +			if ( !part.match(/\W/) ) { +				var nodeCheck = part = isXML ? part : part.toUpperCase(); +				checkFn = dirNodeCheck; +			} + +			checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); +		}, +		"~": function(checkSet, part, isXML){ +			var doneName = done++, checkFn = dirCheck; + +			if ( typeof part === "string" && !part.match(/\W/) ) { +				var nodeCheck = part = isXML ? part : part.toUpperCase(); +				checkFn = dirNodeCheck; +			} + +			checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); +		} +	}, +	find: { +		ID: function(match, context, isXML){ +			if ( typeof context.getElementById !== "undefined" && !isXML ) { +				var m = context.getElementById(match[1]); +				return m ? [m] : []; +			} +		}, +		NAME: function(match, context, isXML){ +			if ( typeof context.getElementsByName !== "undefined" ) { +				var ret = [], results = context.getElementsByName(match[1]); + +				for ( var i = 0, l = results.length; i < l; i++ ) { +					if ( results[i].getAttribute("name") === match[1] ) { +						ret.push( results[i] ); +					} +				} + +				return ret.length === 0 ? null : ret; +			} +		}, +		TAG: function(match, context){ +			return context.getElementsByTagName(match[1]); +		} +	}, +	preFilter: { +		CLASS: function(match, curLoop, inplace, result, not, isXML){ +			match = " " + match[1].replace(/\\/g, "") + " "; + +			if ( isXML ) { +				return match; +			} + +			for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { +				if ( elem ) { +					if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { +						if ( !inplace ) +							result.push( elem ); +					} else if ( inplace ) { +						curLoop[i] = false; +					} +				} +			} + +			return false; +		}, +		ID: function(match){ +			return match[1].replace(/\\/g, ""); +		}, +		TAG: function(match, curLoop){ +			for ( var i = 0; curLoop[i] === false; i++ ){} +			return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); +		}, +		CHILD: function(match){ +			if ( match[1] == "nth" ) { +				// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' +				var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( +					match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || +					!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + +				// calculate the numbers (first)n+(last) including if they are negative +				match[2] = (test[1] + (test[2] || 1)) - 0; +				match[3] = test[3] - 0; +			} + +			// TODO: Move to normal caching system +			match[0] = done++; + +			return match; +		}, +		ATTR: function(match, curLoop, inplace, result, not, isXML){ +			var name = match[1].replace(/\\/g, ""); +			 +			if ( !isXML && Expr.attrMap[name] ) { +				match[1] = Expr.attrMap[name]; +			} + +			if ( match[2] === "~=" ) { +				match[4] = " " + match[4] + " "; +			} + +			return match; +		}, +		PSEUDO: function(match, curLoop, inplace, result, not){ +			if ( match[1] === "not" ) { +				// If we're dealing with a complex expression, or a simple one +				if ( match[3].match(chunker).length > 1 || /^\w/.test(match[3]) ) { +					match[3] = Sizzle(match[3], null, null, curLoop); +				} else { +					var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); +					if ( !inplace ) { +						result.push.apply( result, ret ); +					} +					return false; +				} +			} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { +				return true; +			} +			 +			return match; +		}, +		POS: function(match){ +			match.unshift( true ); +			return match; +		} +	}, +	filters: { +		enabled: function(elem){ +			return elem.disabled === false && elem.type !== "hidden"; +		}, +		disabled: function(elem){ +			return elem.disabled === true; +		}, +		checked: function(elem){ +			return elem.checked === true; +		}, +		selected: function(elem){ +			// Accessing this property makes selected-by-default +			// options in Safari work properly +			elem.parentNode.selectedIndex; +			return elem.selected === true; +		}, +		parent: function(elem){ +			return !!elem.firstChild; +		}, +		empty: function(elem){ +			return !elem.firstChild; +		}, +		has: function(elem, i, match){ +			return !!Sizzle( match[3], elem ).length; +		}, +		header: function(elem){ +			return /h\d/i.test( elem.nodeName ); +		}, +		text: function(elem){ +			return "text" === elem.type; +		}, +		radio: function(elem){ +			return "radio" === elem.type; +		}, +		checkbox: function(elem){ +			return "checkbox" === elem.type; +		}, +		file: function(elem){ +			return "file" === elem.type; +		}, +		password: function(elem){ +			return "password" === elem.type; +		}, +		submit: function(elem){ +			return "submit" === elem.type; +		}, +		image: function(elem){ +			return "image" === elem.type; +		}, +		reset: function(elem){ +			return "reset" === elem.type; +		}, +		button: function(elem){ +			return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; +		}, +		input: function(elem){ +			return /input|select|textarea|button/i.test(elem.nodeName); +		} +	}, +	setFilters: { +		first: function(elem, i){ +			return i === 0; +		}, +		last: function(elem, i, match, array){ +			return i === array.length - 1; +		}, +		even: function(elem, i){ +			return i % 2 === 0; +		}, +		odd: function(elem, i){ +			return i % 2 === 1; +		}, +		lt: function(elem, i, match){ +			return i < match[3] - 0; +		}, +		gt: function(elem, i, match){ +			return i > match[3] - 0; +		}, +		nth: function(elem, i, match){ +			return match[3] - 0 == i; +		}, +		eq: function(elem, i, match){ +			return match[3] - 0 == i; +		} +	}, +	filter: { +		PSEUDO: function(elem, match, i, array){ +			var name = match[1], filter = Expr.filters[ name ]; + +			if ( filter ) { +				return filter( elem, i, match, array ); +			} else if ( name === "contains" ) { +				return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; +			} else if ( name === "not" ) { +				var not = match[3]; + +				for ( var i = 0, l = not.length; i < l; i++ ) { +					if ( not[i] === elem ) { +						return false; +					} +				} + +				return true; +			} +		}, +		CHILD: function(elem, match){ +			var type = match[1], node = elem; +			switch (type) { +				case 'only': +				case 'first': +					while (node = node.previousSibling)  { +						if ( node.nodeType === 1 ) return false; +					} +					if ( type == 'first') return true; +					node = elem; +				case 'last': +					while (node = node.nextSibling)  { +						if ( node.nodeType === 1 ) return false; +					} +					return true; +				case 'nth': +					var first = match[2], last = match[3]; + +					if ( first == 1 && last == 0 ) { +						return true; +					} +					 +					var doneName = match[0], +						parent = elem.parentNode; +	 +					if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { +						var count = 0; +						for ( node = parent.firstChild; node; node = node.nextSibling ) { +							if ( node.nodeType === 1 ) { +								node.nodeIndex = ++count; +							} +						}  +						parent.sizcache = doneName; +					} +					 +					var diff = elem.nodeIndex - last; +					if ( first == 0 ) { +						return diff == 0; +					} else { +						return ( diff % first == 0 && diff / first >= 0 ); +					} +			} +		}, +		ID: function(elem, match){ +			return elem.nodeType === 1 && elem.getAttribute("id") === match; +		}, +		TAG: function(elem, match){ +			return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; +		}, +		CLASS: function(elem, match){ +			return (" " + (elem.className || elem.getAttribute("class")) + " ") +				.indexOf( match ) > -1; +		}, +		ATTR: function(elem, match){ +			var name = match[1], +				result = Expr.attrHandle[ name ] ? +					Expr.attrHandle[ name ]( elem ) : +					elem[ name ] != null ? +						elem[ name ] : +						elem.getAttribute( name ), +				value = result + "", +				type = match[2], +				check = match[4]; + +			return result == null ? +				type === "!=" : +				type === "=" ? +				value === check : +				type === "*=" ? +				value.indexOf(check) >= 0 : +				type === "~=" ? +				(" " + value + " ").indexOf(check) >= 0 : +				!check ? +				value && result !== false : +				type === "!=" ? +				value != check : +				type === "^=" ? +				value.indexOf(check) === 0 : +				type === "$=" ? +				value.substr(value.length - check.length) === check : +				type === "|=" ? +				value === check || value.substr(0, check.length + 1) === check + "-" : +				false; +		}, +		POS: function(elem, match, i, array){ +			var name = match[2], filter = Expr.setFilters[ name ]; + +			if ( filter ) { +				return filter( elem, i, match, array ); +			} +		} +	} +}; + +var origPOS = Expr.match.POS; + +for ( var type in Expr.match ) { +	Expr.match[ type ] = RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); +} + +var makeArray = function(array, results) { +	array = Array.prototype.slice.call( array ); + +	if ( results ) { +		results.push.apply( results, array ); +		return results; +	} +	 +	return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +try { +	Array.prototype.slice.call( document.documentElement.childNodes ); + +// Provide a fallback method if it does not work +} catch(e){ +	makeArray = function(array, results) { +		var ret = results || []; + +		if ( toString.call(array) === "[object Array]" ) { +			Array.prototype.push.apply( ret, array ); +		} else { +			if ( typeof array.length === "number" ) { +				for ( var i = 0, l = array.length; i < l; i++ ) { +					ret.push( array[i] ); +				} +			} else { +				for ( var i = 0; array[i]; i++ ) { +					ret.push( array[i] ); +				} +			} +		} + +		return ret; +	}; +} + +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { +	sortOrder = function( a, b ) { +		var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; +		if ( ret === 0 ) { +			hasDuplicate = true; +		} +		return ret; +	}; +} else if ( "sourceIndex" in document.documentElement ) { +	sortOrder = function( a, b ) { +		var ret = a.sourceIndex - b.sourceIndex; +		if ( ret === 0 ) { +			hasDuplicate = true; +		} +		return ret; +	}; +} else if ( document.createRange ) { +	sortOrder = function( a, b ) { +		var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); +		aRange.selectNode(a); +		aRange.collapse(true); +		bRange.selectNode(b); +		bRange.collapse(true); +		var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); +		if ( ret === 0 ) { +			hasDuplicate = true; +		} +		return ret; +	}; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ +	// We're going to inject a fake input element with a specified name +	var form = document.createElement("form"), +		id = "script" + (new Date).getTime(); +	form.innerHTML = "<input name='" + id + "'/>"; + +	// Inject it into the root element, check its status, and remove it quickly +	var root = document.documentElement; +	root.insertBefore( form, root.firstChild ); + +	// The workaround has to do additional checks after a getElementById +	// Which slows things down for other browsers (hence the branching) +	if ( !!document.getElementById( id ) ) { +		Expr.find.ID = function(match, context, isXML){ +			if ( typeof context.getElementById !== "undefined" && !isXML ) { +				var m = context.getElementById(match[1]); +				return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; +			} +		}; + +		Expr.filter.ID = function(elem, match){ +			var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); +			return elem.nodeType === 1 && node && node.nodeValue === match; +		}; +	} + +	root.removeChild( form ); +})(); + +(function(){ +	// Check to see if the browser returns only elements +	// when doing getElementsByTagName("*") + +	// Create a fake element +	var div = document.createElement("div"); +	div.appendChild( document.createComment("") ); + +	// Make sure no comments are found +	if ( div.getElementsByTagName("*").length > 0 ) { +		Expr.find.TAG = function(match, context){ +			var results = context.getElementsByTagName(match[1]); + +			// Filter out possible comments +			if ( match[1] === "*" ) { +				var tmp = []; + +				for ( var i = 0; results[i]; i++ ) { +					if ( results[i].nodeType === 1 ) { +						tmp.push( results[i] ); +					} +				} + +				results = tmp; +			} + +			return results; +		}; +	} + +	// Check to see if an attribute returns normalized href attributes +	div.innerHTML = "<a href='#'></a>"; +	if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && +			div.firstChild.getAttribute("href") !== "#" ) { +		Expr.attrHandle.href = function(elem){ +			return elem.getAttribute("href", 2); +		}; +	} +})(); + +if ( document.querySelectorAll ) (function(){ +	var oldSizzle = Sizzle, div = document.createElement("div"); +	div.innerHTML = "<p class='TEST'></p>"; + +	// Safari can't handle uppercase or unicode characters when +	// in quirks mode. +	if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { +		return; +	} +	 +	Sizzle = function(query, context, extra, seed){ +		context = context || document; + +		// Only use querySelectorAll on non-XML documents +		// (ID selectors don't work in non-HTML documents) +		if ( !seed && context.nodeType === 9 && !isXML(context) ) { +			try { +				return makeArray( context.querySelectorAll(query), extra ); +			} catch(e){} +		} +		 +		return oldSizzle(query, context, extra, seed); +	}; + +	Sizzle.find = oldSizzle.find; +	Sizzle.filter = oldSizzle.filter; +	Sizzle.selectors = oldSizzle.selectors; +	Sizzle.matches = oldSizzle.matches; +})(); + +if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ +	var div = document.createElement("div"); +	div.innerHTML = "<div class='test e'></div><div class='test'></div>"; + +	// Opera can't find a second classname (in 9.6) +	if ( div.getElementsByClassName("e").length === 0 ) +		return; + +	// Safari caches class attributes, doesn't catch changes (in 3.2) +	div.lastChild.className = "e"; + +	if ( div.getElementsByClassName("e").length === 1 ) +		return; + +	Expr.order.splice(1, 0, "CLASS"); +	Expr.find.CLASS = function(match, context, isXML) { +		if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { +			return context.getElementsByClassName(match[1]); +		} +	}; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { +	var sibDir = dir == "previousSibling" && !isXML; +	for ( var i = 0, l = checkSet.length; i < l; i++ ) { +		var elem = checkSet[i]; +		if ( elem ) { +			if ( sibDir && elem.nodeType === 1 ){ +				elem.sizcache = doneName; +				elem.sizset = i; +			} +			elem = elem[dir]; +			var match = false; + +			while ( elem ) { +				if ( elem.sizcache === doneName ) { +					match = checkSet[elem.sizset]; +					break; +				} + +				if ( elem.nodeType === 1 && !isXML ){ +					elem.sizcache = doneName; +					elem.sizset = i; +				} + +				if ( elem.nodeName === cur ) { +					match = elem; +					break; +				} + +				elem = elem[dir]; +			} + +			checkSet[i] = match; +		} +	} +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { +	var sibDir = dir == "previousSibling" && !isXML; +	for ( var i = 0, l = checkSet.length; i < l; i++ ) { +		var elem = checkSet[i]; +		if ( elem ) { +			if ( sibDir && elem.nodeType === 1 ) { +				elem.sizcache = doneName; +				elem.sizset = i; +			} +			elem = elem[dir]; +			var match = false; + +			while ( elem ) { +				if ( elem.sizcache === doneName ) { +					match = checkSet[elem.sizset]; +					break; +				} + +				if ( elem.nodeType === 1 ) { +					if ( !isXML ) { +						elem.sizcache = doneName; +						elem.sizset = i; +					} +					if ( typeof cur !== "string" ) { +						if ( elem === cur ) { +							match = true; +							break; +						} + +					} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { +						match = elem; +						break; +					} +				} + +				elem = elem[dir]; +			} + +			checkSet[i] = match; +		} +	} +} + +var contains = document.compareDocumentPosition ?  function(a, b){ +	return a.compareDocumentPosition(b) & 16; +} : function(a, b){ +	return a !== b && (a.contains ? a.contains(b) : true); +}; + +var isXML = function(elem){ +	return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || +		!!elem.ownerDocument && isXML( elem.ownerDocument ); +}; + +var posProcess = function(selector, context){ +	var tmpSet = [], later = "", match, +		root = context.nodeType ? [context] : context; + +	// Position selectors must be done after the filter +	// And so must :not(positional) so we move all PSEUDOs to the end +	while ( (match = Expr.match.PSEUDO.exec( selector )) ) { +		later += match[0]; +		selector = selector.replace( Expr.match.PSEUDO, "" ); +	} + +	selector = Expr.relative[selector] ? selector + "*" : selector; + +	for ( var i = 0, l = root.length; i < l; i++ ) { +		Sizzle( selector, root[i], tmpSet ); +	} + +	return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +jQuery.find = Sizzle; +jQuery.filter = Sizzle.filter; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; + +Sizzle.selectors.filters.hidden = function(elem){ +	return elem.offsetWidth === 0 || elem.offsetHeight === 0; +}; + +Sizzle.selectors.filters.visible = function(elem){ +	return elem.offsetWidth > 0 || elem.offsetHeight > 0; +}; + +Sizzle.selectors.filters.animated = function(elem){ +	return jQuery.grep(jQuery.timers, function(fn){ +		return elem === fn.elem; +	}).length; +}; + +jQuery.multiFilter = function( expr, elems, not ) { +	if ( not ) { +		expr = ":not(" + expr + ")"; +	} + +	return Sizzle.matches(expr, elems); +}; + +jQuery.dir = function( elem, dir ){ +	var matched = [], cur = elem[dir]; +	while ( cur && cur != document ) { +		if ( cur.nodeType == 1 ) +			matched.push( cur ); +		cur = cur[dir]; +	} +	return matched; +}; + +jQuery.nth = function(cur, result, dir, elem){ +	result = result || 1; +	var num = 0; + +	for ( ; cur; cur = cur[dir] ) +		if ( cur.nodeType == 1 && ++num == result ) +			break; + +	return cur; +}; + +jQuery.sibling = function(n, elem){ +	var r = []; + +	for ( ; n; n = n.nextSibling ) { +		if ( n.nodeType == 1 && n != elem ) +			r.push( n ); +	} + +	return r; +}; + +return; + +window.Sizzle = Sizzle; + +})(); +/* + * A number of helper functions used for managing events. + * Many of the ideas behind this code originated from + * Dean Edwards' addEvent library. + */ +jQuery.event = { + +	// Bind an event to an element +	// Original by Dean Edwards +	add: function(elem, types, handler, data) { +		if ( elem.nodeType == 3 || elem.nodeType == 8 ) +			return; + +		// For whatever reason, IE has trouble passing the window object +		// around, causing it to be cloned in the process +		if ( elem.setInterval && elem != window ) +			elem = window; + +		// Make sure that the function being executed has a unique ID +		if ( !handler.guid ) +			handler.guid = this.guid++; + +		// if data is passed, bind to handler +		if ( data !== undefined ) { +			// Create temporary function pointer to original handler +			var fn = handler; + +			// Create unique handler function, wrapped around original handler +			handler = this.proxy( fn ); + +			// Store data in unique handler +			handler.data = data; +		} + +		// Init the element's event structure +		var events = jQuery.data(elem, "events") || jQuery.data(elem, "events", {}), +			handle = jQuery.data(elem, "handle") || jQuery.data(elem, "handle", function(){ +				// Handle the second event of a trigger and when +				// an event is called after a page has unloaded +				return typeof jQuery !== "undefined" && !jQuery.event.triggered ? +					jQuery.event.handle.apply(arguments.callee.elem, arguments) : +					undefined; +			}); +		// Add elem as a property of the handle function +		// This is to prevent a memory leak with non-native +		// event in IE. +		handle.elem = elem; + +		// Handle multiple events separated by a space +		// jQuery(...).bind("mouseover mouseout", fn); +		jQuery.each(types.split(/\s+/), function(index, type) { +			// Namespaced event handlers +			var namespaces = type.split("."); +			type = namespaces.shift(); +			handler.type = namespaces.slice().sort().join("."); + +			// Get the current list of functions bound to this event +			var handlers = events[type]; +			 +			if ( jQuery.event.specialAll[type] ) +				jQuery.event.specialAll[type].setup.call(elem, data, namespaces); + +			// Init the event handler queue +			if (!handlers) { +				handlers = events[type] = {}; + +				// Check for a special event handler +				// Only use addEventListener/attachEvent if the special +				// events handler returns false +				if ( !jQuery.event.special[type] || jQuery.event.special[type].setup.call(elem, data, namespaces) === false ) { +					// Bind the global event handler to the element +					if (elem.addEventListener) +						elem.addEventListener(type, handle, false); +					else if (elem.attachEvent) +						elem.attachEvent("on" + type, handle); +				} +			} + +			// Add the function to the element's handler list +			handlers[handler.guid] = handler; + +			// Keep track of which events have been used, for global triggering +			jQuery.event.global[type] = true; +		}); + +		// Nullify elem to prevent memory leaks in IE +		elem = null; +	}, + +	guid: 1, +	global: {}, + +	// Detach an event or set of events from an element +	remove: function(elem, types, handler) { +		// don't do events on text and comment nodes +		if ( elem.nodeType == 3 || elem.nodeType == 8 ) +			return; + +		var events = jQuery.data(elem, "events"), ret, index; + +		if ( events ) { +			// Unbind all events for the element +			if ( types === undefined || (typeof types === "string" && types.charAt(0) == ".") ) +				for ( var type in events ) +					this.remove( elem, type + (types || "") ); +			else { +				// types is actually an event object here +				if ( types.type ) { +					handler = types.handler; +					types = types.type; +				} + +				// Handle multiple events seperated by a space +				// jQuery(...).unbind("mouseover mouseout", fn); +				jQuery.each(types.split(/\s+/), function(index, type){ +					// Namespaced event handlers +					var namespaces = type.split("."); +					type = namespaces.shift(); +					var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)"); + +					if ( events[type] ) { +						// remove the given handler for the given type +						if ( handler ) +							delete events[type][handler.guid]; + +						// remove all handlers for the given type +						else +							for ( var handle in events[type] ) +								// Handle the removal of namespaced events +								if ( namespace.test(events[type][handle].type) ) +									delete events[type][handle]; +									 +						if ( jQuery.event.specialAll[type] ) +							jQuery.event.specialAll[type].teardown.call(elem, namespaces); + +						// remove generic event handler if no more handlers exist +						for ( ret in events[type] ) break; +						if ( !ret ) { +							if ( !jQuery.event.special[type] || jQuery.event.special[type].teardown.call(elem, namespaces) === false ) { +								if (elem.removeEventListener) +									elem.removeEventListener(type, jQuery.data(elem, "handle"), false); +								else if (elem.detachEvent) +									elem.detachEvent("on" + type, jQuery.data(elem, "handle")); +							} +							ret = null; +							delete events[type]; +						} +					} +				}); +			} + +			// Remove the expando if it's no longer used +			for ( ret in events ) break; +			if ( !ret ) { +				var handle = jQuery.data( elem, "handle" ); +				if ( handle ) handle.elem = null; +				jQuery.removeData( elem, "events" ); +				jQuery.removeData( elem, "handle" ); +			} +		} +	}, + +	// bubbling is internal +	trigger: function( event, data, elem, bubbling ) { +		// Event object or event type +		var type = event.type || event; + +		if( !bubbling ){ +			event = typeof event === "object" ? +				// jQuery.Event object +				event[expando] ? event : +				// Object literal +				jQuery.extend( jQuery.Event(type), event ) : +				// Just the event type (string) +				jQuery.Event(type); + +			if ( type.indexOf("!") >= 0 ) { +				event.type = type = type.slice(0, -1); +				event.exclusive = true; +			} + +			// Handle a global trigger +			if ( !elem ) { +				// Don't bubble custom events when global (to avoid too much overhead) +				event.stopPropagation(); +				// Only trigger if we've ever bound an event for it +				if ( this.global[type] ) +					jQuery.each( jQuery.cache, function(){ +						if ( this.events && this.events[type] ) +							jQuery.event.trigger( event, data, this.handle.elem ); +					}); +			} + +			// Handle triggering a single element + +			// don't do events on text and comment nodes +			if ( !elem || elem.nodeType == 3 || elem.nodeType == 8 ) +				return undefined; +			 +			// Clean up in case it is reused +			event.result = undefined; +			event.target = elem; +			 +			// Clone the incoming data, if any +			data = jQuery.makeArray(data); +			data.unshift( event ); +		} + +		event.currentTarget = elem; + +		// Trigger the event, it is assumed that "handle" is a function +		var handle = jQuery.data(elem, "handle"); +		if ( handle ) +			handle.apply( elem, data ); + +		// Handle triggering native .onfoo handlers (and on links since we don't call .click() for links) +		if ( (!elem[type] || (jQuery.nodeName(elem, 'a') && type == "click")) && elem["on"+type] && elem["on"+type].apply( elem, data ) === false ) +			event.result = false; + +		// Trigger the native events (except for clicks on links) +		if ( !bubbling && elem[type] && !event.isDefaultPrevented() && !(jQuery.nodeName(elem, 'a') && type == "click") ) { +			this.triggered = true; +			try { +				elem[ type ](); +			// prevent IE from throwing an error for some hidden elements +			} catch (e) {} +		} + +		this.triggered = false; + +		if ( !event.isPropagationStopped() ) { +			var parent = elem.parentNode || elem.ownerDocument; +			if ( parent ) +				jQuery.event.trigger(event, data, parent, true); +		} +	}, + +	handle: function(event) { +		// returned undefined or false +		var all, handlers; + +		event = arguments[0] = jQuery.event.fix( event || window.event ); +		event.currentTarget = this; +		 +		// Namespaced event handlers +		var namespaces = event.type.split("."); +		event.type = namespaces.shift(); + +		// Cache this now, all = true means, any handler +		all = !namespaces.length && !event.exclusive; +		 +		var namespace = RegExp("(^|\\.)" + namespaces.slice().sort().join(".*\\.") + "(\\.|$)"); + +		handlers = ( jQuery.data(this, "events") || {} )[event.type]; + +		for ( var j in handlers ) { +			var handler = handlers[j]; + +			// Filter the functions by class +			if ( all || namespace.test(handler.type) ) { +				// Pass in a reference to the handler function itself +				// So that we can later remove it +				event.handler = handler; +				event.data = handler.data; + +				var ret = handler.apply(this, arguments); + +				if( ret !== undefined ){ +					event.result = ret; +					if ( ret === false ) { +						event.preventDefault(); +						event.stopPropagation(); +					} +				} + +				if( event.isImmediatePropagationStopped() ) +					break; + +			} +		} +	}, + +	props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "), + +	fix: function(event) { +		if ( event[expando] ) +			return event; + +		// store a copy of the original event object +		// and "clone" to set read-only properties +		var originalEvent = event; +		event = jQuery.Event( originalEvent ); + +		for ( var i = this.props.length, prop; i; ){ +			prop = this.props[ --i ]; +			event[ prop ] = originalEvent[ prop ]; +		} + +		// Fix target property, if necessary +		if ( !event.target ) +			event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either + +		// check if target is a textnode (safari) +		if ( event.target.nodeType == 3 ) +			event.target = event.target.parentNode; + +		// Add relatedTarget, if necessary +		if ( !event.relatedTarget && event.fromElement ) +			event.relatedTarget = event.fromElement == event.target ? event.toElement : event.fromElement; + +		// Calculate pageX/Y if missing and clientX/Y available +		if ( event.pageX == null && event.clientX != null ) { +			var doc = document.documentElement, body = document.body; +			event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0); +			event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0); +		} + +		// Add which for key events +		if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) +			event.which = event.charCode || event.keyCode; + +		// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs) +		if ( !event.metaKey && event.ctrlKey ) +			event.metaKey = event.ctrlKey; + +		// Add which for click: 1 == left; 2 == middle; 3 == right +		// Note: button is not normalized, so don't use it +		if ( !event.which && event.button ) +			event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) )); + +		return event; +	}, + +	proxy: function( fn, proxy ){ +		proxy = proxy || function(){ return fn.apply(this, arguments); }; +		// Set the guid of unique handler to the same of original handler, so it can be removed +		proxy.guid = fn.guid = fn.guid || proxy.guid || this.guid++; +		// So proxy can be declared as an argument +		return proxy; +	}, + +	special: { +		ready: { +			// Make sure the ready event is setup +			setup: bindReady, +			teardown: function() {} +		} +	}, +	 +	specialAll: { +		live: { +			setup: function( selector, namespaces ){ +				jQuery.event.add( this, namespaces[0], liveHandler ); +			}, +			teardown:  function( namespaces ){ +				if ( namespaces.length ) { +					var remove = 0, name = RegExp("(^|\\.)" + namespaces[0] + "(\\.|$)"); +					 +					jQuery.each( (jQuery.data(this, "events").live || {}), function(){ +						if ( name.test(this.type) ) +							remove++; +					}); +					 +					if ( remove < 1 ) +						jQuery.event.remove( this, namespaces[0], liveHandler ); +				} +			} +		} +	} +}; + +jQuery.Event = function( src ){ +	// Allow instantiation without the 'new' keyword +	if( !this.preventDefault ) +		return new jQuery.Event(src); +	 +	// Event object +	if( src && src.type ){ +		this.originalEvent = src; +		this.type = src.type; +	// Event type +	}else +		this.type = src; + +	// timeStamp is buggy for some events on Firefox(#3843) +	// So we won't rely on the native value +	this.timeStamp = now(); +	 +	// Mark it as fixed +	this[expando] = true; +}; + +function returnFalse(){ +	return false; +} +function returnTrue(){ +	return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { +	preventDefault: function() { +		this.isDefaultPrevented = returnTrue; + +		var e = this.originalEvent; +		if( !e ) +			return; +		// if preventDefault exists run it on the original event +		if (e.preventDefault) +			e.preventDefault(); +		// otherwise set the returnValue property of the original event to false (IE) +		e.returnValue = false; +	}, +	stopPropagation: function() { +		this.isPropagationStopped = returnTrue; + +		var e = this.originalEvent; +		if( !e ) +			return; +		// if stopPropagation exists run it on the original event +		if (e.stopPropagation) +			e.stopPropagation(); +		// otherwise set the cancelBubble property of the original event to true (IE) +		e.cancelBubble = true; +	}, +	stopImmediatePropagation:function(){ +		this.isImmediatePropagationStopped = returnTrue; +		this.stopPropagation(); +	}, +	isDefaultPrevented: returnFalse, +	isPropagationStopped: returnFalse, +	isImmediatePropagationStopped: returnFalse +}; +// Checks if an event happened on an element within another element +// Used in jQuery.event.special.mouseenter and mouseleave handlers +var withinElement = function(event) { +	// Check if mouse(over|out) are still within the same parent element +	var parent = event.relatedTarget; +	// Traverse up the tree +	while ( parent && parent != this ) +		try { parent = parent.parentNode; } +		catch(e) { parent = this; } +	 +	if( parent != this ){ +		// set the correct event type +		event.type = event.data; +		// handle event if we actually just moused on to a non sub-element +		jQuery.event.handle.apply( this, arguments ); +	} +}; +	 +jQuery.each({  +	mouseover: 'mouseenter',  +	mouseout: 'mouseleave' +}, function( orig, fix ){ +	jQuery.event.special[ fix ] = { +		setup: function(){ +			jQuery.event.add( this, orig, withinElement, fix ); +		}, +		teardown: function(){ +			jQuery.event.remove( this, orig, withinElement ); +		} +	};			    +}); + +jQuery.fn.extend({ +	bind: function( type, data, fn ) { +		return type == "unload" ? this.one(type, data, fn) : this.each(function(){ +			jQuery.event.add( this, type, fn || data, fn && data ); +		}); +	}, + +	one: function( type, data, fn ) { +		var one = jQuery.event.proxy( fn || data, function(event) { +			jQuery(this).unbind(event, one); +			return (fn || data).apply( this, arguments ); +		}); +		return this.each(function(){ +			jQuery.event.add( this, type, one, fn && data); +		}); +	}, + +	unbind: function( type, fn ) { +		return this.each(function(){ +			jQuery.event.remove( this, type, fn ); +		}); +	}, + +	trigger: function( type, data ) { +		return this.each(function(){ +			jQuery.event.trigger( type, data, this ); +		}); +	}, + +	triggerHandler: function( type, data ) { +		if( this[0] ){ +			var event = jQuery.Event(type); +			event.preventDefault(); +			event.stopPropagation(); +			jQuery.event.trigger( event, data, this[0] ); +			return event.result; +		}		 +	}, + +	toggle: function( fn ) { +		// Save reference to arguments for access in closure +		var args = arguments, i = 1; + +		// link all the functions, so any of them can unbind this click handler +		while( i < args.length ) +			jQuery.event.proxy( fn, args[i++] ); + +		return this.click( jQuery.event.proxy( fn, function(event) { +			// Figure out which function to execute +			this.lastToggle = ( this.lastToggle || 0 ) % i; + +			// Make sure that clicks stop +			event.preventDefault(); + +			// and execute the function +			return args[ this.lastToggle++ ].apply( this, arguments ) || false; +		})); +	}, + +	hover: function(fnOver, fnOut) { +		return this.mouseenter(fnOver).mouseleave(fnOut); +	}, + +	ready: function(fn) { +		// Attach the listeners +		bindReady(); + +		// If the DOM is already ready +		if ( jQuery.isReady ) +			// Execute the function immediately +			fn.call( document, jQuery ); + +		// Otherwise, remember the function for later +		else +			// Add the function to the wait list +			jQuery.readyList.push( fn ); + +		return this; +	}, +	 +	live: function( type, fn ){ +		var proxy = jQuery.event.proxy( fn ); +		proxy.guid += this.selector + type; + +		jQuery(document).bind( liveConvert(type, this.selector), this.selector, proxy ); + +		return this; +	}, +	 +	die: function( type, fn ){ +		jQuery(document).unbind( liveConvert(type, this.selector), fn ? { guid: fn.guid + this.selector + type } : null ); +		return this; +	} +}); + +function liveHandler( event ){ +	var check = RegExp("(^|\\.)" + event.type + "(\\.|$)"), +		stop = true, +		elems = []; + +	jQuery.each(jQuery.data(this, "events").live || [], function(i, fn){ +		if ( check.test(fn.type) ) { +			var elem = jQuery(event.target).closest(fn.data)[0]; +			if ( elem ) +				elems.push({ elem: elem, fn: fn }); +		} +	}); + +	elems.sort(function(a,b) { +		return jQuery.data(a.elem, "closest") - jQuery.data(b.elem, "closest"); +	}); +	 +	jQuery.each(elems, function(){ +		if ( this.fn.call(this.elem, event, this.fn.data) === false ) +			return (stop = false); +	}); + +	return stop; +} + +function liveConvert(type, selector){ +	return ["live", type, selector.replace(/\./g, "`").replace(/ /g, "|")].join("."); +} + +jQuery.extend({ +	isReady: false, +	readyList: [], +	// Handle when the DOM is ready +	ready: function() { +		// Make sure that the DOM is not already loaded +		if ( !jQuery.isReady ) { +			// Remember that the DOM is ready +			jQuery.isReady = true; + +			// If there are functions bound, to execute +			if ( jQuery.readyList ) { +				// Execute all of them +				jQuery.each( jQuery.readyList, function(){ +					this.call( document, jQuery ); +				}); + +				// Reset the list of functions +				jQuery.readyList = null; +			} + +			// Trigger any bound ready events +			jQuery(document).triggerHandler("ready"); +		} +	} +}); + +var readyBound = false; + +function bindReady(){ +	if ( readyBound ) return; +	readyBound = true; + +	// Mozilla, Opera and webkit nightlies currently support this event +	if ( document.addEventListener ) { +		// Use the handy event callback +		document.addEventListener( "DOMContentLoaded", function(){ +			document.removeEventListener( "DOMContentLoaded", arguments.callee, false ); +			jQuery.ready(); +		}, false ); + +	// If IE event model is used +	} else if ( document.attachEvent ) { +		// ensure firing before onload, +		// maybe late but safe also for iframes +		document.attachEvent("onreadystatechange", function(){ +			if ( document.readyState === "complete" ) { +				document.detachEvent( "onreadystatechange", arguments.callee ); +				jQuery.ready(); +			} +		}); + +		// If IE and not an iframe +		// continually check to see if the document is ready +		if ( document.documentElement.doScroll && window == window.top ) (function(){ +			if ( jQuery.isReady ) return; + +			try { +				// If IE is used, use the trick by Diego Perini +				// http://javascript.nwbox.com/IEContentLoaded/ +				document.documentElement.doScroll("left"); +			} catch( error ) { +				setTimeout( arguments.callee, 0 ); +				return; +			} + +			// and execute any waiting functions +			jQuery.ready(); +		})(); +	} + +	// A fallback to window.onload, that will always work +	jQuery.event.add( window, "load", jQuery.ready ); +} + +jQuery.each( ("blur,focus,load,resize,scroll,unload,click,dblclick," + +	"mousedown,mouseup,mousemove,mouseover,mouseout,mouseenter,mouseleave," + +	"change,select,submit,keydown,keypress,keyup,error").split(","), function(i, name){ + +	// Handle event binding +	jQuery.fn[name] = function(fn){ +		return fn ? this.bind(name, fn) : this.trigger(name); +	}; +}); + +// Prevent memory leaks in IE +// And prevent errors on refresh with events like mouseover in other browsers +// Window isn't included so as not to unbind existing unload events +jQuery( window ).bind( 'unload', function(){  +	for ( var id in jQuery.cache ) +		// Skip the window +		if ( id != 1 && jQuery.cache[ id ].handle ) +			jQuery.event.remove( jQuery.cache[ id ].handle.elem ); +});  +(function(){ + +	jQuery.support = {}; + +	var root = document.documentElement, +		script = document.createElement("script"), +		div = document.createElement("div"), +		id = "script" + (new Date).getTime(); + +	div.style.display = "none"; +	div.innerHTML = '   <link/><table></table><a href="/a" style="color:red;float:left;opacity:.5;">a</a><select><option>text</option></select><object><param/></object>'; + +	var all = div.getElementsByTagName("*"), +		a = div.getElementsByTagName("a")[0]; + +	// Can't get basic test support +	if ( !all || !all.length || !a ) { +		return; +	} + +	jQuery.support = { +		// IE strips leading whitespace when .innerHTML is used +		leadingWhitespace: div.firstChild.nodeType == 3, +		 +		// Make sure that tbody elements aren't automatically inserted +		// IE will insert them into empty tables +		tbody: !div.getElementsByTagName("tbody").length, +		 +		// Make sure that you can get all elements in an <object> element +		// IE 7 always returns no results +		objectAll: !!div.getElementsByTagName("object")[0] +			.getElementsByTagName("*").length, +		 +		// Make sure that link elements get serialized correctly by innerHTML +		// This requires a wrapper element in IE +		htmlSerialize: !!div.getElementsByTagName("link").length, +		 +		// Get the style information from getAttribute +		// (IE uses .cssText insted) +		style: /red/.test( a.getAttribute("style") ), +		 +		// Make sure that URLs aren't manipulated +		// (IE normalizes it by default) +		hrefNormalized: a.getAttribute("href") === "/a", +		 +		// Make sure that element opacity exists +		// (IE uses filter instead) +		opacity: a.style.opacity === "0.5", +		 +		// Verify style float existence +		// (IE uses styleFloat instead of cssFloat) +		cssFloat: !!a.style.cssFloat, + +		// Will be defined later +		scriptEval: false, +		noCloneEvent: true, +		boxModel: null +	}; +	 +	script.type = "text/javascript"; +	try { +		script.appendChild( document.createTextNode( "window." + id + "=1;" ) ); +	} catch(e){} + +	root.insertBefore( script, root.firstChild ); +	 +	// Make sure that the execution of code works by injecting a script +	// tag with appendChild/createTextNode +	// (IE doesn't support this, fails, and uses .text instead) +	if ( window[ id ] ) { +		jQuery.support.scriptEval = true; +		delete window[ id ]; +	} + +	root.removeChild( script ); + +	if ( div.attachEvent && div.fireEvent ) { +		div.attachEvent("onclick", function(){ +			// Cloning a node shouldn't copy over any +			// bound event handlers (IE does this) +			jQuery.support.noCloneEvent = false; +			div.detachEvent("onclick", arguments.callee); +		}); +		div.cloneNode(true).fireEvent("onclick"); +	} + +	// Figure out if the W3C box model works as expected +	// document.body must exist before we can do this +	jQuery(function(){ +		var div = document.createElement("div"); +		div.style.width = div.style.paddingLeft = "1px"; + +		document.body.appendChild( div ); +		jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2; +		document.body.removeChild( div ).style.display = 'none'; +	}); +})(); + +var styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat"; + +jQuery.props = { +	"for": "htmlFor", +	"class": "className", +	"float": styleFloat, +	cssFloat: styleFloat, +	styleFloat: styleFloat, +	readonly: "readOnly", +	maxlength: "maxLength", +	cellspacing: "cellSpacing", +	rowspan: "rowSpan", +	tabindex: "tabIndex" +}; +jQuery.fn.extend({ +	// Keep a copy of the old load +	_load: jQuery.fn.load, + +	load: function( url, params, callback ) { +		if ( typeof url !== "string" ) +			return this._load( url ); + +		var off = url.indexOf(" "); +		if ( off >= 0 ) { +			var selector = url.slice(off, url.length); +			url = url.slice(0, off); +		} + +		// Default to a GET request +		var type = "GET"; + +		// If the second parameter was provided +		if ( params ) +			// If it's a function +			if ( jQuery.isFunction( params ) ) { +				// We assume that it's the callback +				callback = params; +				params = null; + +			// Otherwise, build a param string +			} else if( typeof params === "object" ) { +				params = jQuery.param( params ); +				type = "POST"; +			} + +		var self = this; + +		// Request the remote document +		jQuery.ajax({ +			url: url, +			type: type, +			dataType: "html", +			data: params, +			complete: function(res, status){ +				// If successful, inject the HTML into all the matched elements +				if ( status == "success" || status == "notmodified" ) +					// See if a selector was specified +					self.html( selector ? +						// Create a dummy div to hold the results +						jQuery("<div/>") +							// inject the contents of the document in, removing the scripts +							// to avoid any 'Permission Denied' errors in IE +							.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, "")) + +							// Locate the specified elements +							.find(selector) : + +						// If not, just inject the full result +						res.responseText ); + +				if( callback ) +					self.each( callback, [res.responseText, status, res] ); +			} +		}); +		return this; +	}, + +	serialize: function() { +		return jQuery.param(this.serializeArray()); +	}, +	serializeArray: function() { +		return this.map(function(){ +			return this.elements ? jQuery.makeArray(this.elements) : this; +		}) +		.filter(function(){ +			return this.name && !this.disabled && +				(this.checked || /select|textarea/i.test(this.nodeName) || +					/text|hidden|password|search/i.test(this.type)); +		}) +		.map(function(i, elem){ +			var val = jQuery(this).val(); +			return val == null ? null : +				jQuery.isArray(val) ? +					jQuery.map( val, function(val, i){ +						return {name: elem.name, value: val}; +					}) : +					{name: elem.name, value: val}; +		}).get(); +	} +}); + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( "ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","), function(i,o){ +	jQuery.fn[o] = function(f){ +		return this.bind(o, f); +	}; +}); + +var jsc = now(); + +jQuery.extend({ +   +	get: function( url, data, callback, type ) { +		// shift arguments if data argument was ommited +		if ( jQuery.isFunction( data ) ) { +			callback = data; +			data = null; +		} + +		return jQuery.ajax({ +			type: "GET", +			url: url, +			data: data, +			success: callback, +			dataType: type +		}); +	}, + +	getScript: function( url, callback ) { +		return jQuery.get(url, null, callback, "script"); +	}, + +	getJSON: function( url, data, callback ) { +		return jQuery.get(url, data, callback, "json"); +	}, + +	post: function( url, data, callback, type ) { +		if ( jQuery.isFunction( data ) ) { +			callback = data; +			data = {}; +		} + +		return jQuery.ajax({ +			type: "POST", +			url: url, +			data: data, +			success: callback, +			dataType: type +		}); +	}, + +	ajaxSetup: function( settings ) { +		jQuery.extend( jQuery.ajaxSettings, settings ); +	}, + +	ajaxSettings: { +		url: location.href, +		global: true, +		type: "GET", +		contentType: "application/x-www-form-urlencoded", +		processData: true, +		async: true, +		/* +		timeout: 0, +		data: null, +		username: null, +		password: null, +		*/ +		// Create the request object; Microsoft failed to properly +		// implement the XMLHttpRequest in IE7, so we use the ActiveXObject when it is available +		// This function can be overriden by calling jQuery.ajaxSetup +		xhr:function(){ +			return window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest(); +		}, +		accepts: { +			xml: "application/xml, text/xml", +			html: "text/html", +			script: "text/javascript, application/javascript", +			json: "application/json, text/javascript", +			text: "text/plain", +			_default: "*/*" +		} +	}, + +	// Last-Modified header cache for next request +	lastModified: {}, + +	ajax: function( s ) { +		// Extend the settings, but re-extend 's' so that it can be +		// checked again later (in the test suite, specifically) +		s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); + +		var jsonp, jsre = /=\?(&|$)/g, status, data, +			type = s.type.toUpperCase(); + +		// convert data if not already a string +		if ( s.data && s.processData && typeof s.data !== "string" ) +			s.data = jQuery.param(s.data); + +		// Handle JSONP Parameter Callbacks +		if ( s.dataType == "jsonp" ) { +			if ( type == "GET" ) { +				if ( !s.url.match(jsre) ) +					s.url += (s.url.match(/\?/) ? "&" : "?") + (s.jsonp || "callback") + "=?"; +			} else if ( !s.data || !s.data.match(jsre) ) +				s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?"; +			s.dataType = "json"; +		} + +		// Build temporary JSONP function +		if ( s.dataType == "json" && (s.data && s.data.match(jsre) || s.url.match(jsre)) ) { +			jsonp = "jsonp" + jsc++; + +			// Replace the =? sequence both in the query string and the data +			if ( s.data ) +				s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1"); +			s.url = s.url.replace(jsre, "=" + jsonp + "$1"); + +			// We need to make sure +			// that a JSONP style response is executed properly +			s.dataType = "script"; + +			// Handle JSONP-style loading +			window[ jsonp ] = function(tmp){ +				data = tmp; +				success(); +				complete(); +				// Garbage collect +				window[ jsonp ] = undefined; +				try{ delete window[ jsonp ]; } catch(e){} +				if ( head ) +					head.removeChild( script ); +			}; +		} + +		if ( s.dataType == "script" && s.cache == null ) +			s.cache = false; + +		if ( s.cache === false && type == "GET" ) { +			var ts = now(); +			// try replacing _= if it is there +			var ret = s.url.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2"); +			// if nothing was replaced, add timestamp to the end +			s.url = ret + ((ret == s.url) ? (s.url.match(/\?/) ? "&" : "?") + "_=" + ts : ""); +		} + +		// If data is available, append data to url for get requests +		if ( s.data && type == "GET" ) { +			s.url += (s.url.match(/\?/) ? "&" : "?") + s.data; + +			// IE likes to send both get and post data, prevent this +			s.data = null; +		} + +		// Watch for a new set of requests +		if ( s.global && ! jQuery.active++ ) +			jQuery.event.trigger( "ajaxStart" ); + +		// Matches an absolute URL, and saves the domain +		var parts = /^(\w+:)?\/\/([^\/?#]+)/.exec( s.url ); + +		// If we're requesting a remote document +		// and trying to load JSON or Script with a GET +		if ( s.dataType == "script" && type == "GET" && parts +			&& ( parts[1] && parts[1] != location.protocol || parts[2] != location.host )){ + +			var head = document.getElementsByTagName("head")[0]; +			var script = document.createElement("script"); +			script.src = s.url; +			if (s.scriptCharset) +				script.charset = s.scriptCharset; + +			// Handle Script loading +			if ( !jsonp ) { +				var done = false; + +				// Attach handlers for all browsers +				script.onload = script.onreadystatechange = function(){ +					if ( !done && (!this.readyState || +							this.readyState == "loaded" || this.readyState == "complete") ) { +						done = true; +						success(); +						complete(); + +						// Handle memory leak in IE +						script.onload = script.onreadystatechange = null; +						head.removeChild( script ); +					} +				}; +			} + +			head.appendChild(script); + +			// We handle everything using the script element injection +			return undefined; +		} + +		var requestDone = false; + +		// Create the request object +		var xhr = s.xhr(); + +		// Open the socket +		// Passing null username, generates a login popup on Opera (#2865) +		if( s.username ) +			xhr.open(type, s.url, s.async, s.username, s.password); +		else +			xhr.open(type, s.url, s.async); + +		// Need an extra try/catch for cross domain requests in Firefox 3 +		try { +			// Set the correct header, if data is being sent +			if ( s.data ) +				xhr.setRequestHeader("Content-Type", s.contentType); + +			// Set the If-Modified-Since header, if ifModified mode. +			if ( s.ifModified ) +				xhr.setRequestHeader("If-Modified-Since", +					jQuery.lastModified[s.url] || "Thu, 01 Jan 1970 00:00:00 GMT" ); + +			// Set header so the called script knows that it's an XMLHttpRequest +			xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest"); + +			// Set the Accepts header for the server, depending on the dataType +			xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ? +				s.accepts[ s.dataType ] + ", */*" : +				s.accepts._default ); +		} catch(e){} + +		// Allow custom headers/mimetypes and early abort +		if ( s.beforeSend && s.beforeSend(xhr, s) === false ) { +			// Handle the global AJAX counter +			if ( s.global && ! --jQuery.active ) +				jQuery.event.trigger( "ajaxStop" ); +			// close opended socket +			xhr.abort(); +			return false; +		} + +		if ( s.global ) +			jQuery.event.trigger("ajaxSend", [xhr, s]); + +		// Wait for a response to come back +		var onreadystatechange = function(isTimeout){ +			// The request was aborted, clear the interval and decrement jQuery.active +			if (xhr.readyState == 0) { +				if (ival) { +					// clear poll interval +					clearInterval(ival); +					ival = null; +					// Handle the global AJAX counter +					if ( s.global && ! --jQuery.active ) +						jQuery.event.trigger( "ajaxStop" ); +				} +			// The transfer is complete and the data is available, or the request timed out +			} else if ( !requestDone && xhr && (xhr.readyState == 4 || isTimeout == "timeout") ) { +				requestDone = true; + +				// clear poll interval +				if (ival) { +					clearInterval(ival); +					ival = null; +				} + +				status = isTimeout == "timeout" ? "timeout" : +					!jQuery.httpSuccess( xhr ) ? "error" : +					s.ifModified && jQuery.httpNotModified( xhr, s.url ) ? "notmodified" : +					"success"; + +				if ( status == "success" ) { +					// Watch for, and catch, XML document parse errors +					try { +						// process the data (runs the xml through httpData regardless of callback) +						data = jQuery.httpData( xhr, s.dataType, s ); +					} catch(e) { +						status = "parsererror"; +					} +				} + +				// Make sure that the request was successful or notmodified +				if ( status == "success" ) { +					// Cache Last-Modified header, if ifModified mode. +					var modRes; +					try { +						modRes = xhr.getResponseHeader("Last-Modified"); +					} catch(e) {} // swallow exception thrown by FF if header is not available + +					if ( s.ifModified && modRes ) +						jQuery.lastModified[s.url] = modRes; + +					// JSONP handles its own success callback +					if ( !jsonp ) +						success(); +				} else +					jQuery.handleError(s, xhr, status); + +				// Fire the complete handlers +				complete(); + +				if ( isTimeout ) +					xhr.abort(); + +				// Stop memory leaks +				if ( s.async ) +					xhr = null; +			} +		}; + +		if ( s.async ) { +			// don't attach the handler to the request, just poll it instead +			var ival = setInterval(onreadystatechange, 13); + +			// Timeout checker +			if ( s.timeout > 0 ) +				setTimeout(function(){ +					// Check to see if the request is still happening +					if ( xhr && !requestDone ) +						onreadystatechange( "timeout" ); +				}, s.timeout); +		} + +		// Send the data +		try { +			xhr.send(s.data); +		} catch(e) { +			jQuery.handleError(s, xhr, null, e); +		} + +		// firefox 1.5 doesn't fire statechange for sync requests +		if ( !s.async ) +			onreadystatechange(); + +		function success(){ +			// If a local callback was specified, fire it and pass it the data +			if ( s.success ) +				s.success( data, status ); + +			// Fire the global callback +			if ( s.global ) +				jQuery.event.trigger( "ajaxSuccess", [xhr, s] ); +		} + +		function complete(){ +			// Process result +			if ( s.complete ) +				s.complete(xhr, status); + +			// The request was completed +			if ( s.global ) +				jQuery.event.trigger( "ajaxComplete", [xhr, s] ); + +			// Handle the global AJAX counter +			if ( s.global && ! --jQuery.active ) +				jQuery.event.trigger( "ajaxStop" ); +		} + +		// return XMLHttpRequest to allow aborting the request etc. +		return xhr; +	}, + +	handleError: function( s, xhr, status, e ) { +		// If a local callback was specified, fire it +		if ( s.error ) s.error( xhr, status, e ); + +		// Fire the global callback +		if ( s.global ) +			jQuery.event.trigger( "ajaxError", [xhr, s, e] ); +	}, + +	// Counter for holding the number of active queries +	active: 0, + +	// Determines if an XMLHttpRequest was successful or not +	httpSuccess: function( xhr ) { +		try { +			// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450 +			return !xhr.status && location.protocol == "file:" || +				( xhr.status >= 200 && xhr.status < 300 ) || xhr.status == 304 || xhr.status == 1223; +		} catch(e){} +		return false; +	}, + +	// Determines if an XMLHttpRequest returns NotModified +	httpNotModified: function( xhr, url ) { +		try { +			var xhrRes = xhr.getResponseHeader("Last-Modified"); + +			// Firefox always returns 200. check Last-Modified date +			return xhr.status == 304 || xhrRes == jQuery.lastModified[url]; +		} catch(e){} +		return false; +	}, + +	httpData: function( xhr, type, s ) { +		var ct = xhr.getResponseHeader("content-type"), +			xml = type == "xml" || !type && ct && ct.indexOf("xml") >= 0, +			data = xml ? xhr.responseXML : xhr.responseText; + +		if ( xml && data.documentElement.tagName == "parsererror" ) +			throw "parsererror"; +			 +		// Allow a pre-filtering function to sanitize the response +		// s != null is checked to keep backwards compatibility +		if( s && s.dataFilter ) +			data = s.dataFilter( data, type ); + +		// The filter can actually parse the response +		if( typeof data === "string" ){ + +			// If the type is "script", eval it in global context +			if ( type == "script" ) +				jQuery.globalEval( data ); + +			// Get the JavaScript object, if JSON is used. +			if ( type == "json" ) +				data = window["eval"]("(" + data + ")"); +		} +		 +		return data; +	}, + +	// Serialize an array of form elements or a set of +	// key/values into a query string +	param: function( a ) { +		var s = [ ]; + +		function add( key, value ){ +			s[ s.length ] = encodeURIComponent(key) + '=' + encodeURIComponent(value); +		}; + +		// If an array was passed in, assume that it is an array +		// of form elements +		if ( jQuery.isArray(a) || a.jquery ) +			// Serialize the form elements +			jQuery.each( a, function(){ +				add( this.name, this.value ); +			}); + +		// Otherwise, assume that it's an object of key/value pairs +		else +			// Serialize the key/values +			for ( var j in a ) +				// If the value is an array then the key names need to be repeated +				if ( jQuery.isArray(a[j]) ) +					jQuery.each( a[j], function(){ +						add( j, this ); +					}); +				else +					add( j, jQuery.isFunction(a[j]) ? a[j]() : a[j] ); + +		// Return the resulting serialization +		return s.join("&").replace(/%20/g, "+"); +	} + +}); +var elemdisplay = {}, +	timerId, +	fxAttrs = [ +		// height animations +		[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ], +		// width animations +		[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ], +		// opacity animations +		[ "opacity" ] +	]; + +function genFx( type, num ){ +	var obj = {}; +	jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function(){ +		obj[ this ] = type; +	}); +	return obj; +} + +jQuery.fn.extend({ +	show: function(speed,callback){ +		if ( speed ) { +			return this.animate( genFx("show", 3), speed, callback); +		} else { +			for ( var i = 0, l = this.length; i < l; i++ ){ +				var old = jQuery.data(this[i], "olddisplay"); +				 +				this[i].style.display = old || ""; +				 +				if ( jQuery.css(this[i], "display") === "none" ) { +					var tagName = this[i].tagName, display; +					 +					if ( elemdisplay[ tagName ] ) { +						display = elemdisplay[ tagName ]; +					} else { +						var elem = jQuery("<" + tagName + " />").appendTo("body"); +						 +						display = elem.css("display"); +						if ( display === "none" ) +							display = "block"; +						 +						elem.remove(); +						 +						elemdisplay[ tagName ] = display; +					} +					 +					jQuery.data(this[i], "olddisplay", display); +				} +			} + +			// Set the display of the elements in a second loop +			// to avoid the constant reflow +			for ( var i = 0, l = this.length; i < l; i++ ){ +				this[i].style.display = jQuery.data(this[i], "olddisplay") || ""; +			} +			 +			return this; +		} +	}, + +	hide: function(speed,callback){ +		if ( speed ) { +			return this.animate( genFx("hide", 3), speed, callback); +		} else { +			for ( var i = 0, l = this.length; i < l; i++ ){ +				var old = jQuery.data(this[i], "olddisplay"); +				if ( !old && old !== "none" ) +					jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display")); +			} + +			// Set the display of the elements in a second loop +			// to avoid the constant reflow +			for ( var i = 0, l = this.length; i < l; i++ ){ +				this[i].style.display = "none"; +			} + +			return this; +		} +	}, + +	// Save the old toggle function +	_toggle: jQuery.fn.toggle, + +	toggle: function( fn, fn2 ){ +		var bool = typeof fn === "boolean"; + +		return jQuery.isFunction(fn) && jQuery.isFunction(fn2) ? +			this._toggle.apply( this, arguments ) : +			fn == null || bool ? +				this.each(function(){ +					var state = bool ? fn : jQuery(this).is(":hidden"); +					jQuery(this)[ state ? "show" : "hide" ](); +				}) : +				this.animate(genFx("toggle", 3), fn, fn2); +	}, + +	fadeTo: function(speed,to,callback){ +		return this.animate({opacity: to}, speed, callback); +	}, + +	animate: function( prop, speed, easing, callback ) { +		var optall = jQuery.speed(speed, easing, callback); + +		return this[ optall.queue === false ? "each" : "queue" ](function(){ +		 +			var opt = jQuery.extend({}, optall), p, +				hidden = this.nodeType == 1 && jQuery(this).is(":hidden"), +				self = this; +	 +			for ( p in prop ) { +				if ( prop[p] == "hide" && hidden || prop[p] == "show" && !hidden ) +					return opt.complete.call(this); + +				if ( ( p == "height" || p == "width" ) && this.style ) { +					// Store display property +					opt.display = jQuery.css(this, "display"); + +					// Make sure that nothing sneaks out +					opt.overflow = this.style.overflow; +				} +			} + +			if ( opt.overflow != null ) +				this.style.overflow = "hidden"; + +			opt.curAnim = jQuery.extend({}, prop); + +			jQuery.each( prop, function(name, val){ +				var e = new jQuery.fx( self, opt, name ); + +				if ( /toggle|show|hide/.test(val) ) +					e[ val == "toggle" ? hidden ? "show" : "hide" : val ]( prop ); +				else { +					var parts = val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/), +						start = e.cur(true) || 0; + +					if ( parts ) { +						var end = parseFloat(parts[2]), +							unit = parts[3] || "px"; + +						// We need to compute starting value +						if ( unit != "px" ) { +							self.style[ name ] = (end || 1) + unit; +							start = ((end || 1) / e.cur(true)) * start; +							self.style[ name ] = start + unit; +						} + +						// If a +=/-= token was provided, we're doing a relative animation +						if ( parts[1] ) +							end = ((parts[1] == "-=" ? -1 : 1) * end) + start; + +						e.custom( start, end, unit ); +					} else +						e.custom( start, val, "" ); +				} +			}); + +			// For JS strict compliance +			return true; +		}); +	}, + +	stop: function(clearQueue, gotoEnd){ +		var timers = jQuery.timers; + +		if (clearQueue) +			this.queue([]); + +		this.each(function(){ +			// go in reverse order so anything added to the queue during the loop is ignored +			for ( var i = timers.length - 1; i >= 0; i-- ) +				if ( timers[i].elem == this ) { +					if (gotoEnd) +						// force the next step to be the last +						timers[i](true); +					timers.splice(i, 1); +				} +		}); + +		// start the next in the queue if the last step wasn't forced +		if (!gotoEnd) +			this.dequeue(); + +		return this; +	} + +}); + +// Generate shortcuts for custom animations +jQuery.each({ +	slideDown: genFx("show", 1), +	slideUp: genFx("hide", 1), +	slideToggle: genFx("toggle", 1), +	fadeIn: { opacity: "show" }, +	fadeOut: { opacity: "hide" } +}, function( name, props ){ +	jQuery.fn[ name ] = function( speed, callback ){ +		return this.animate( props, speed, callback ); +	}; +}); + +jQuery.extend({ + +	speed: function(speed, easing, fn) { +		var opt = typeof speed === "object" ? speed : { +			complete: fn || !fn && easing || +				jQuery.isFunction( speed ) && speed, +			duration: speed, +			easing: fn && easing || easing && !jQuery.isFunction(easing) && easing +		}; + +		opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : +			jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default; + +		// Queueing +		opt.old = opt.complete; +		opt.complete = function(){ +			if ( opt.queue !== false ) +				jQuery(this).dequeue(); +			if ( jQuery.isFunction( opt.old ) ) +				opt.old.call( this ); +		}; + +		return opt; +	}, + +	easing: { +		linear: function( p, n, firstNum, diff ) { +			return firstNum + diff * p; +		}, +		swing: function( p, n, firstNum, diff ) { +			return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum; +		} +	}, + +	timers: [], + +	fx: function( elem, options, prop ){ +		this.options = options; +		this.elem = elem; +		this.prop = prop; + +		if ( !options.orig ) +			options.orig = {}; +	} + +}); + +jQuery.fx.prototype = { + +	// Simple function for setting a style value +	update: function(){ +		if ( this.options.step ) +			this.options.step.call( this.elem, this.now, this ); + +		(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this ); + +		// Set display property to block for height/width animations +		if ( ( this.prop == "height" || this.prop == "width" ) && this.elem.style ) +			this.elem.style.display = "block"; +	}, + +	// Get the current size +	cur: function(force){ +		if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) +			return this.elem[ this.prop ]; + +		var r = parseFloat(jQuery.css(this.elem, this.prop, force)); +		return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0; +	}, + +	// Start an animation from one number to another +	custom: function(from, to, unit){ +		this.startTime = now(); +		this.start = from; +		this.end = to; +		this.unit = unit || this.unit || "px"; +		this.now = this.start; +		this.pos = this.state = 0; + +		var self = this; +		function t(gotoEnd){ +			return self.step(gotoEnd); +		} + +		t.elem = this.elem; + +		if ( t() && jQuery.timers.push(t) && !timerId ) { +			timerId = setInterval(function(){ +				var timers = jQuery.timers; + +				for ( var i = 0; i < timers.length; i++ ) +					if ( !timers[i]() ) +						timers.splice(i--, 1); + +				if ( !timers.length ) { +					clearInterval( timerId ); +					timerId = undefined; +				} +			}, 13); +		} +	}, + +	// Simple 'show' function +	show: function(){ +		// Remember where we started, so that we can go back to it later +		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop ); +		this.options.show = true; + +		// Begin the animation +		// Make sure that we start at a small width/height to avoid any +		// flash of content +		this.custom(this.prop == "width" || this.prop == "height" ? 1 : 0, this.cur()); + +		// Start by showing the element +		jQuery(this.elem).show(); +	}, + +	// Simple 'hide' function +	hide: function(){ +		// Remember where we started, so that we can go back to it later +		this.options.orig[this.prop] = jQuery.attr( this.elem.style, this.prop ); +		this.options.hide = true; + +		// Begin the animation +		this.custom(this.cur(), 0); +	}, + +	// Each step of an animation +	step: function(gotoEnd){ +		var t = now(); + +		if ( gotoEnd || t >= this.options.duration + this.startTime ) { +			this.now = this.end; +			this.pos = this.state = 1; +			this.update(); + +			this.options.curAnim[ this.prop ] = true; + +			var done = true; +			for ( var i in this.options.curAnim ) +				if ( this.options.curAnim[i] !== true ) +					done = false; + +			if ( done ) { +				if ( this.options.display != null ) { +					// Reset the overflow +					this.elem.style.overflow = this.options.overflow; + +					// Reset the display +					this.elem.style.display = this.options.display; +					if ( jQuery.css(this.elem, "display") == "none" ) +						this.elem.style.display = "block"; +				} + +				// Hide the element if the "hide" operation was done +				if ( this.options.hide ) +					jQuery(this.elem).hide(); + +				// Reset the properties, if the item has been hidden or shown +				if ( this.options.hide || this.options.show ) +					for ( var p in this.options.curAnim ) +						jQuery.attr(this.elem.style, p, this.options.orig[p]); +					 +				// Execute the complete function +				this.options.complete.call( this.elem ); +			} + +			return false; +		} else { +			var n = t - this.startTime; +			this.state = n / this.options.duration; + +			// Perform the easing function, defaults to swing +			this.pos = jQuery.easing[this.options.easing || (jQuery.easing.swing ? "swing" : "linear")](this.state, n, 0, 1, this.options.duration); +			this.now = this.start + ((this.end - this.start) * this.pos); + +			// Perform the next step of the animation +			this.update(); +		} + +		return true; +	} + +}; + +jQuery.extend( jQuery.fx, { +	speeds:{ +		slow: 600, + 		fast: 200, + 		// Default speed + 		_default: 400 +	}, +	step: { + +		opacity: function(fx){ +			jQuery.attr(fx.elem.style, "opacity", fx.now); +		}, + +		_default: function(fx){ +			if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) +				fx.elem.style[ fx.prop ] = fx.now + fx.unit; +			else +				fx.elem[ fx.prop ] = fx.now; +		} +	} +}); +if ( document.documentElement["getBoundingClientRect"] ) +	jQuery.fn.offset = function() { +		if ( !this[0] ) return { top: 0, left: 0 }; +		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] ); +		var box  = this[0].getBoundingClientRect(), doc = this[0].ownerDocument, body = doc.body, docElem = doc.documentElement, +			clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0, +			top  = box.top  + (self.pageYOffset || jQuery.boxModel && docElem.scrollTop  || body.scrollTop ) - clientTop, +			left = box.left + (self.pageXOffset || jQuery.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft; +		return { top: top, left: left }; +	}; +else  +	jQuery.fn.offset = function() { +		if ( !this[0] ) return { top: 0, left: 0 }; +		if ( this[0] === this[0].ownerDocument.body ) return jQuery.offset.bodyOffset( this[0] ); +		jQuery.offset.initialized || jQuery.offset.initialize(); + +		var elem = this[0], offsetParent = elem.offsetParent, prevOffsetParent = elem, +			doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement, +			body = doc.body, defaultView = doc.defaultView, +			prevComputedStyle = defaultView.getComputedStyle(elem, null), +			top = elem.offsetTop, left = elem.offsetLeft; + +		while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) { +			computedStyle = defaultView.getComputedStyle(elem, null); +			top -= elem.scrollTop, left -= elem.scrollLeft; +			if ( elem === offsetParent ) { +				top += elem.offsetTop, left += elem.offsetLeft; +				if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.tagName)) ) +					top  += parseInt( computedStyle.borderTopWidth,  10) || 0, +					left += parseInt( computedStyle.borderLeftWidth, 10) || 0; +				prevOffsetParent = offsetParent, offsetParent = elem.offsetParent; +			} +			if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) +				top  += parseInt( computedStyle.borderTopWidth,  10) || 0, +				left += parseInt( computedStyle.borderLeftWidth, 10) || 0; +			prevComputedStyle = computedStyle; +		} + +		if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) +			top  += body.offsetTop, +			left += body.offsetLeft; + +		if ( prevComputedStyle.position === "fixed" ) +			top  += Math.max(docElem.scrollTop, body.scrollTop), +			left += Math.max(docElem.scrollLeft, body.scrollLeft); + +		return { top: top, left: left }; +	}; + +jQuery.offset = { +	initialize: function() { +		if ( this.initialized ) return; +		var body = document.body, container = document.createElement('div'), innerDiv, checkDiv, table, td, rules, prop, bodyMarginTop = body.style.marginTop, +			html = '<div style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;"><div></div></div><table style="position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;" cellpadding="0" cellspacing="0"><tr><td></td></tr></table>'; + +		rules = { position: 'absolute', top: 0, left: 0, margin: 0, border: 0, width: '1px', height: '1px', visibility: 'hidden' }; +		for ( prop in rules ) container.style[prop] = rules[prop]; + +		container.innerHTML = html; +		body.insertBefore(container, body.firstChild); +		innerDiv = container.firstChild, checkDiv = innerDiv.firstChild, td = innerDiv.nextSibling.firstChild.firstChild; + +		this.doesNotAddBorder = (checkDiv.offsetTop !== 5); +		this.doesAddBorderForTableAndCells = (td.offsetTop === 5); + +		innerDiv.style.overflow = 'hidden', innerDiv.style.position = 'relative'; +		this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5); + +		body.style.marginTop = '1px'; +		this.doesNotIncludeMarginInBodyOffset = (body.offsetTop === 0); +		body.style.marginTop = bodyMarginTop; + +		body.removeChild(container); +		this.initialized = true; +	}, + +	bodyOffset: function(body) { +		jQuery.offset.initialized || jQuery.offset.initialize(); +		var top = body.offsetTop, left = body.offsetLeft; +		if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) +			top  += parseInt( jQuery.curCSS(body, 'marginTop',  true), 10 ) || 0, +			left += parseInt( jQuery.curCSS(body, 'marginLeft', true), 10 ) || 0; +		return { top: top, left: left }; +	} +}; + + +jQuery.fn.extend({ +	position: function() { +		var left = 0, top = 0, results; + +		if ( this[0] ) { +			// Get *real* offsetParent +			var offsetParent = this.offsetParent(), + +			// Get correct offsets +			offset       = this.offset(), +			parentOffset = /^body|html$/i.test(offsetParent[0].tagName) ? { top: 0, left: 0 } : offsetParent.offset(); + +			// Subtract element margins +			// note: when an element has margin: auto the offsetLeft and marginLeft  +			// are the same in Safari causing offset.left to incorrectly be 0 +			offset.top  -= num( this, 'marginTop'  ); +			offset.left -= num( this, 'marginLeft' ); + +			// Add offsetParent borders +			parentOffset.top  += num( offsetParent, 'borderTopWidth'  ); +			parentOffset.left += num( offsetParent, 'borderLeftWidth' ); + +			// Subtract the two offsets +			results = { +				top:  offset.top  - parentOffset.top, +				left: offset.left - parentOffset.left +			}; +		} + +		return results; +	}, + +	offsetParent: function() { +		var offsetParent = this[0].offsetParent || document.body; +		while ( offsetParent && (!/^body|html$/i.test(offsetParent.tagName) && jQuery.css(offsetParent, 'position') == 'static') ) +			offsetParent = offsetParent.offsetParent; +		return jQuery(offsetParent); +	} +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( ['Left', 'Top'], function(i, name) { +	var method = 'scroll' + name; +	 +	jQuery.fn[ method ] = function(val) { +		if (!this[0]) return null; + +		return val !== undefined ? + +			// Set the scroll offset +			this.each(function() { +				this == window || this == document ? +					window.scrollTo( +						!i ? val : jQuery(window).scrollLeft(), +						 i ? val : jQuery(window).scrollTop() +					) : +					this[ method ] = val; +			}) : + +			// Return the scroll offset +			this[0] == window || this[0] == document ? +				self[ i ? 'pageYOffset' : 'pageXOffset' ] || +					jQuery.boxModel && document.documentElement[ method ] || +					document.body[ method ] : +				this[0][ method ]; +	}; +}); +// Create innerHeight, innerWidth, outerHeight and outerWidth methods +jQuery.each([ "Height", "Width" ], function(i, name){ + +	var tl = i ? "Left"  : "Top",  // top or left +		br = i ? "Right" : "Bottom", // bottom or right +		lower = name.toLowerCase(); + +	// innerHeight and innerWidth +	jQuery.fn["inner" + name] = function(){ +		return this[0] ? +			jQuery.css( this[0], lower, false, "padding" ) : +			null; +	}; + +	// outerHeight and outerWidth +	jQuery.fn["outer" + name] = function(margin) { +		return this[0] ? +			jQuery.css( this[0], lower, false, margin ? "margin" : "border" ) : +			null; +	}; +	 +	var type = name.toLowerCase(); + +	jQuery.fn[ type ] = function( size ) { +		// Get window width or height +		return this[0] == window ? +			// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode +			document.compatMode == "CSS1Compat" && document.documentElement[ "client" + name ] || +			document.body[ "client" + name ] : + +			// Get document width or height +			this[0] == document ? +				// Either scroll[Width/Height] or offset[Width/Height], whichever is greater +				Math.max( +					document.documentElement["client" + name], +					document.body["scroll" + name], document.documentElement["scroll" + name], +					document.body["offset" + name], document.documentElement["offset" + name] +				) : + +				// Get or set width or height on the element +				size === undefined ? +					// Get width or height on the element +					(this.length ? jQuery.css( this[0], type ) : null) : + +					// Set the width or height on the element (default to pixels if value is unitless) +					this.css( type, typeof size === "string" ? size : size + "px" ); +	}; + +}); +})(); diff --git a/lib/jquery/jquery-ui-1.7.1.custom.min.js b/lib/jquery/jquery-ui-1.7.1.custom.min.js new file mode 100644 index 00000000..7e37b4a2 --- /dev/null +++ b/lib/jquery/jquery-ui-1.7.1.custom.min.js @@ -0,0 +1,77 @@ +/*
 + * jQuery UI 1.7.1
 + *
 + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 + * Dual licensed under the MIT (MIT-LICENSE.txt)
 + * and GPL (GPL-LICENSE.txt) licenses.
 + *
 + * http://docs.jquery.com/UI
 + */
jQuery.ui||(function(c){var i=c.fn.remove,d=c.browser.mozilla&&(parseFloat(c.browser.version)<1.9);c.ui={version:"1.7.1",plugin:{add:function(k,l,n){var m=c.ui[k].prototype;for(var j in n){m.plugins[j]=m.plugins[j]||[];m.plugins[j].push([l,n[j]])}},call:function(j,l,k){var n=j.plugins[l];if(!n||!j.element[0].parentNode){return}for(var m=0;m<n.length;m++){if(j.options[n[m][0]]){n[m][1].apply(j.element,k)}}}},contains:function(k,j){return document.compareDocumentPosition?k.compareDocumentPosition(j)&16:k!==j&&k.contains(j)},hasScroll:function(m,k){if(c(m).css("overflow")=="hidden"){return false}var j=(k&&k=="left")?"scrollLeft":"scrollTop",l=false;if(m[j]>0){return true}m[j]=1;l=(m[j]>0);m[j]=0;return l},isOverAxis:function(k,j,l){return(k>j)&&(k<(j+l))},isOver:function(o,k,n,m,j,l){return c.ui.isOverAxis(o,n,j)&&c.ui.isOverAxis(k,m,l)},keyCode:{BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38}};if(d){var f=c.attr,e=c.fn.removeAttr,h="http://www.w3.org/2005/07/aaa",a=/^aria-/,b=/^wairole:/;c.attr=function(k,j,l){var m=l!==undefined;return(j=="role"?(m?f.call(this,k,j,"wairole:"+l):(f.apply(this,arguments)||"").replace(b,"")):(a.test(j)?(m?k.setAttributeNS(h,j.replace(a,"aaa:"),l):f.call(this,k,j.replace(a,"aaa:"))):f.apply(this,arguments)))};c.fn.removeAttr=function(j){return(a.test(j)?this.each(function(){this.removeAttributeNS(h,j.replace(a,""))}):e.call(this,j))}}c.fn.extend({remove:function(){c("*",this).add(this).each(function(){c(this).triggerHandler("remove")});return i.apply(this,arguments)},enableSelection:function(){return this.attr("unselectable","off").css("MozUserSelect","").unbind("selectstart.ui")},disableSelection:function(){return this.attr("unselectable","on").css("MozUserSelect","none").bind("selectstart.ui",function(){return false})},scrollParent:function(){var j;if((c.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){j=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(c.curCSS(this,"position",1))&&(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}else{j=this.parents().filter(function(){return(/(auto|scroll)/).test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!j.length?c(document):j}});c.extend(c.expr[":"],{data:function(l,k,j){return !!c.data(l,j[3])},focusable:function(k){var l=k.nodeName.toLowerCase(),j=c.attr(k,"tabindex");return(/input|select|textarea|button|object/.test(l)?!k.disabled:"a"==l||"area"==l?k.href||!isNaN(j):!isNaN(j))&&!c(k)["area"==l?"parents":"closest"](":hidden").length},tabbable:function(k){var j=c.attr(k,"tabindex");return(isNaN(j)||j>=0)&&c(k).is(":focusable")}});function g(m,n,o,l){function k(q){var p=c[m][n][q]||[];return(typeof p=="string"?p.split(/,?\s+/):p)}var j=k("getter");if(l.length==1&&typeof l[0]=="string"){j=j.concat(k("getterSetter"))}return(c.inArray(o,j)!=-1)}c.widget=function(k,j){var l=k.split(".")[0];k=k.split(".")[1];c.fn[k]=function(p){var n=(typeof p=="string"),o=Array.prototype.slice.call(arguments,1);if(n&&p.substring(0,1)=="_"){return this}if(n&&g(l,k,p,o)){var m=c.data(this[0],k);return(m?m[p].apply(m,o):undefined)}return this.each(function(){var q=c.data(this,k);(!q&&!n&&c.data(this,k,new c[l][k](this,p))._init());(q&&n&&c.isFunction(q[p])&&q[p].apply(q,o))})};c[l]=c[l]||{};c[l][k]=function(o,n){var m=this;this.namespace=l;this.widgetName=k;this.widgetEventPrefix=c[l][k].eventPrefix||k;this.widgetBaseClass=l+"-"+k;this.options=c.extend({},c.widget.defaults,c[l][k].defaults,c.metadata&&c.metadata.get(o)[k],n);this.element=c(o).bind("setData."+k,function(q,p,r){if(q.target==o){return m._setData(p,r)}}).bind("getData."+k,function(q,p){if(q.target==o){return m._getData(p)}}).bind("remove",function(){return m.destroy()})};c[l][k].prototype=c.extend({},c.widget.prototype,j);c[l][k].getterSetter="option"};c.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName).removeClass(this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").removeAttr("aria-disabled")},option:function(l,m){var k=l,j=this;if(typeof l=="string"){if(m===undefined){return this._getData(l)}k={};k[l]=m}c.each(k,function(n,o){j._setData(n,o)})},_getData:function(j){return this.options[j]},_setData:function(j,k){this.options[j]=k;if(j=="disabled"){this.element[k?"addClass":"removeClass"](this.widgetBaseClass+"-disabled "+this.namespace+"-state-disabled").attr("aria-disabled",k)}},enable:function(){this._setData("disabled",false)},disable:function(){this._setData("disabled",true)},_trigger:function(l,m,n){var p=this.options[l],j=(l==this.widgetEventPrefix?l:this.widgetEventPrefix+l);m=c.Event(m);m.type=j;if(m.originalEvent){for(var k=c.event.props.length,o;k;){o=c.event.props[--k];m[o]=m.originalEvent[o]}}this.element.trigger(m,n);return !(c.isFunction(p)&&p.call(this.element[0],m,n)===false||m.isDefaultPrevented())}};c.widget.defaults={disabled:false};c.ui.mouse={_mouseInit:function(){var j=this;this.element.bind("mousedown."+this.widgetName,function(k){return j._mouseDown(k)}).bind("click."+this.widgetName,function(k){if(j._preventClickEvent){j._preventClickEvent=false;k.stopImmediatePropagation();return false}});if(c.browser.msie){this._mouseUnselectable=this.element.attr("unselectable");this.element.attr("unselectable","on")}this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName);(c.browser.msie&&this.element.attr("unselectable",this._mouseUnselectable))},_mouseDown:function(l){l.originalEvent=l.originalEvent||{};if(l.originalEvent.mouseHandled){return}(this._mouseStarted&&this._mouseUp(l));this._mouseDownEvent=l;var k=this,m=(l.which==1),j=(typeof this.options.cancel=="string"?c(l.target).parents().add(l.target).filter(this.options.cancel).length:false);if(!m||j||!this._mouseCapture(l)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){k.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(l)&&this._mouseDelayMet(l)){this._mouseStarted=(this._mouseStart(l)!==false);if(!this._mouseStarted){l.preventDefault();return true}}this._mouseMoveDelegate=function(n){return k._mouseMove(n)};this._mouseUpDelegate=function(n){return k._mouseUp(n)};c(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);(c.browser.safari||l.preventDefault());l.originalEvent.mouseHandled=true;return true},_mouseMove:function(j){if(c.browser.msie&&!j.button){return this._mouseUp(j)}if(this._mouseStarted){this._mouseDrag(j);return j.preventDefault()}if(this._mouseDistanceMet(j)&&this._mouseDelayMet(j)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,j)!==false);(this._mouseStarted?this._mouseDrag(j):this._mouseUp(j))}return !this._mouseStarted},_mouseUp:function(j){c(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._preventClickEvent=(j.target==this._mouseDownEvent.target);this._mouseStop(j)}return false},_mouseDistanceMet:function(j){return(Math.max(Math.abs(this._mouseDownEvent.pageX-j.pageX),Math.abs(this._mouseDownEvent.pageY-j.pageY))>=this.options.distance)},_mouseDelayMet:function(j){return this.mouseDelayMet},_mouseStart:function(j){},_mouseDrag:function(j){},_mouseStop:function(j){},_mouseCapture:function(j){return true}};c.ui.mouse.defaults={cancel:null,distance:1,delay:0}})(jQuery);;/*
 + * jQuery UI Accordion 1.7.1
 + *
 + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 + * Dual licensed under the MIT (MIT-LICENSE.txt)
 + * and GPL (GPL-LICENSE.txt) licenses.
 + *
 + * http://docs.jquery.com/UI/Accordion
 + *
 + * Depends:
 + *	ui.core.js
 + */
(function(a){a.widget("ui.accordion",{_init:function(){var d=this.options,b=this;this.running=0;if(d.collapsible==a.ui.accordion.defaults.collapsible&&d.alwaysOpen!=a.ui.accordion.defaults.alwaysOpen){d.collapsible=!d.alwaysOpen}if(d.navigation){var c=this.element.find("a").filter(d.navigationFilter);if(c.length){if(c.filter(d.header).length){this.active=c}else{this.active=c.parent().parent().prev();c.addClass("ui-accordion-content-active")}}}this.element.addClass("ui-accordion ui-widget ui-helper-reset");if(this.element[0].nodeName=="UL"){this.element.children("li").addClass("ui-accordion-li-fix")}this.headers=this.element.find(d.header).addClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all").bind("mouseenter.accordion",function(){a(this).addClass("ui-state-hover")}).bind("mouseleave.accordion",function(){a(this).removeClass("ui-state-hover")}).bind("focus.accordion",function(){a(this).addClass("ui-state-focus")}).bind("blur.accordion",function(){a(this).removeClass("ui-state-focus")});this.headers.next().addClass("ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom");this.active=this._findActive(this.active||d.active).toggleClass("ui-state-default").toggleClass("ui-state-active").toggleClass("ui-corner-all").toggleClass("ui-corner-top");this.active.next().addClass("ui-accordion-content-active");a("<span/>").addClass("ui-icon "+d.icons.header).prependTo(this.headers);this.active.find(".ui-icon").toggleClass(d.icons.header).toggleClass(d.icons.headerSelected);if(a.browser.msie){this.element.find("a").css("zoom","1")}this.resize();this.element.attr("role","tablist");this.headers.attr("role","tab").bind("keydown",function(e){return b._keydown(e)}).next().attr("role","tabpanel");this.headers.not(this.active||"").attr("aria-expanded","false").attr("tabIndex","-1").next().hide();if(!this.active.length){this.headers.eq(0).attr("tabIndex","0")}else{this.active.attr("aria-expanded","true").attr("tabIndex","0")}if(!a.browser.safari){this.headers.find("a").attr("tabIndex","-1")}if(d.event){this.headers.bind((d.event)+".accordion",function(e){return b._clickHandler.call(b,e,this)})}},destroy:function(){var c=this.options;this.element.removeClass("ui-accordion ui-widget ui-helper-reset").removeAttr("role").unbind(".accordion").removeData("accordion");this.headers.unbind(".accordion").removeClass("ui-accordion-header ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-corner-top").removeAttr("role").removeAttr("aria-expanded").removeAttr("tabindex");this.headers.find("a").removeAttr("tabindex");this.headers.children(".ui-icon").remove();var b=this.headers.next().css("display","").removeAttr("role").removeClass("ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active");if(c.autoHeight||c.fillHeight){b.css("height","")}},_setData:function(b,c){if(b=="alwaysOpen"){b="collapsible";c=!c}a.widget.prototype._setData.apply(this,arguments)},_keydown:function(e){var g=this.options,f=a.ui.keyCode;if(g.disabled||e.altKey||e.ctrlKey){return}var d=this.headers.length;var b=this.headers.index(e.target);var c=false;switch(e.keyCode){case f.RIGHT:case f.DOWN:c=this.headers[(b+1)%d];break;case f.LEFT:case f.UP:c=this.headers[(b-1+d)%d];break;case f.SPACE:case f.ENTER:return this._clickHandler({target:e.target},e.target)}if(c){a(e.target).attr("tabIndex","-1");a(c).attr("tabIndex","0");c.focus();return false}return true},resize:function(){var e=this.options,d;if(e.fillSpace){if(a.browser.msie){var b=this.element.parent().css("overflow");this.element.parent().css("overflow","hidden")}d=this.element.parent().height();if(a.browser.msie){this.element.parent().css("overflow",b)}this.headers.each(function(){d-=a(this).outerHeight()});var c=0;this.headers.next().each(function(){c=Math.max(c,a(this).innerHeight()-a(this).height())}).height(Math.max(0,d-c)).css("overflow","auto")}else{if(e.autoHeight){d=0;this.headers.next().each(function(){d=Math.max(d,a(this).outerHeight())}).height(d)}}},activate:function(b){var c=this._findActive(b)[0];this._clickHandler({target:c},c)},_findActive:function(b){return b?typeof b=="number"?this.headers.filter(":eq("+b+")"):this.headers.not(this.headers.not(b)):b===false?a([]):this.headers.filter(":eq(0)")},_clickHandler:function(b,f){var d=this.options;if(d.disabled){return false}if(!b.target&&d.collapsible){this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");var h=this.active.next(),e={options:d,newHeader:a([]),oldHeader:d.active,newContent:a([]),oldContent:h},c=(this.active=a([]));this._toggle(c,h,e);return false}var g=a(b.currentTarget||f);var i=g[0]==this.active[0];if(this.running||(!d.collapsible&&i)){return false}this.active.removeClass("ui-state-active ui-corner-top").addClass("ui-state-default ui-corner-all").find(".ui-icon").removeClass(d.icons.headerSelected).addClass(d.icons.header);this.active.next().addClass("ui-accordion-content-active");if(!i){g.removeClass("ui-state-default ui-corner-all").addClass("ui-state-active ui-corner-top").find(".ui-icon").removeClass(d.icons.header).addClass(d.icons.headerSelected);g.next().addClass("ui-accordion-content-active")}var c=g.next(),h=this.active.next(),e={options:d,newHeader:i&&d.collapsible?a([]):g,oldHeader:this.active,newContent:i&&d.collapsible?a([]):c.find("> *"),oldContent:h.find("> *")},j=this.headers.index(this.active[0])>this.headers.index(g[0]);this.active=i?a([]):g;this._toggle(c,h,e,i,j);return false},_toggle:function(b,i,g,j,k){var d=this.options,m=this;this.toShow=b;this.toHide=i;this.data=g;var c=function(){if(!m){return}return m._completed.apply(m,arguments)};this._trigger("changestart",null,this.data);this.running=i.size()===0?b.size():i.size();if(d.animated){var f={};if(d.collapsible&&j){f={toShow:a([]),toHide:i,complete:c,down:k,autoHeight:d.autoHeight||d.fillSpace}}else{f={toShow:b,toHide:i,complete:c,down:k,autoHeight:d.autoHeight||d.fillSpace}}if(!d.proxied){d.proxied=d.animated}if(!d.proxiedDuration){d.proxiedDuration=d.duration}d.animated=a.isFunction(d.proxied)?d.proxied(f):d.proxied;d.duration=a.isFunction(d.proxiedDuration)?d.proxiedDuration(f):d.proxiedDuration;var l=a.ui.accordion.animations,e=d.duration,h=d.animated;if(!l[h]){l[h]=function(n){this.slide(n,{easing:h,duration:e||700})}}l[h](f)}else{if(d.collapsible&&j){b.toggle()}else{i.hide();b.show()}c(true)}i.prev().attr("aria-expanded","false").attr("tabIndex","-1").blur();b.prev().attr("aria-expanded","true").attr("tabIndex","0").focus()},_completed:function(b){var c=this.options;this.running=b?0:--this.running;if(this.running){return}if(c.clearStyle){this.toShow.add(this.toHide).css({height:"",overflow:""})}this._trigger("change",null,this.data)}});a.extend(a.ui.accordion,{version:"1.7.1",defaults:{active:null,alwaysOpen:true,animated:"slide",autoHeight:true,clearStyle:false,collapsible:false,event:"click",fillSpace:false,header:"> li > :first-child,> :not(li):even",icons:{header:"ui-icon-triangle-1-e",headerSelected:"ui-icon-triangle-1-s"},navigation:false,navigationFilter:function(){return this.href.toLowerCase()==location.href.toLowerCase()}},animations:{slide:function(j,h){j=a.extend({easing:"swing",duration:300},j,h);if(!j.toHide.size()){j.toShow.animate({height:"show"},j);return}if(!j.toShow.size()){j.toHide.animate({height:"hide"},j);return}var c=j.toShow.css("overflow"),g,d={},f={},e=["height","paddingTop","paddingBottom"],b;var i=j.toShow;b=i[0].style.width;i.width(parseInt(i.parent().width(),10)-parseInt(i.css("paddingLeft"),10)-parseInt(i.css("paddingRight"),10)-(parseInt(i.css("borderLeftWidth"),10)||0)-(parseInt(i.css("borderRightWidth"),10)||0));a.each(e,function(k,m){f[m]="hide";var l=(""+a.css(j.toShow[0],m)).match(/^([\d+-.]+)(.*)$/);d[m]={value:l[1],unit:l[2]||"px"}});j.toShow.css({height:0,overflow:"hidden"}).show();j.toHide.filter(":hidden").each(j.complete).end().filter(":visible").animate(f,{step:function(k,l){if(l.prop=="height"){g=(l.now-l.start)/(l.end-l.start)}j.toShow[0].style[l.prop]=(g*d[l.prop].value)+d[l.prop].unit},duration:j.duration,easing:j.easing,complete:function(){if(!j.autoHeight){j.toShow.css("height","")}j.toShow.css("width",b);j.toShow.css({overflow:c});j.complete()}})},bounceslide:function(b){this.slide(b,{easing:b.down?"easeOutBounce":"swing",duration:b.down?1000:200})},easeslide:function(b){this.slide(b,{easing:"easeinout",duration:700})}}})})(jQuery);;/*
 + * jQuery UI Dialog 1.7.1
 + *
 + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 + * Dual licensed under the MIT (MIT-LICENSE.txt)
 + * and GPL (GPL-LICENSE.txt) licenses.
 + *
 + * http://docs.jquery.com/UI/Dialog
 + *
 + * Depends:
 + *	ui.core.js
 + *	ui.draggable.js
 + *	ui.resizable.js
 + */
(function(c){var b={dragStart:"start.draggable",drag:"drag.draggable",dragStop:"stop.draggable",maxHeight:"maxHeight.resizable",minHeight:"minHeight.resizable",maxWidth:"maxWidth.resizable",minWidth:"minWidth.resizable",resizeStart:"start.resizable",resize:"drag.resizable",resizeStop:"stop.resizable"},a="ui-dialog ui-widget ui-widget-content ui-corner-all ";c.widget("ui.dialog",{_init:function(){this.originalTitle=this.element.attr("title");var l=this,m=this.options,j=m.title||this.originalTitle||" ",e=c.ui.dialog.getTitleId(this.element),k=(this.uiDialog=c("<div/>")).appendTo(document.body).hide().addClass(a+m.dialogClass).css({position:"absolute",overflow:"hidden",zIndex:m.zIndex}).attr("tabIndex",-1).css("outline",0).keydown(function(n){(m.closeOnEscape&&n.keyCode&&n.keyCode==c.ui.keyCode.ESCAPE&&l.close(n))}).attr({role:"dialog","aria-labelledby":e}).mousedown(function(n){l.moveToTop(false,n)}),g=this.element.show().removeAttr("title").addClass("ui-dialog-content ui-widget-content").appendTo(k),f=(this.uiDialogTitlebar=c("<div></div>")).addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix").prependTo(k),i=c('<a href="#"/>').addClass("ui-dialog-titlebar-close ui-corner-all").attr("role","button").hover(function(){i.addClass("ui-state-hover")},function(){i.removeClass("ui-state-hover")}).focus(function(){i.addClass("ui-state-focus")}).blur(function(){i.removeClass("ui-state-focus")}).mousedown(function(n){n.stopPropagation()}).click(function(n){l.close(n);return false}).appendTo(f),h=(this.uiDialogTitlebarCloseText=c("<span/>")).addClass("ui-icon ui-icon-closethick").text(m.closeText).appendTo(i),d=c("<span/>").addClass("ui-dialog-title").attr("id",e).html(j).prependTo(f);f.find("*").add(f).disableSelection();(m.draggable&&c.fn.draggable&&this._makeDraggable());(m.resizable&&c.fn.resizable&&this._makeResizable());this._createButtons(m.buttons);this._isOpen=false;(m.bgiframe&&c.fn.bgiframe&&k.bgiframe());(m.autoOpen&&this.open())},destroy:function(){(this.overlay&&this.overlay.destroy());this.uiDialog.hide();this.element.unbind(".dialog").removeData("dialog").removeClass("ui-dialog-content ui-widget-content").hide().appendTo("body");this.uiDialog.remove();(this.originalTitle&&this.element.attr("title",this.originalTitle))},close:function(e){var d=this;if(false===d._trigger("beforeclose",e)){return}(d.overlay&&d.overlay.destroy());d.uiDialog.unbind("keypress.ui-dialog");(d.options.hide?d.uiDialog.hide(d.options.hide,function(){d._trigger("close",e)}):d.uiDialog.hide()&&d._trigger("close",e));c.ui.dialog.overlay.resize();d._isOpen=false},isOpen:function(){return this._isOpen},moveToTop:function(f,e){if((this.options.modal&&!f)||(!this.options.stack&&!this.options.modal)){return this._trigger("focus",e)}if(this.options.zIndex>c.ui.dialog.maxZ){c.ui.dialog.maxZ=this.options.zIndex}(this.overlay&&this.overlay.$el.css("z-index",c.ui.dialog.overlay.maxZ=++c.ui.dialog.maxZ));var d={scrollTop:this.element.attr("scrollTop"),scrollLeft:this.element.attr("scrollLeft")};this.uiDialog.css("z-index",++c.ui.dialog.maxZ);this.element.attr(d);this._trigger("focus",e)},open:function(){if(this._isOpen){return}var e=this.options,d=this.uiDialog;this.overlay=e.modal?new c.ui.dialog.overlay(this):null;(d.next().length&&d.appendTo("body"));this._size();this._position(e.position);d.show(e.show);this.moveToTop(true);(e.modal&&d.bind("keypress.ui-dialog",function(h){if(h.keyCode!=c.ui.keyCode.TAB){return}var g=c(":tabbable",this),i=g.filter(":first")[0],f=g.filter(":last")[0];if(h.target==f&&!h.shiftKey){setTimeout(function(){i.focus()},1)}else{if(h.target==i&&h.shiftKey){setTimeout(function(){f.focus()},1)}}}));c([]).add(d.find(".ui-dialog-content :tabbable:first")).add(d.find(".ui-dialog-buttonpane :tabbable:first")).add(d).filter(":first").focus();this._trigger("open");this._isOpen=true},_createButtons:function(g){var f=this,d=false,e=c("<div></div>").addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");this.uiDialog.find(".ui-dialog-buttonpane").remove();(typeof g=="object"&&g!==null&&c.each(g,function(){return !(d=true)}));if(d){c.each(g,function(h,i){c('<button type="button"></button>').addClass("ui-state-default ui-corner-all").text(h).click(function(){i.apply(f.element[0],arguments)}).hover(function(){c(this).addClass("ui-state-hover")},function(){c(this).removeClass("ui-state-hover")}).focus(function(){c(this).addClass("ui-state-focus")}).blur(function(){c(this).removeClass("ui-state-focus")}).appendTo(e)});e.appendTo(this.uiDialog)}},_makeDraggable:function(){var d=this,f=this.options,e;this.uiDialog.draggable({cancel:".ui-dialog-content",handle:".ui-dialog-titlebar",containment:"document",start:function(){e=f.height;c(this).height(c(this).height()).addClass("ui-dialog-dragging");(f.dragStart&&f.dragStart.apply(d.element[0],arguments))},drag:function(){(f.drag&&f.drag.apply(d.element[0],arguments))},stop:function(){c(this).removeClass("ui-dialog-dragging").height(e);(f.dragStop&&f.dragStop.apply(d.element[0],arguments));c.ui.dialog.overlay.resize()}})},_makeResizable:function(g){g=(g===undefined?this.options.resizable:g);var d=this,f=this.options,e=typeof g=="string"?g:"n,e,s,w,se,sw,ne,nw";this.uiDialog.resizable({cancel:".ui-dialog-content",alsoResize:this.element,maxWidth:f.maxWidth,maxHeight:f.maxHeight,minWidth:f.minWidth,minHeight:f.minHeight,start:function(){c(this).addClass("ui-dialog-resizing");(f.resizeStart&&f.resizeStart.apply(d.element[0],arguments))},resize:function(){(f.resize&&f.resize.apply(d.element[0],arguments))},handles:e,stop:function(){c(this).removeClass("ui-dialog-resizing");f.height=c(this).height();f.width=c(this).width();(f.resizeStop&&f.resizeStop.apply(d.element[0],arguments));c.ui.dialog.overlay.resize()}}).find(".ui-resizable-se").addClass("ui-icon ui-icon-grip-diagonal-se")},_position:function(i){var e=c(window),f=c(document),g=f.scrollTop(),d=f.scrollLeft(),h=g;if(c.inArray(i,["center","top","right","bottom","left"])>=0){i=[i=="right"||i=="left"?i:"center",i=="top"||i=="bottom"?i:"middle"]}if(i.constructor!=Array){i=["center","middle"]}if(i[0].constructor==Number){d+=i[0]}else{switch(i[0]){case"left":d+=0;break;case"right":d+=e.width()-this.uiDialog.outerWidth();break;default:case"center":d+=(e.width()-this.uiDialog.outerWidth())/2}}if(i[1].constructor==Number){g+=i[1]}else{switch(i[1]){case"top":g+=0;break;case"bottom":g+=e.height()-this.uiDialog.outerHeight();break;default:case"middle":g+=(e.height()-this.uiDialog.outerHeight())/2}}g=Math.max(g,h);this.uiDialog.css({top:g,left:d})},_setData:function(e,f){(b[e]&&this.uiDialog.data(b[e],f));switch(e){case"buttons":this._createButtons(f);break;case"closeText":this.uiDialogTitlebarCloseText.text(f);break;case"dialogClass":this.uiDialog.removeClass(this.options.dialogClass).addClass(a+f);break;case"draggable":(f?this._makeDraggable():this.uiDialog.draggable("destroy"));break;case"height":this.uiDialog.height(f);break;case"position":this._position(f);break;case"resizable":var d=this.uiDialog,g=this.uiDialog.is(":data(resizable)");(g&&!f&&d.resizable("destroy"));(g&&typeof f=="string"&&d.resizable("option","handles",f));(g||this._makeResizable(f));break;case"title":c(".ui-dialog-title",this.uiDialogTitlebar).html(f||" ");break;case"width":this.uiDialog.width(f);break}c.widget.prototype._setData.apply(this,arguments)},_size:function(){var e=this.options;this.element.css({height:0,minHeight:0,width:"auto"});var d=this.uiDialog.css({height:"auto",width:e.width}).height();this.element.css({minHeight:Math.max(e.minHeight-d,0),height:e.height=="auto"?"auto":Math.max(e.height-d,0)})}});c.extend(c.ui.dialog,{version:"1.7.1",defaults:{autoOpen:true,bgiframe:false,buttons:{},closeOnEscape:true,closeText:"close",dialogClass:"",draggable:true,hide:null,height:"auto",maxHeight:false,maxWidth:false,minHeight:150,minWidth:150,modal:false,position:"center",resizable:true,show:null,stack:true,title:"",width:300,zIndex:1000},getter:"isOpen",uuid:0,maxZ:0,getTitleId:function(d){return"ui-dialog-title-"+(d.attr("id")||++this.uuid)},overlay:function(d){this.$el=c.ui.dialog.overlay.create(d)}});c.extend(c.ui.dialog.overlay,{instances:[],maxZ:0,events:c.map("focus,mousedown,mouseup,keydown,keypress,click".split(","),function(d){return d+".dialog-overlay"}).join(" "),create:function(e){if(this.instances.length===0){setTimeout(function(){c(document).bind(c.ui.dialog.overlay.events,function(f){var g=c(f.target).parents(".ui-dialog").css("zIndex")||0;return(g>c.ui.dialog.overlay.maxZ)})},1);c(document).bind("keydown.dialog-overlay",function(f){(e.options.closeOnEscape&&f.keyCode&&f.keyCode==c.ui.keyCode.ESCAPE&&e.close(f))});c(window).bind("resize.dialog-overlay",c.ui.dialog.overlay.resize)}var d=c("<div></div>").appendTo(document.body).addClass("ui-widget-overlay").css({width:this.width(),height:this.height()});(e.options.bgiframe&&c.fn.bgiframe&&d.bgiframe());this.instances.push(d);return d},destroy:function(d){this.instances.splice(c.inArray(this.instances,d),1);if(this.instances.length===0){c([document,window]).unbind(".dialog-overlay")}d.remove()},height:function(){if(c.browser.msie&&c.browser.version<7){var e=Math.max(document.documentElement.scrollHeight,document.body.scrollHeight);var d=Math.max(document.documentElement.offsetHeight,document.body.offsetHeight);if(e<d){return c(window).height()+"px"}else{return e+"px"}}else{return c(document).height()+"px"}},width:function(){if(c.browser.msie&&c.browser.version<7){var d=Math.max(document.documentElement.scrollWidth,document.body.scrollWidth);var e=Math.max(document.documentElement.offsetWidth,document.body.offsetWidth);if(d<e){return c(window).width()+"px"}else{return d+"px"}}else{return c(document).width()+"px"}},resize:function(){var d=c([]);c.each(c.ui.dialog.overlay.instances,function(){d=d.add(this)});d.css({width:0,height:0}).css({width:c.ui.dialog.overlay.width(),height:c.ui.dialog.overlay.height()})}});c.extend(c.ui.dialog.overlay.prototype,{destroy:function(){c.ui.dialog.overlay.destroy(this.$el)}})})(jQuery);;/*
 + * jQuery UI Slider 1.7.1
 + *
 + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 + * Dual licensed under the MIT (MIT-LICENSE.txt)
 + * and GPL (GPL-LICENSE.txt) licenses.
 + *
 + * http://docs.jquery.com/UI/Slider
 + *
 + * Depends:
 + *	ui.core.js
 + */
(function(a){a.widget("ui.slider",a.extend({},a.ui.mouse,{_init:function(){var b=this,c=this.options;this._keySliding=false;this._handleIndex=null;this._detectOrientation();this._mouseInit();this.element.addClass("ui-slider ui-slider-"+this.orientation+" ui-widget ui-widget-content ui-corner-all");this.range=a([]);if(c.range){if(c.range===true){this.range=a("<div></div>");if(!c.values){c.values=[this._valueMin(),this._valueMin()]}if(c.values.length&&c.values.length!=2){c.values=[c.values[0],c.values[0]]}}else{this.range=a("<div></div>")}this.range.appendTo(this.element).addClass("ui-slider-range");if(c.range=="min"||c.range=="max"){this.range.addClass("ui-slider-range-"+c.range)}this.range.addClass("ui-widget-header")}if(a(".ui-slider-handle",this.element).length==0){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}if(c.values&&c.values.length){while(a(".ui-slider-handle",this.element).length<c.values.length){a('<a href="#"></a>').appendTo(this.element).addClass("ui-slider-handle")}}this.handles=a(".ui-slider-handle",this.element).addClass("ui-state-default ui-corner-all");this.handle=this.handles.eq(0);this.handles.add(this.range).filter("a").click(function(d){d.preventDefault()}).hover(function(){a(this).addClass("ui-state-hover")},function(){a(this).removeClass("ui-state-hover")}).focus(function(){a(".ui-slider .ui-state-focus").removeClass("ui-state-focus");a(this).addClass("ui-state-focus")}).blur(function(){a(this).removeClass("ui-state-focus")});this.handles.each(function(d){a(this).data("index.ui-slider-handle",d)});this.handles.keydown(function(i){var f=true;var e=a(this).data("index.ui-slider-handle");if(b.options.disabled){return}switch(i.keyCode){case a.ui.keyCode.HOME:case a.ui.keyCode.END:case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:f=false;if(!b._keySliding){b._keySliding=true;a(this).addClass("ui-state-active");b._start(i,e)}break}var g,d,h=b._step();if(b.options.values&&b.options.values.length){g=d=b.values(e)}else{g=d=b.value()}switch(i.keyCode){case a.ui.keyCode.HOME:d=b._valueMin();break;case a.ui.keyCode.END:d=b._valueMax();break;case a.ui.keyCode.UP:case a.ui.keyCode.RIGHT:if(g==b._valueMax()){return}d=g+h;break;case a.ui.keyCode.DOWN:case a.ui.keyCode.LEFT:if(g==b._valueMin()){return}d=g-h;break}b._slide(i,e,d);return f}).keyup(function(e){var d=a(this).data("index.ui-slider-handle");if(b._keySliding){b._stop(e,d);b._change(e,d);b._keySliding=false;a(this).removeClass("ui-state-active")}});this._refreshValue()},destroy:function(){this.handles.remove();this.range.remove();this.element.removeClass("ui-slider ui-slider-horizontal ui-slider-vertical ui-slider-disabled ui-widget ui-widget-content ui-corner-all").removeData("slider").unbind(".slider");this._mouseDestroy()},_mouseCapture:function(d){var e=this.options;if(e.disabled){return false}this.elementSize={width:this.element.outerWidth(),height:this.element.outerHeight()};this.elementOffset=this.element.offset();var h={x:d.pageX,y:d.pageY};var j=this._normValueFromMouse(h);var c=this._valueMax()-this._valueMin()+1,f;var k=this,i;this.handles.each(function(l){var m=Math.abs(j-k.values(l));if(c>m){c=m;f=a(this);i=l}});if(e.range==true&&this.values(1)==e.min){f=a(this.handles[++i])}this._start(d,i);k._handleIndex=i;f.addClass("ui-state-active").focus();var g=f.offset();var b=!a(d.target).parents().andSelf().is(".ui-slider-handle");this._clickOffset=b?{left:0,top:0}:{left:d.pageX-g.left-(f.width()/2),top:d.pageY-g.top-(f.height()/2)-(parseInt(f.css("borderTopWidth"),10)||0)-(parseInt(f.css("borderBottomWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0)};j=this._normValueFromMouse(h);this._slide(d,i,j);return true},_mouseStart:function(b){return true},_mouseDrag:function(d){var b={x:d.pageX,y:d.pageY};var c=this._normValueFromMouse(b);this._slide(d,this._handleIndex,c);return false},_mouseStop:function(b){this.handles.removeClass("ui-state-active");this._stop(b,this._handleIndex);this._change(b,this._handleIndex);this._handleIndex=null;this._clickOffset=null;return false},_detectOrientation:function(){this.orientation=this.options.orientation=="vertical"?"vertical":"horizontal"},_normValueFromMouse:function(d){var c,h;if("horizontal"==this.orientation){c=this.elementSize.width;h=d.x-this.elementOffset.left-(this._clickOffset?this._clickOffset.left:0)}else{c=this.elementSize.height;h=d.y-this.elementOffset.top-(this._clickOffset?this._clickOffset.top:0)}var f=(h/c);if(f>1){f=1}if(f<0){f=0}if("vertical"==this.orientation){f=1-f}var e=this._valueMax()-this._valueMin(),i=f*e,b=i%this.options.step,g=this._valueMin()+i-b;if(b>(this.options.step/2)){g+=this.options.step}return parseFloat(g.toFixed(5))},_start:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("start",d,b)},_slide:function(f,e,d){var g=this.handles[e];if(this.options.values&&this.options.values.length){var b=this.values(e?0:1);if((e==0&&d>=b)||(e==1&&d<=b)){d=b}if(d!=this.values(e)){var c=this.values();c[e]=d;var h=this._trigger("slide",f,{handle:this.handles[e],value:d,values:c});var b=this.values(e?0:1);if(h!==false){this.values(e,d,(f.type=="mousedown"&&this.options.animate),true)}}}else{if(d!=this.value()){var h=this._trigger("slide",f,{handle:this.handles[e],value:d});if(h!==false){this._setData("value",d,(f.type=="mousedown"&&this.options.animate))}}}},_stop:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("stop",d,b)},_change:function(d,c){var b={handle:this.handles[c],value:this.value()};if(this.options.values&&this.options.values.length){b.value=this.values(c);b.values=this.values()}this._trigger("change",d,b)},value:function(b){if(arguments.length){this._setData("value",b);this._change(null,0)}return this._value()},values:function(b,e,c,d){if(arguments.length>1){this.options.values[b]=e;this._refreshValue(c);if(!d){this._change(null,b)}}if(arguments.length){if(this.options.values&&this.options.values.length){return this._values(b)}else{return this.value()}}else{return this._values()}},_setData:function(b,d,c){a.widget.prototype._setData.apply(this,arguments);switch(b){case"orientation":this._detectOrientation();this.element.removeClass("ui-slider-horizontal ui-slider-vertical").addClass("ui-slider-"+this.orientation);this._refreshValue(c);break;case"value":this._refreshValue(c);break}},_step:function(){var b=this.options.step;return b},_value:function(){var b=this.options.value;if(b<this._valueMin()){b=this._valueMin()}if(b>this._valueMax()){b=this._valueMax()}return b},_values:function(b){if(arguments.length){var c=this.options.values[b];if(c<this._valueMin()){c=this._valueMin()}if(c>this._valueMax()){c=this._valueMax()}return c}else{return this.options.values}},_valueMin:function(){var b=this.options.min;return b},_valueMax:function(){var b=this.options.max;return b},_refreshValue:function(c){var f=this.options.range,d=this.options,l=this;if(this.options.values&&this.options.values.length){var i,h;this.handles.each(function(p,n){var o=(l.values(p)-l._valueMin())/(l._valueMax()-l._valueMin())*100;var m={};m[l.orientation=="horizontal"?"left":"bottom"]=o+"%";a(this).stop(1,1)[c?"animate":"css"](m,d.animate);if(l.options.range===true){if(l.orientation=="horizontal"){(p==0)&&l.range.stop(1,1)[c?"animate":"css"]({left:o+"%"},d.animate);(p==1)&&l.range[c?"animate":"css"]({width:(o-lastValPercent)+"%"},{queue:false,duration:d.animate})}else{(p==0)&&l.range.stop(1,1)[c?"animate":"css"]({bottom:(o)+"%"},d.animate);(p==1)&&l.range[c?"animate":"css"]({height:(o-lastValPercent)+"%"},{queue:false,duration:d.animate})}}lastValPercent=o})}else{var j=this.value(),g=this._valueMin(),k=this._valueMax(),e=k!=g?(j-g)/(k-g)*100:0;var b={};b[l.orientation=="horizontal"?"left":"bottom"]=e+"%";this.handle.stop(1,1)[c?"animate":"css"](b,d.animate);(f=="min")&&(this.orientation=="horizontal")&&this.range.stop(1,1)[c?"animate":"css"]({width:e+"%"},d.animate);(f=="max")&&(this.orientation=="horizontal")&&this.range[c?"animate":"css"]({width:(100-e)+"%"},{queue:false,duration:d.animate});(f=="min")&&(this.orientation=="vertical")&&this.range.stop(1,1)[c?"animate":"css"]({height:e+"%"},d.animate);(f=="max")&&(this.orientation=="vertical")&&this.range[c?"animate":"css"]({height:(100-e)+"%"},{queue:false,duration:d.animate})}}}));a.extend(a.ui.slider,{getter:"value values",version:"1.7.1",eventPrefix:"slide",defaults:{animate:false,delay:0,distance:0,max:100,min:0,orientation:"horizontal",range:false,step:1,value:0,values:null}})})(jQuery);;/*
 + * jQuery UI Tabs 1.7.1
 + *
 + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 + * Dual licensed under the MIT (MIT-LICENSE.txt)
 + * and GPL (GPL-LICENSE.txt) licenses.
 + *
 + * http://docs.jquery.com/UI/Tabs
 + *
 + * Depends:
 + *	ui.core.js
 + */
(function(a){a.widget("ui.tabs",{_init:function(){if(this.options.deselectable!==undefined){this.options.collapsible=this.options.deselectable}this._tabify(true)},_setData:function(b,c){if(b=="selected"){if(this.options.collapsible&&c==this.options.selected){return}this.select(c)}else{this.options[b]=c;if(b=="deselectable"){this.options.collapsible=c}this._tabify()}},_tabId:function(b){return b.title&&b.title.replace(/\s/g,"_").replace(/[^A-Za-z0-9\-_:\.]/g,"")||this.options.idPrefix+a.data(b)},_sanitizeSelector:function(b){return b.replace(/:/g,"\\:")},_cookie:function(){var b=this.cookie||(this.cookie=this.options.cookie.name||"ui-tabs-"+a.data(this.list[0]));return a.cookie.apply(null,[b].concat(a.makeArray(arguments)))},_ui:function(c,b){return{tab:c,panel:b,index:this.anchors.index(c)}},_cleanup:function(){this.lis.filter(".ui-state-processing").removeClass("ui-state-processing").find("span:data(label.tabs)").each(function(){var b=a(this);b.html(b.data("label.tabs")).removeData("label.tabs")})},_tabify:function(n){this.list=this.element.children("ul:first");this.lis=a("li:has(a[href])",this.list);this.anchors=this.lis.map(function(){return a("a",this)[0]});this.panels=a([]);var p=this,d=this.options;var c=/^#.+/;this.anchors.each(function(r,o){var q=a(o).attr("href");var s=q.split("#")[0],u;if(s&&(s===location.toString().split("#")[0]||(u=a("base")[0])&&s===u.href)){q=o.hash;o.href=q}if(c.test(q)){p.panels=p.panels.add(p._sanitizeSelector(q))}else{if(q!="#"){a.data(o,"href.tabs",q);a.data(o,"load.tabs",q.replace(/#.*$/,""));var w=p._tabId(o);o.href="#"+w;var v=a("#"+w);if(!v.length){v=a(d.panelTemplate).attr("id",w).addClass("ui-tabs-panel ui-widget-content ui-corner-bottom").insertAfter(p.panels[r-1]||p.list);v.data("destroy.tabs",true)}p.panels=p.panels.add(v)}else{d.disabled.push(r)}}});if(n){this.element.addClass("ui-tabs ui-widget ui-widget-content ui-corner-all");this.list.addClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.lis.addClass("ui-state-default ui-corner-top");this.panels.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom");if(d.selected===undefined){if(location.hash){this.anchors.each(function(q,o){if(o.hash==location.hash){d.selected=q;return false}})}if(typeof d.selected!="number"&&d.cookie){d.selected=parseInt(p._cookie(),10)}if(typeof d.selected!="number"&&this.lis.filter(".ui-tabs-selected").length){d.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"))}d.selected=d.selected||0}else{if(d.selected===null){d.selected=-1}}d.selected=((d.selected>=0&&this.anchors[d.selected])||d.selected<0)?d.selected:0;d.disabled=a.unique(d.disabled.concat(a.map(this.lis.filter(".ui-state-disabled"),function(q,o){return p.lis.index(q)}))).sort();if(a.inArray(d.selected,d.disabled)!=-1){d.disabled.splice(a.inArray(d.selected,d.disabled),1)}this.panels.addClass("ui-tabs-hide");this.lis.removeClass("ui-tabs-selected ui-state-active");if(d.selected>=0&&this.anchors.length){this.panels.eq(d.selected).removeClass("ui-tabs-hide");this.lis.eq(d.selected).addClass("ui-tabs-selected ui-state-active");p.element.queue("tabs",function(){p._trigger("show",null,p._ui(p.anchors[d.selected],p.panels[d.selected]))});this.load(d.selected)}a(window).bind("unload",function(){p.lis.add(p.anchors).unbind(".tabs");p.lis=p.anchors=p.panels=null})}else{d.selected=this.lis.index(this.lis.filter(".ui-tabs-selected"))}this.element[d.collapsible?"addClass":"removeClass"]("ui-tabs-collapsible");if(d.cookie){this._cookie(d.selected,d.cookie)}for(var g=0,m;(m=this.lis[g]);g++){a(m)[a.inArray(g,d.disabled)!=-1&&!a(m).hasClass("ui-tabs-selected")?"addClass":"removeClass"]("ui-state-disabled")}if(d.cache===false){this.anchors.removeData("cache.tabs")}this.lis.add(this.anchors).unbind(".tabs");if(d.event!="mouseover"){var f=function(o,i){if(i.is(":not(.ui-state-disabled)")){i.addClass("ui-state-"+o)}};var j=function(o,i){i.removeClass("ui-state-"+o)};this.lis.bind("mouseover.tabs",function(){f("hover",a(this))});this.lis.bind("mouseout.tabs",function(){j("hover",a(this))});this.anchors.bind("focus.tabs",function(){f("focus",a(this).closest("li"))});this.anchors.bind("blur.tabs",function(){j("focus",a(this).closest("li"))})}var b,h;if(d.fx){if(a.isArray(d.fx)){b=d.fx[0];h=d.fx[1]}else{b=h=d.fx}}function e(i,o){i.css({display:""});if(a.browser.msie&&o.opacity){i[0].style.removeAttribute("filter")}}var k=h?function(i,o){a(i).closest("li").removeClass("ui-state-default").addClass("ui-tabs-selected ui-state-active");o.hide().removeClass("ui-tabs-hide").animate(h,h.duration||"normal",function(){e(o,h);p._trigger("show",null,p._ui(i,o[0]))})}:function(i,o){a(i).closest("li").removeClass("ui-state-default").addClass("ui-tabs-selected ui-state-active");o.removeClass("ui-tabs-hide");p._trigger("show",null,p._ui(i,o[0]))};var l=b?function(o,i){i.animate(b,b.duration||"normal",function(){p.lis.removeClass("ui-tabs-selected ui-state-active").addClass("ui-state-default");i.addClass("ui-tabs-hide");e(i,b);p.element.dequeue("tabs")})}:function(o,i,q){p.lis.removeClass("ui-tabs-selected ui-state-active").addClass("ui-state-default");i.addClass("ui-tabs-hide");p.element.dequeue("tabs")};this.anchors.bind(d.event+".tabs",function(){var o=this,r=a(this).closest("li"),i=p.panels.filter(":not(.ui-tabs-hide)"),q=a(p._sanitizeSelector(this.hash));if((r.hasClass("ui-tabs-selected")&&!d.collapsible)||r.hasClass("ui-state-disabled")||r.hasClass("ui-state-processing")||p._trigger("select",null,p._ui(this,q[0]))===false){this.blur();return false}d.selected=p.anchors.index(this);p.abort();if(d.collapsible){if(r.hasClass("ui-tabs-selected")){d.selected=-1;if(d.cookie){p._cookie(d.selected,d.cookie)}p.element.queue("tabs",function(){l(o,i)}).dequeue("tabs");this.blur();return false}else{if(!i.length){if(d.cookie){p._cookie(d.selected,d.cookie)}p.element.queue("tabs",function(){k(o,q)});p.load(p.anchors.index(this));this.blur();return false}}}if(d.cookie){p._cookie(d.selected,d.cookie)}if(q.length){if(i.length){p.element.queue("tabs",function(){l(o,i)})}p.element.queue("tabs",function(){k(o,q)});p.load(p.anchors.index(this))}else{throw"jQuery UI Tabs: Mismatching fragment identifier."}if(a.browser.msie){this.blur()}});this.anchors.bind("click.tabs",function(){return false})},destroy:function(){var b=this.options;this.abort();this.element.unbind(".tabs").removeClass("ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible").removeData("tabs");this.list.removeClass("ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all");this.anchors.each(function(){var c=a.data(this,"href.tabs");if(c){this.href=c}var d=a(this).unbind(".tabs");a.each(["href","load","cache"],function(e,f){d.removeData(f+".tabs")})});this.lis.unbind(".tabs").add(this.panels).each(function(){if(a.data(this,"destroy.tabs")){a(this).remove()}else{a(this).removeClass(["ui-state-default","ui-corner-top","ui-tabs-selected","ui-state-active","ui-state-hover","ui-state-focus","ui-state-disabled","ui-tabs-panel","ui-widget-content","ui-corner-bottom","ui-tabs-hide"].join(" "))}});if(b.cookie){this._cookie(null,b.cookie)}},add:function(e,d,c){if(c===undefined){c=this.anchors.length}var b=this,g=this.options,i=a(g.tabTemplate.replace(/#\{href\}/g,e).replace(/#\{label\}/g,d)),h=!e.indexOf("#")?e.replace("#",""):this._tabId(a("a",i)[0]);i.addClass("ui-state-default ui-corner-top").data("destroy.tabs",true);var f=a("#"+h);if(!f.length){f=a(g.panelTemplate).attr("id",h).data("destroy.tabs",true)}f.addClass("ui-tabs-panel ui-widget-content ui-corner-bottom ui-tabs-hide");if(c>=this.lis.length){i.appendTo(this.list);f.appendTo(this.list[0].parentNode)}else{i.insertBefore(this.lis[c]);f.insertBefore(this.panels[c])}g.disabled=a.map(g.disabled,function(k,j){return k>=c?++k:k});this._tabify();if(this.anchors.length==1){i.addClass("ui-tabs-selected ui-state-active");f.removeClass("ui-tabs-hide");this.element.queue("tabs",function(){b._trigger("show",null,b._ui(b.anchors[0],b.panels[0]))});this.load(0)}this._trigger("add",null,this._ui(this.anchors[c],this.panels[c]))},remove:function(b){var d=this.options,e=this.lis.eq(b).remove(),c=this.panels.eq(b).remove();if(e.hasClass("ui-tabs-selected")&&this.anchors.length>1){this.select(b+(b+1<this.anchors.length?1:-1))}d.disabled=a.map(a.grep(d.disabled,function(g,f){return g!=b}),function(g,f){return g>=b?--g:g});this._tabify();this._trigger("remove",null,this._ui(e.find("a")[0],c[0]))},enable:function(b){var c=this.options;if(a.inArray(b,c.disabled)==-1){return}this.lis.eq(b).removeClass("ui-state-disabled");c.disabled=a.grep(c.disabled,function(e,d){return e!=b});this._trigger("enable",null,this._ui(this.anchors[b],this.panels[b]))},disable:function(c){var b=this,d=this.options;if(c!=d.selected){this.lis.eq(c).addClass("ui-state-disabled");d.disabled.push(c);d.disabled.sort();this._trigger("disable",null,this._ui(this.anchors[c],this.panels[c]))}},select:function(b){if(typeof b=="string"){b=this.anchors.index(this.anchors.filter("[href$="+b+"]"))}else{if(b===null){b=-1}}if(b==-1&&this.options.collapsible){b=this.options.selected}this.anchors.eq(b).trigger(this.options.event+".tabs")},load:function(e){var c=this,g=this.options,b=this.anchors.eq(e)[0],d=a.data(b,"load.tabs");this.abort();if(!d||this.element.queue("tabs").length!==0&&a.data(b,"cache.tabs")){this.element.dequeue("tabs");return}this.lis.eq(e).addClass("ui-state-processing");if(g.spinner){var f=a("span",b);f.data("label.tabs",f.html()).html(g.spinner)}this.xhr=a.ajax(a.extend({},g.ajaxOptions,{url:d,success:function(i,h){a(c._sanitizeSelector(b.hash)).html(i);c._cleanup();if(g.cache){a.data(b,"cache.tabs",true)}c._trigger("load",null,c._ui(c.anchors[e],c.panels[e]));try{g.ajaxOptions.success(i,h)}catch(j){}c.element.dequeue("tabs")}}))},abort:function(){this.element.queue([]);this.panels.stop(false,true);if(this.xhr){this.xhr.abort();delete this.xhr}this._cleanup()},url:function(c,b){this.anchors.eq(c).removeData("cache.tabs").data("load.tabs",b)},length:function(){return this.anchors.length}});a.extend(a.ui.tabs,{version:"1.7.1",getter:"length",defaults:{ajaxOptions:null,cache:false,cookie:null,collapsible:false,disabled:[],event:"click",fx:null,idPrefix:"ui-tabs-",panelTemplate:"<div></div>",spinner:"<em>Loading…</em>",tabTemplate:'<li><a href="#{href}"><span>#{label}</span></a></li>'}});a.extend(a.ui.tabs.prototype,{rotation:null,rotate:function(d,f){var b=this,g=this.options;var c=b._rotate||(b._rotate=function(h){clearTimeout(b.rotation);b.rotation=setTimeout(function(){var i=g.selected;b.select(++i<b.anchors.length?i:0)},d);if(h){h.stopPropagation()}});var e=b._unrotate||(b._unrotate=!f?function(h){if(h.clientX){b.rotate(null)}}:function(h){t=g.selected;c()});if(d){this.element.bind("tabsshow",c);this.anchors.bind(g.event+".tabs",e);c()}else{clearTimeout(b.rotation);this.element.unbind("tabsshow",c);this.anchors.unbind(g.event+".tabs",e);delete this._rotate;delete this._unrotate}}})})(jQuery);;/*
 + * jQuery UI Datepicker 1.7.1
 + *
 + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 + * Dual licensed under the MIT (MIT-LICENSE.txt)
 + * and GPL (GPL-LICENSE.txt) licenses.
 + *
 + * http://docs.jquery.com/UI/Datepicker
 + *
 + * Depends:
 + *	ui.core.js
 + */
(function($){$.extend($.ui,{datepicker:{version:"1.7.1"}});var PROP_NAME="datepicker";function Datepicker(){this.debug=false;this._curInst=null;this._keyEvent=false;this._disabledInputs=[];this._datepickerShowing=false;this._inDialog=false;this._mainDivId="ui-datepicker-div";this._inlineClass="ui-datepicker-inline";this._appendClass="ui-datepicker-append";this._triggerClass="ui-datepicker-trigger";this._dialogClass="ui-datepicker-dialog";this._disableClass="ui-datepicker-disabled";this._unselectableClass="ui-datepicker-unselectable";this._currentClass="ui-datepicker-current-day";this._dayOverClass="ui-datepicker-days-cell-over";this.regional=[];this.regional[""]={closeText:"Done",prevText:"Prev",nextText:"Next",currentText:"Today",monthNames:["January","February","March","April","May","June","July","August","September","October","November","December"],monthNamesShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],dayNames:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],dayNamesShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],dayNamesMin:["Su","Mo","Tu","We","Th","Fr","Sa"],dateFormat:"mm/dd/yy",firstDay:0,isRTL:false};this._defaults={showOn:"focus",showAnim:"show",showOptions:{},defaultDate:null,appendText:"",buttonText:"...",buttonImage:"",buttonImageOnly:false,hideIfNoPrevNext:false,navigationAsDateFormat:false,gotoCurrent:false,changeMonth:false,changeYear:false,showMonthAfterYear:false,yearRange:"-10:+10",showOtherMonths:false,calculateWeek:this.iso8601Week,shortYearCutoff:"+10",minDate:null,maxDate:null,duration:"normal",beforeShowDay:null,beforeShow:null,onSelect:null,onChangeMonthYear:null,onClose:null,numberOfMonths:1,showCurrentAtPos:0,stepMonths:1,stepBigMonths:12,altField:"",altFormat:"",constrainInput:true,showButtonPanel:false};$.extend(this._defaults,this.regional[""]);this.dpDiv=$('<div id="'+this._mainDivId+'" class="ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all ui-helper-hidden-accessible"></div>')}$.extend(Datepicker.prototype,{markerClassName:"hasDatepicker",log:function(){if(this.debug){console.log.apply("",arguments)}},setDefaults:function(settings){extendRemove(this._defaults,settings||{});return this},_attachDatepicker:function(target,settings){var inlineSettings=null;for(var attrName in this._defaults){var attrValue=target.getAttribute("date:"+attrName);if(attrValue){inlineSettings=inlineSettings||{};try{inlineSettings[attrName]=eval(attrValue)}catch(err){inlineSettings[attrName]=attrValue}}}var nodeName=target.nodeName.toLowerCase();var inline=(nodeName=="div"||nodeName=="span");if(!target.id){target.id="dp"+(++this.uuid)}var inst=this._newInst($(target),inline);inst.settings=$.extend({},settings||{},inlineSettings||{});if(nodeName=="input"){this._connectDatepicker(target,inst)}else{if(inline){this._inlineDatepicker(target,inst)}}},_newInst:function(target,inline){var id=target[0].id.replace(/([:\[\]\.])/g,"\\\\$1");return{id:id,input:target,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:inline,dpDiv:(!inline?this.dpDiv:$('<div class="'+this._inlineClass+' ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all"></div>'))}},_connectDatepicker:function(target,inst){var input=$(target);inst.trigger=$([]);if(input.hasClass(this.markerClassName)){return}var appendText=this._get(inst,"appendText");var isRTL=this._get(inst,"isRTL");if(appendText){input[isRTL?"before":"after"]('<span class="'+this._appendClass+'">'+appendText+"</span>")}var showOn=this._get(inst,"showOn");if(showOn=="focus"||showOn=="both"){input.focus(this._showDatepicker)}if(showOn=="button"||showOn=="both"){var buttonText=this._get(inst,"buttonText");var buttonImage=this._get(inst,"buttonImage");inst.trigger=$(this._get(inst,"buttonImageOnly")?$("<img/>").addClass(this._triggerClass).attr({src:buttonImage,alt:buttonText,title:buttonText}):$('<button type="button"></button>').addClass(this._triggerClass).html(buttonImage==""?buttonText:$("<img/>").attr({src:buttonImage,alt:buttonText,title:buttonText})));input[isRTL?"before":"after"](inst.trigger);inst.trigger.click(function(){if($.datepicker._datepickerShowing&&$.datepicker._lastInput==target){$.datepicker._hideDatepicker()}else{$.datepicker._showDatepicker(target)}return false})}input.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).bind("setData.datepicker",function(event,key,value){inst.settings[key]=value}).bind("getData.datepicker",function(event,key){return this._get(inst,key)});$.data(target,PROP_NAME,inst)},_inlineDatepicker:function(target,inst){var divSpan=$(target);if(divSpan.hasClass(this.markerClassName)){return}divSpan.addClass(this.markerClassName).append(inst.dpDiv).bind("setData.datepicker",function(event,key,value){inst.settings[key]=value}).bind("getData.datepicker",function(event,key){return this._get(inst,key)});$.data(target,PROP_NAME,inst);this._setDate(inst,this._getDefaultDate(inst));this._updateDatepicker(inst);this._updateAlternate(inst)},_dialogDatepicker:function(input,dateText,onSelect,settings,pos){var inst=this._dialogInst;if(!inst){var id="dp"+(++this.uuid);this._dialogInput=$('<input type="text" id="'+id+'" size="1" style="position: absolute; top: -100px;"/>');this._dialogInput.keydown(this._doKeyDown);$("body").append(this._dialogInput);inst=this._dialogInst=this._newInst(this._dialogInput,false);inst.settings={};$.data(this._dialogInput[0],PROP_NAME,inst)}extendRemove(inst.settings,settings||{});this._dialogInput.val(dateText);this._pos=(pos?(pos.length?pos:[pos.pageX,pos.pageY]):null);if(!this._pos){var browserWidth=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth;var browserHeight=window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight;var scrollX=document.documentElement.scrollLeft||document.body.scrollLeft;var scrollY=document.documentElement.scrollTop||document.body.scrollTop;this._pos=[(browserWidth/2)-100+scrollX,(browserHeight/2)-150+scrollY]}this._dialogInput.css("left",this._pos[0]+"px").css("top",this._pos[1]+"px");inst.settings.onSelect=onSelect;this._inDialog=true;this.dpDiv.addClass(this._dialogClass);this._showDatepicker(this._dialogInput[0]);if($.blockUI){$.blockUI(this.dpDiv)}$.data(this._dialogInput[0],PROP_NAME,inst);return this},_destroyDatepicker:function(target){var $target=$(target);var inst=$.data(target,PROP_NAME);if(!$target.hasClass(this.markerClassName)){return}var nodeName=target.nodeName.toLowerCase();$.removeData(target,PROP_NAME);if(nodeName=="input"){inst.trigger.remove();$target.siblings("."+this._appendClass).remove().end().removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress)}else{if(nodeName=="div"||nodeName=="span"){$target.removeClass(this.markerClassName).empty()}}},_enableDatepicker:function(target){var $target=$(target);var inst=$.data(target,PROP_NAME);if(!$target.hasClass(this.markerClassName)){return}var nodeName=target.nodeName.toLowerCase();if(nodeName=="input"){target.disabled=false;inst.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else{if(nodeName=="div"||nodeName=="span"){var inline=$target.children("."+this._inlineClass);inline.children().removeClass("ui-state-disabled")}}this._disabledInputs=$.map(this._disabledInputs,function(value){return(value==target?null:value)})},_disableDatepicker:function(target){var $target=$(target);var inst=$.data(target,PROP_NAME);if(!$target.hasClass(this.markerClassName)){return}var nodeName=target.nodeName.toLowerCase();if(nodeName=="input"){target.disabled=true;inst.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else{if(nodeName=="div"||nodeName=="span"){var inline=$target.children("."+this._inlineClass);inline.children().addClass("ui-state-disabled")}}this._disabledInputs=$.map(this._disabledInputs,function(value){return(value==target?null:value)});this._disabledInputs[this._disabledInputs.length]=target},_isDisabledDatepicker:function(target){if(!target){return false}for(var i=0;i<this._disabledInputs.length;i++){if(this._disabledInputs[i]==target){return true}}return false},_getInst:function(target){try{return $.data(target,PROP_NAME)}catch(err){throw"Missing instance data for this datepicker"}},_optionDatepicker:function(target,name,value){var settings=name||{};if(typeof name=="string"){settings={};settings[name]=value}var inst=this._getInst(target);if(inst){if(this._curInst==inst){this._hideDatepicker(null)}extendRemove(inst.settings,settings);var date=new Date();extendRemove(inst,{rangeStart:null,endDay:null,endMonth:null,endYear:null,selectedDay:date.getDate(),selectedMonth:date.getMonth(),selectedYear:date.getFullYear(),currentDay:date.getDate(),currentMonth:date.getMonth(),currentYear:date.getFullYear(),drawMonth:date.getMonth(),drawYear:date.getFullYear()});this._updateDatepicker(inst)}},_changeDatepicker:function(target,name,value){this._optionDatepicker(target,name,value)},_refreshDatepicker:function(target){var inst=this._getInst(target);if(inst){this._updateDatepicker(inst)}},_setDateDatepicker:function(target,date,endDate){var inst=this._getInst(target);if(inst){this._setDate(inst,date,endDate);this._updateDatepicker(inst);this._updateAlternate(inst)}},_getDateDatepicker:function(target){var inst=this._getInst(target);if(inst&&!inst.inline){this._setDateFromField(inst)}return(inst?this._getDate(inst):null)},_doKeyDown:function(event){var inst=$.datepicker._getInst(event.target);var handled=true;var isRTL=inst.dpDiv.is(".ui-datepicker-rtl");inst._keyEvent=true;if($.datepicker._datepickerShowing){switch(event.keyCode){case 9:$.datepicker._hideDatepicker(null,"");break;case 13:var sel=$("td."+$.datepicker._dayOverClass+", td."+$.datepicker._currentClass,inst.dpDiv);if(sel[0]){$.datepicker._selectDay(event.target,inst.selectedMonth,inst.selectedYear,sel[0])}else{$.datepicker._hideDatepicker(null,$.datepicker._get(inst,"duration"))}return false;break;case 27:$.datepicker._hideDatepicker(null,$.datepicker._get(inst,"duration"));break;case 33:$.datepicker._adjustDate(event.target,(event.ctrlKey?-$.datepicker._get(inst,"stepBigMonths"):-$.datepicker._get(inst,"stepMonths")),"M");break;case 34:$.datepicker._adjustDate(event.target,(event.ctrlKey?+$.datepicker._get(inst,"stepBigMonths"):+$.datepicker._get(inst,"stepMonths")),"M");break;case 35:if(event.ctrlKey||event.metaKey){$.datepicker._clearDate(event.target)}handled=event.ctrlKey||event.metaKey;break;case 36:if(event.ctrlKey||event.metaKey){$.datepicker._gotoToday(event.target)}handled=event.ctrlKey||event.metaKey;break;case 37:if(event.ctrlKey||event.metaKey){$.datepicker._adjustDate(event.target,(isRTL?+1:-1),"D")}handled=event.ctrlKey||event.metaKey;if(event.originalEvent.altKey){$.datepicker._adjustDate(event.target,(event.ctrlKey?-$.datepicker._get(inst,"stepBigMonths"):-$.datepicker._get(inst,"stepMonths")),"M")}break;case 38:if(event.ctrlKey||event.metaKey){$.datepicker._adjustDate(event.target,-7,"D")}handled=event.ctrlKey||event.metaKey;break;case 39:if(event.ctrlKey||event.metaKey){$.datepicker._adjustDate(event.target,(isRTL?-1:+1),"D")}handled=event.ctrlKey||event.metaKey;if(event.originalEvent.altKey){$.datepicker._adjustDate(event.target,(event.ctrlKey?+$.datepicker._get(inst,"stepBigMonths"):+$.datepicker._get(inst,"stepMonths")),"M")}break;case 40:if(event.ctrlKey||event.metaKey){$.datepicker._adjustDate(event.target,+7,"D")}handled=event.ctrlKey||event.metaKey;break;default:handled=false}}else{if(event.keyCode==36&&event.ctrlKey){$.datepicker._showDatepicker(this)}else{handled=false}}if(handled){event.preventDefault();event.stopPropagation()}},_doKeyPress:function(event){var inst=$.datepicker._getInst(event.target);if($.datepicker._get(inst,"constrainInput")){var chars=$.datepicker._possibleChars($.datepicker._get(inst,"dateFormat"));var chr=String.fromCharCode(event.charCode==undefined?event.keyCode:event.charCode);return event.ctrlKey||(chr<" "||!chars||chars.indexOf(chr)>-1)}},_showDatepicker:function(input){input=input.target||input;if(input.nodeName.toLowerCase()!="input"){input=$("input",input.parentNode)[0]}if($.datepicker._isDisabledDatepicker(input)||$.datepicker._lastInput==input){return}var inst=$.datepicker._getInst(input);var beforeShow=$.datepicker._get(inst,"beforeShow");extendRemove(inst.settings,(beforeShow?beforeShow.apply(input,[input,inst]):{}));$.datepicker._hideDatepicker(null,"");$.datepicker._lastInput=input;$.datepicker._setDateFromField(inst);if($.datepicker._inDialog){input.value=""}if(!$.datepicker._pos){$.datepicker._pos=$.datepicker._findPos(input);$.datepicker._pos[1]+=input.offsetHeight}var isFixed=false;$(input).parents().each(function(){isFixed|=$(this).css("position")=="fixed";return !isFixed});if(isFixed&&$.browser.opera){$.datepicker._pos[0]-=document.documentElement.scrollLeft;$.datepicker._pos[1]-=document.documentElement.scrollTop}var offset={left:$.datepicker._pos[0],top:$.datepicker._pos[1]};$.datepicker._pos=null;inst.rangeStart=null;inst.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});$.datepicker._updateDatepicker(inst);offset=$.datepicker._checkOffset(inst,offset,isFixed);inst.dpDiv.css({position:($.datepicker._inDialog&&$.blockUI?"static":(isFixed?"fixed":"absolute")),display:"none",left:offset.left+"px",top:offset.top+"px"});if(!inst.inline){var showAnim=$.datepicker._get(inst,"showAnim")||"show";var duration=$.datepicker._get(inst,"duration");var postProcess=function(){$.datepicker._datepickerShowing=true;if($.browser.msie&&parseInt($.browser.version,10)<7){$("iframe.ui-datepicker-cover").css({width:inst.dpDiv.width()+4,height:inst.dpDiv.height()+4})}};if($.effects&&$.effects[showAnim]){inst.dpDiv.show(showAnim,$.datepicker._get(inst,"showOptions"),duration,postProcess)}else{inst.dpDiv[showAnim](duration,postProcess)}if(duration==""){postProcess()}if(inst.input[0].type!="hidden"){inst.input[0].focus()}$.datepicker._curInst=inst}},_updateDatepicker:function(inst){var dims={width:inst.dpDiv.width()+4,height:inst.dpDiv.height()+4};var self=this;inst.dpDiv.empty().append(this._generateHTML(inst)).find("iframe.ui-datepicker-cover").css({width:dims.width,height:dims.height}).end().find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout",function(){$(this).removeClass("ui-state-hover");if(this.className.indexOf("ui-datepicker-prev")!=-1){$(this).removeClass("ui-datepicker-prev-hover")}if(this.className.indexOf("ui-datepicker-next")!=-1){$(this).removeClass("ui-datepicker-next-hover")}}).bind("mouseover",function(){if(!self._isDisabledDatepicker(inst.inline?inst.dpDiv.parent()[0]:inst.input[0])){$(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");$(this).addClass("ui-state-hover");if(this.className.indexOf("ui-datepicker-prev")!=-1){$(this).addClass("ui-datepicker-prev-hover")}if(this.className.indexOf("ui-datepicker-next")!=-1){$(this).addClass("ui-datepicker-next-hover")}}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();var numMonths=this._getNumberOfMonths(inst);var cols=numMonths[1];var width=17;if(cols>1){inst.dpDiv.addClass("ui-datepicker-multi-"+cols).css("width",(width*cols)+"em")}else{inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("")}inst.dpDiv[(numMonths[0]!=1||numMonths[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");inst.dpDiv[(this._get(inst,"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");if(inst.input&&inst.input[0].type!="hidden"&&inst==$.datepicker._curInst){$(inst.input[0]).focus()}},_checkOffset:function(inst,offset,isFixed){var dpWidth=inst.dpDiv.outerWidth();var dpHeight=inst.dpDiv.outerHeight();var inputWidth=inst.input?inst.input.outerWidth():0;var inputHeight=inst.input?inst.input.outerHeight():0;var viewWidth=(window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth)+$(document).scrollLeft();var viewHeight=(window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight)+$(document).scrollTop();offset.left-=(this._get(inst,"isRTL")?(dpWidth-inputWidth):0);offset.left-=(isFixed&&offset.left==inst.input.offset().left)?$(document).scrollLeft():0;offset.top-=(isFixed&&offset.top==(inst.input.offset().top+inputHeight))?$(document).scrollTop():0;offset.left-=(offset.left+dpWidth>viewWidth&&viewWidth>dpWidth)?Math.abs(offset.left+dpWidth-viewWidth):0;offset.top-=(offset.top+dpHeight>viewHeight&&viewHeight>dpHeight)?Math.abs(offset.top+dpHeight+inputHeight*2-viewHeight):0;return offset},_findPos:function(obj){while(obj&&(obj.type=="hidden"||obj.nodeType!=1)){obj=obj.nextSibling}var position=$(obj).offset();return[position.left,position.top]},_hideDatepicker:function(input,duration){var inst=this._curInst;if(!inst||(input&&inst!=$.data(input,PROP_NAME))){return}if(inst.stayOpen){this._selectDate("#"+inst.id,this._formatDate(inst,inst.currentDay,inst.currentMonth,inst.currentYear))}inst.stayOpen=false;if(this._datepickerShowing){duration=(duration!=null?duration:this._get(inst,"duration"));var showAnim=this._get(inst,"showAnim");var postProcess=function(){$.datepicker._tidyDialog(inst)};if(duration!=""&&$.effects&&$.effects[showAnim]){inst.dpDiv.hide(showAnim,$.datepicker._get(inst,"showOptions"),duration,postProcess)}else{inst.dpDiv[(duration==""?"hide":(showAnim=="slideDown"?"slideUp":(showAnim=="fadeIn"?"fadeOut":"hide")))](duration,postProcess)}if(duration==""){this._tidyDialog(inst)}var onClose=this._get(inst,"onClose");if(onClose){onClose.apply((inst.input?inst.input[0]:null),[(inst.input?inst.input.val():""),inst])}this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if($.blockUI){$.unblockUI();$("body").append(this.dpDiv)}}this._inDialog=false}this._curInst=null},_tidyDialog:function(inst){inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")},_checkExternalClick:function(event){if(!$.datepicker._curInst){return}var $target=$(event.target);if(($target.parents("#"+$.datepicker._mainDivId).length==0)&&!$target.hasClass($.datepicker.markerClassName)&&!$target.hasClass($.datepicker._triggerClass)&&$.datepicker._datepickerShowing&&!($.datepicker._inDialog&&$.blockUI)){$.datepicker._hideDatepicker(null,"")}},_adjustDate:function(id,offset,period){var target=$(id);var inst=this._getInst(target[0]);if(this._isDisabledDatepicker(target[0])){return}this._adjustInstDate(inst,offset+(period=="M"?this._get(inst,"showCurrentAtPos"):0),period);this._updateDatepicker(inst)},_gotoToday:function(id){var target=$(id);var inst=this._getInst(target[0]);if(this._get(inst,"gotoCurrent")&&inst.currentDay){inst.selectedDay=inst.currentDay;inst.drawMonth=inst.selectedMonth=inst.currentMonth;inst.drawYear=inst.selectedYear=inst.currentYear}else{var date=new Date();inst.selectedDay=date.getDate();inst.drawMonth=inst.selectedMonth=date.getMonth();inst.drawYear=inst.selectedYear=date.getFullYear()}this._notifyChange(inst);this._adjustDate(target)},_selectMonthYear:function(id,select,period){var target=$(id);var inst=this._getInst(target[0]);inst._selectingMonthYear=false;inst["selected"+(period=="M"?"Month":"Year")]=inst["draw"+(period=="M"?"Month":"Year")]=parseInt(select.options[select.selectedIndex].value,10);this._notifyChange(inst);this._adjustDate(target)},_clickMonthYear:function(id){var target=$(id);var inst=this._getInst(target[0]);if(inst.input&&inst._selectingMonthYear&&!$.browser.msie){inst.input[0].focus()}inst._selectingMonthYear=!inst._selectingMonthYear},_selectDay:function(id,month,year,td){var target=$(id);if($(td).hasClass(this._unselectableClass)||this._isDisabledDatepicker(target[0])){return}var inst=this._getInst(target[0]);inst.selectedDay=inst.currentDay=$("a",td).html();inst.selectedMonth=inst.currentMonth=month;inst.selectedYear=inst.currentYear=year;if(inst.stayOpen){inst.endDay=inst.endMonth=inst.endYear=null}this._selectDate(id,this._formatDate(inst,inst.currentDay,inst.currentMonth,inst.currentYear));if(inst.stayOpen){inst.rangeStart=this._daylightSavingAdjust(new Date(inst.currentYear,inst.currentMonth,inst.currentDay));this._updateDatepicker(inst)}},_clearDate:function(id){var target=$(id);var inst=this._getInst(target[0]);inst.stayOpen=false;inst.endDay=inst.endMonth=inst.endYear=inst.rangeStart=null;this._selectDate(target,"")},_selectDate:function(id,dateStr){var target=$(id);var inst=this._getInst(target[0]);dateStr=(dateStr!=null?dateStr:this._formatDate(inst));if(inst.input){inst.input.val(dateStr)}this._updateAlternate(inst);var onSelect=this._get(inst,"onSelect");if(onSelect){onSelect.apply((inst.input?inst.input[0]:null),[dateStr,inst])}else{if(inst.input){inst.input.trigger("change")}}if(inst.inline){this._updateDatepicker(inst)}else{if(!inst.stayOpen){this._hideDatepicker(null,this._get(inst,"duration"));this._lastInput=inst.input[0];if(typeof(inst.input[0])!="object"){inst.input[0].focus()}this._lastInput=null}}},_updateAlternate:function(inst){var altField=this._get(inst,"altField");if(altField){var altFormat=this._get(inst,"altFormat")||this._get(inst,"dateFormat");var date=this._getDate(inst);dateStr=this.formatDate(altFormat,date,this._getFormatConfig(inst));$(altField).each(function(){$(this).val(dateStr)})}},noWeekends:function(date){var day=date.getDay();return[(day>0&&day<6),""]},iso8601Week:function(date){var checkDate=new Date(date.getFullYear(),date.getMonth(),date.getDate());var firstMon=new Date(checkDate.getFullYear(),1-1,4);var firstDay=firstMon.getDay()||7;firstMon.setDate(firstMon.getDate()+1-firstDay);if(firstDay<4&&checkDate<firstMon){checkDate.setDate(checkDate.getDate()-3);return $.datepicker.iso8601Week(checkDate)}else{if(checkDate>new Date(checkDate.getFullYear(),12-1,28)){firstDay=new Date(checkDate.getFullYear()+1,1-1,4).getDay()||7;if(firstDay>4&&(checkDate.getDay()||7)<firstDay-3){return 1}}}return Math.floor(((checkDate-firstMon)/86400000)/7)+1},parseDate:function(format,value,settings){if(format==null||value==null){throw"Invalid arguments"}value=(typeof value=="object"?value.toString():value+"");if(value==""){return null}var shortYearCutoff=(settings?settings.shortYearCutoff:null)||this._defaults.shortYearCutoff;var dayNamesShort=(settings?settings.dayNamesShort:null)||this._defaults.dayNamesShort;var dayNames=(settings?settings.dayNames:null)||this._defaults.dayNames;var monthNamesShort=(settings?settings.monthNamesShort:null)||this._defaults.monthNamesShort;var monthNames=(settings?settings.monthNames:null)||this._defaults.monthNames;var year=-1;var month=-1;var day=-1;var doy=-1;var literal=false;var lookAhead=function(match){var matches=(iFormat+1<format.length&&format.charAt(iFormat+1)==match);if(matches){iFormat++}return matches};var getNumber=function(match){lookAhead(match);var origSize=(match=="@"?14:(match=="y"?4:(match=="o"?3:2)));var size=origSize;var num=0;while(size>0&&iValue<value.length&&value.charAt(iValue)>="0"&&value.charAt(iValue)<="9"){num=num*10+parseInt(value.charAt(iValue++),10);size--}if(size==origSize){throw"Missing number at position "+iValue}return num};var getName=function(match,shortNames,longNames){var names=(lookAhead(match)?longNames:shortNames);var size=0;for(var j=0;j<names.length;j++){size=Math.max(size,names[j].length)}var name="";var iInit=iValue;while(size>0&&iValue<value.length){name+=value.charAt(iValue++);for(var i=0;i<names.length;i++){if(name==names[i]){return i+1}}size--}throw"Unknown name at position "+iInit};var checkLiteral=function(){if(value.charAt(iValue)!=format.charAt(iFormat)){throw"Unexpected literal at position "+iValue}iValue++};var iValue=0;for(var iFormat=0;iFormat<format.length;iFormat++){if(literal){if(format.charAt(iFormat)=="'"&&!lookAhead("'")){literal=false}else{checkLiteral()}}else{switch(format.charAt(iFormat)){case"d":day=getNumber("d");break;case"D":getName("D",dayNamesShort,dayNames);break;case"o":doy=getNumber("o");break;case"m":month=getNumber("m");break;case"M":month=getName("M",monthNamesShort,monthNames);break;case"y":year=getNumber("y");break;case"@":var date=new Date(getNumber("@"));year=date.getFullYear();month=date.getMonth()+1;day=date.getDate();break;case"'":if(lookAhead("'")){checkLiteral()}else{literal=true}break;default:checkLiteral()}}}if(year==-1){year=new Date().getFullYear()}else{if(year<100){year+=new Date().getFullYear()-new Date().getFullYear()%100+(year<=shortYearCutoff?0:-100)}}if(doy>-1){month=1;day=doy;do{var dim=this._getDaysInMonth(year,month-1);if(day<=dim){break}month++;day-=dim}while(true)}var date=this._daylightSavingAdjust(new Date(year,month-1,day));if(date.getFullYear()!=year||date.getMonth()+1!=month||date.getDate()!=day){throw"Invalid date"}return date},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y",RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TIMESTAMP:"@",W3C:"yy-mm-dd",formatDate:function(format,date,settings){if(!date){return""}var dayNamesShort=(settings?settings.dayNamesShort:null)||this._defaults.dayNamesShort;var dayNames=(settings?settings.dayNames:null)||this._defaults.dayNames;var monthNamesShort=(settings?settings.monthNamesShort:null)||this._defaults.monthNamesShort;var monthNames=(settings?settings.monthNames:null)||this._defaults.monthNames;var lookAhead=function(match){var matches=(iFormat+1<format.length&&format.charAt(iFormat+1)==match);if(matches){iFormat++}return matches};var formatNumber=function(match,value,len){var num=""+value;if(lookAhead(match)){while(num.length<len){num="0"+num}}return num};var formatName=function(match,value,shortNames,longNames){return(lookAhead(match)?longNames[value]:shortNames[value])};var output="";var literal=false;if(date){for(var iFormat=0;iFormat<format.length;iFormat++){if(literal){if(format.charAt(iFormat)=="'"&&!lookAhead("'")){literal=false}else{output+=format.charAt(iFormat)}}else{switch(format.charAt(iFormat)){case"d":output+=formatNumber("d",date.getDate(),2);break;case"D":output+=formatName("D",date.getDay(),dayNamesShort,dayNames);break;case"o":var doy=date.getDate();for(var m=date.getMonth()-1;m>=0;m--){doy+=this._getDaysInMonth(date.getFullYear(),m)}output+=formatNumber("o",doy,3);break;case"m":output+=formatNumber("m",date.getMonth()+1,2);break;case"M":output+=formatName("M",date.getMonth(),monthNamesShort,monthNames);break;case"y":output+=(lookAhead("y")?date.getFullYear():(date.getYear()%100<10?"0":"")+date.getYear()%100);break;case"@":output+=date.getTime();break;case"'":if(lookAhead("'")){output+="'"}else{literal=true}break;default:output+=format.charAt(iFormat)}}}}return output},_possibleChars:function(format){var chars="";var literal=false;for(var iFormat=0;iFormat<format.length;iFormat++){if(literal){if(format.charAt(iFormat)=="'"&&!lookAhead("'")){literal=false}else{chars+=format.charAt(iFormat)}}else{switch(format.charAt(iFormat)){case"d":case"m":case"y":case"@":chars+="0123456789";break;case"D":case"M":return null;case"'":if(lookAhead("'")){chars+="'"}else{literal=true}break;default:chars+=format.charAt(iFormat)}}}return chars},_get:function(inst,name){return inst.settings[name]!==undefined?inst.settings[name]:this._defaults[name]},_setDateFromField:function(inst){var dateFormat=this._get(inst,"dateFormat");var dates=inst.input?inst.input.val():null;inst.endDay=inst.endMonth=inst.endYear=null;var date=defaultDate=this._getDefaultDate(inst);var settings=this._getFormatConfig(inst);try{date=this.parseDate(dateFormat,dates,settings)||defaultDate}catch(event){this.log(event);date=defaultDate}inst.selectedDay=date.getDate();inst.drawMonth=inst.selectedMonth=date.getMonth();inst.drawYear=inst.selectedYear=date.getFullYear();inst.currentDay=(dates?date.getDate():0);inst.currentMonth=(dates?date.getMonth():0);inst.currentYear=(dates?date.getFullYear():0);this._adjustInstDate(inst)},_getDefaultDate:function(inst){var date=this._determineDate(this._get(inst,"defaultDate"),new Date());var minDate=this._getMinMaxDate(inst,"min",true);var maxDate=this._getMinMaxDate(inst,"max");date=(minDate&&date<minDate?minDate:date);date=(maxDate&&date>maxDate?maxDate:date);return date},_determineDate:function(date,defaultDate){var offsetNumeric=function(offset){var date=new Date();date.setDate(date.getDate()+offset);return date};var offsetString=function(offset,getDaysInMonth){var date=new Date();var year=date.getFullYear();var month=date.getMonth();var day=date.getDate();var pattern=/([+-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g;var matches=pattern.exec(offset);while(matches){switch(matches[2]||"d"){case"d":case"D":day+=parseInt(matches[1],10);break;case"w":case"W":day+=parseInt(matches[1],10)*7;break;case"m":case"M":month+=parseInt(matches[1],10);day=Math.min(day,getDaysInMonth(year,month));break;case"y":case"Y":year+=parseInt(matches[1],10);day=Math.min(day,getDaysInMonth(year,month));break}matches=pattern.exec(offset)}return new Date(year,month,day)};date=(date==null?defaultDate:(typeof date=="string"?offsetString(date,this._getDaysInMonth):(typeof date=="number"?(isNaN(date)?defaultDate:offsetNumeric(date)):date)));date=(date&&date.toString()=="Invalid Date"?defaultDate:date);if(date){date.setHours(0);date.setMinutes(0);date.setSeconds(0);date.setMilliseconds(0)}return this._daylightSavingAdjust(date)},_daylightSavingAdjust:function(date){if(!date){return null}date.setHours(date.getHours()>12?date.getHours()+2:0);return date},_setDate:function(inst,date,endDate){var clear=!(date);var origMonth=inst.selectedMonth;var origYear=inst.selectedYear;date=this._determineDate(date,new Date());inst.selectedDay=inst.currentDay=date.getDate();inst.drawMonth=inst.selectedMonth=inst.currentMonth=date.getMonth();inst.drawYear=inst.selectedYear=inst.currentYear=date.getFullYear();if(origMonth!=inst.selectedMonth||origYear!=inst.selectedYear){this._notifyChange(inst)}this._adjustInstDate(inst);if(inst.input){inst.input.val(clear?"":this._formatDate(inst))}},_getDate:function(inst){var startDate=(!inst.currentYear||(inst.input&&inst.input.val()=="")?null:this._daylightSavingAdjust(new Date(inst.currentYear,inst.currentMonth,inst.currentDay)));return startDate},_generateHTML:function(inst){var today=new Date();today=this._daylightSavingAdjust(new Date(today.getFullYear(),today.getMonth(),today.getDate()));var isRTL=this._get(inst,"isRTL");var showButtonPanel=this._get(inst,"showButtonPanel");var hideIfNoPrevNext=this._get(inst,"hideIfNoPrevNext");var navigationAsDateFormat=this._get(inst,"navigationAsDateFormat");var numMonths=this._getNumberOfMonths(inst);var showCurrentAtPos=this._get(inst,"showCurrentAtPos");var stepMonths=this._get(inst,"stepMonths");var stepBigMonths=this._get(inst,"stepBigMonths");var isMultiMonth=(numMonths[0]!=1||numMonths[1]!=1);var currentDate=this._daylightSavingAdjust((!inst.currentDay?new Date(9999,9,9):new Date(inst.currentYear,inst.currentMonth,inst.currentDay)));var minDate=this._getMinMaxDate(inst,"min",true);var maxDate=this._getMinMaxDate(inst,"max");var drawMonth=inst.drawMonth-showCurrentAtPos;var drawYear=inst.drawYear;if(drawMonth<0){drawMonth+=12;drawYear--}if(maxDate){var maxDraw=this._daylightSavingAdjust(new Date(maxDate.getFullYear(),maxDate.getMonth()-numMonths[1]+1,maxDate.getDate()));maxDraw=(minDate&&maxDraw<minDate?minDate:maxDraw);while(this._daylightSavingAdjust(new Date(drawYear,drawMonth,1))>maxDraw){drawMonth--;if(drawMonth<0){drawMonth=11;drawYear--}}}inst.drawMonth=drawMonth;inst.drawYear=drawYear;var prevText=this._get(inst,"prevText");prevText=(!navigationAsDateFormat?prevText:this.formatDate(prevText,this._daylightSavingAdjust(new Date(drawYear,drawMonth-stepMonths,1)),this._getFormatConfig(inst)));var prev=(this._canAdjustMonth(inst,-1,drawYear,drawMonth)?'<a class="ui-datepicker-prev ui-corner-all" onclick="DP_jQuery.datepicker._adjustDate(\'#'+inst.id+"', -"+stepMonths+", 'M');\" title=\""+prevText+'"><span class="ui-icon ui-icon-circle-triangle-'+(isRTL?"e":"w")+'">'+prevText+"</span></a>":(hideIfNoPrevNext?"":'<a class="ui-datepicker-prev ui-corner-all ui-state-disabled" title="'+prevText+'"><span class="ui-icon ui-icon-circle-triangle-'+(isRTL?"e":"w")+'">'+prevText+"</span></a>"));var nextText=this._get(inst,"nextText");nextText=(!navigationAsDateFormat?nextText:this.formatDate(nextText,this._daylightSavingAdjust(new Date(drawYear,drawMonth+stepMonths,1)),this._getFormatConfig(inst)));var next=(this._canAdjustMonth(inst,+1,drawYear,drawMonth)?'<a class="ui-datepicker-next ui-corner-all" onclick="DP_jQuery.datepicker._adjustDate(\'#'+inst.id+"', +"+stepMonths+", 'M');\" title=\""+nextText+'"><span class="ui-icon ui-icon-circle-triangle-'+(isRTL?"w":"e")+'">'+nextText+"</span></a>":(hideIfNoPrevNext?"":'<a class="ui-datepicker-next ui-corner-all ui-state-disabled" title="'+nextText+'"><span class="ui-icon ui-icon-circle-triangle-'+(isRTL?"w":"e")+'">'+nextText+"</span></a>"));var currentText=this._get(inst,"currentText");var gotoDate=(this._get(inst,"gotoCurrent")&&inst.currentDay?currentDate:today);currentText=(!navigationAsDateFormat?currentText:this.formatDate(currentText,gotoDate,this._getFormatConfig(inst)));var controls=(!inst.inline?'<button type="button" class="ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all" onclick="DP_jQuery.datepicker._hideDatepicker();">'+this._get(inst,"closeText")+"</button>":"");var buttonPanel=(showButtonPanel)?'<div class="ui-datepicker-buttonpane ui-widget-content">'+(isRTL?controls:"")+(this._isInRange(inst,gotoDate)?'<button type="button" class="ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all" onclick="DP_jQuery.datepicker._gotoToday(\'#'+inst.id+"');\">"+currentText+"</button>":"")+(isRTL?"":controls)+"</div>":"";var firstDay=parseInt(this._get(inst,"firstDay"),10);firstDay=(isNaN(firstDay)?0:firstDay);var dayNames=this._get(inst,"dayNames");var dayNamesShort=this._get(inst,"dayNamesShort");var dayNamesMin=this._get(inst,"dayNamesMin");var monthNames=this._get(inst,"monthNames");var monthNamesShort=this._get(inst,"monthNamesShort");var beforeShowDay=this._get(inst,"beforeShowDay");var showOtherMonths=this._get(inst,"showOtherMonths");var calculateWeek=this._get(inst,"calculateWeek")||this.iso8601Week;var endDate=inst.endDay?this._daylightSavingAdjust(new Date(inst.endYear,inst.endMonth,inst.endDay)):currentDate;var defaultDate=this._getDefaultDate(inst);var html="";for(var row=0;row<numMonths[0];row++){var group="";for(var col=0;col<numMonths[1];col++){var selectedDate=this._daylightSavingAdjust(new Date(drawYear,drawMonth,inst.selectedDay));var cornerClass=" ui-corner-all";var calender="";if(isMultiMonth){calender+='<div class="ui-datepicker-group ui-datepicker-group-';switch(col){case 0:calender+="first";cornerClass=" ui-corner-"+(isRTL?"right":"left");break;case numMonths[1]-1:calender+="last";cornerClass=" ui-corner-"+(isRTL?"left":"right");break;default:calender+="middle";cornerClass="";break}calender+='">'}calender+='<div class="ui-datepicker-header ui-widget-header ui-helper-clearfix'+cornerClass+'">'+(/all|left/.test(cornerClass)&&row==0?(isRTL?next:prev):"")+(/all|right/.test(cornerClass)&&row==0?(isRTL?prev:next):"")+this._generateMonthYearHeader(inst,drawMonth,drawYear,minDate,maxDate,selectedDate,row>0||col>0,monthNames,monthNamesShort)+'</div><table class="ui-datepicker-calendar"><thead><tr>';var thead="";for(var dow=0;dow<7;dow++){var day=(dow+firstDay)%7;thead+="<th"+((dow+firstDay+6)%7>=5?' class="ui-datepicker-week-end"':"")+'><span title="'+dayNames[day]+'">'+dayNamesMin[day]+"</span></th>"}calender+=thead+"</tr></thead><tbody>";var daysInMonth=this._getDaysInMonth(drawYear,drawMonth);if(drawYear==inst.selectedYear&&drawMonth==inst.selectedMonth){inst.selectedDay=Math.min(inst.selectedDay,daysInMonth)}var leadDays=(this._getFirstDayOfMonth(drawYear,drawMonth)-firstDay+7)%7;var numRows=(isMultiMonth?6:Math.ceil((leadDays+daysInMonth)/7));var printDate=this._daylightSavingAdjust(new Date(drawYear,drawMonth,1-leadDays));for(var dRow=0;dRow<numRows;dRow++){calender+="<tr>";var tbody="";for(var dow=0;dow<7;dow++){var daySettings=(beforeShowDay?beforeShowDay.apply((inst.input?inst.input[0]:null),[printDate]):[true,""]);var otherMonth=(printDate.getMonth()!=drawMonth);var unselectable=otherMonth||!daySettings[0]||(minDate&&printDate<minDate)||(maxDate&&printDate>maxDate);tbody+='<td class="'+((dow+firstDay+6)%7>=5?" ui-datepicker-week-end":"")+(otherMonth?" ui-datepicker-other-month":"")+((printDate.getTime()==selectedDate.getTime()&&drawMonth==inst.selectedMonth&&inst._keyEvent)||(defaultDate.getTime()==printDate.getTime()&&defaultDate.getTime()==selectedDate.getTime())?" "+this._dayOverClass:"")+(unselectable?" "+this._unselectableClass+" ui-state-disabled":"")+(otherMonth&&!showOtherMonths?"":" "+daySettings[1]+(printDate.getTime()>=currentDate.getTime()&&printDate.getTime()<=endDate.getTime()?" "+this._currentClass:"")+(printDate.getTime()==today.getTime()?" ui-datepicker-today":""))+'"'+((!otherMonth||showOtherMonths)&&daySettings[2]?' title="'+daySettings[2]+'"':"")+(unselectable?"":" onclick=\"DP_jQuery.datepicker._selectDay('#"+inst.id+"',"+drawMonth+","+drawYear+', this);return false;"')+">"+(otherMonth?(showOtherMonths?printDate.getDate():" "):(unselectable?'<span class="ui-state-default">'+printDate.getDate()+"</span>":'<a class="ui-state-default'+(printDate.getTime()==today.getTime()?" ui-state-highlight":"")+(printDate.getTime()>=currentDate.getTime()&&printDate.getTime()<=endDate.getTime()?" ui-state-active":"")+'" href="#">'+printDate.getDate()+"</a>"))+"</td>";printDate.setDate(printDate.getDate()+1);printDate=this._daylightSavingAdjust(printDate)}calender+=tbody+"</tr>"}drawMonth++;if(drawMonth>11){drawMonth=0;drawYear++}calender+="</tbody></table>"+(isMultiMonth?"</div>"+((numMonths[0]>0&&col==numMonths[1]-1)?'<div class="ui-datepicker-row-break"></div>':""):"");group+=calender}html+=group}html+=buttonPanel+($.browser.msie&&parseInt($.browser.version,10)<7&&!inst.inline?'<iframe src="javascript:false;" class="ui-datepicker-cover" frameborder="0"></iframe>':"");inst._keyEvent=false;return html},_generateMonthYearHeader:function(inst,drawMonth,drawYear,minDate,maxDate,selectedDate,secondary,monthNames,monthNamesShort){minDate=(inst.rangeStart&&minDate&&selectedDate<minDate?selectedDate:minDate);var changeMonth=this._get(inst,"changeMonth");var changeYear=this._get(inst,"changeYear");var showMonthAfterYear=this._get(inst,"showMonthAfterYear");var html='<div class="ui-datepicker-title">';var monthHtml="";if(secondary||!changeMonth){monthHtml+='<span class="ui-datepicker-month">'+monthNames[drawMonth]+"</span> "}else{var inMinYear=(minDate&&minDate.getFullYear()==drawYear);var inMaxYear=(maxDate&&maxDate.getFullYear()==drawYear);monthHtml+='<select class="ui-datepicker-month" onchange="DP_jQuery.datepicker._selectMonthYear(\'#'+inst.id+"', this, 'M');\" onclick=\"DP_jQuery.datepicker._clickMonthYear('#"+inst.id+"');\">";for(var month=0;month<12;month++){if((!inMinYear||month>=minDate.getMonth())&&(!inMaxYear||month<=maxDate.getMonth())){monthHtml+='<option value="'+month+'"'+(month==drawMonth?' selected="selected"':"")+">"+monthNamesShort[month]+"</option>"}}monthHtml+="</select>"}if(!showMonthAfterYear){html+=monthHtml+((secondary||changeMonth||changeYear)&&(!(changeMonth&&changeYear))?" ":"")}if(secondary||!changeYear){html+='<span class="ui-datepicker-year">'+drawYear+"</span>"}else{var years=this._get(inst,"yearRange").split(":");var year=0;var endYear=0;if(years.length!=2){year=drawYear-10;endYear=drawYear+10}else{if(years[0].charAt(0)=="+"||years[0].charAt(0)=="-"){year=drawYear+parseInt(years[0],10);endYear=drawYear+parseInt(years[1],10)}else{year=parseInt(years[0],10);endYear=parseInt(years[1],10)}}year=(minDate?Math.max(year,minDate.getFullYear()):year);endYear=(maxDate?Math.min(endYear,maxDate.getFullYear()):endYear);html+='<select class="ui-datepicker-year" onchange="DP_jQuery.datepicker._selectMonthYear(\'#'+inst.id+"', this, 'Y');\" onclick=\"DP_jQuery.datepicker._clickMonthYear('#"+inst.id+"');\">";for(;year<=endYear;year++){html+='<option value="'+year+'"'+(year==drawYear?' selected="selected"':"")+">"+year+"</option>"}html+="</select>"}if(showMonthAfterYear){html+=(secondary||changeMonth||changeYear?" ":"")+monthHtml}html+="</div>";return html},_adjustInstDate:function(inst,offset,period){var year=inst.drawYear+(period=="Y"?offset:0);var month=inst.drawMonth+(period=="M"?offset:0);var day=Math.min(inst.selectedDay,this._getDaysInMonth(year,month))+(period=="D"?offset:0);var date=this._daylightSavingAdjust(new Date(year,month,day));var minDate=this._getMinMaxDate(inst,"min",true);var maxDate=this._getMinMaxDate(inst,"max");date=(minDate&&date<minDate?minDate:date);date=(maxDate&&date>maxDate?maxDate:date);inst.selectedDay=date.getDate();inst.drawMonth=inst.selectedMonth=date.getMonth();inst.drawYear=inst.selectedYear=date.getFullYear();if(period=="M"||period=="Y"){this._notifyChange(inst)}},_notifyChange:function(inst){var onChange=this._get(inst,"onChangeMonthYear");if(onChange){onChange.apply((inst.input?inst.input[0]:null),[inst.selectedYear,inst.selectedMonth+1,inst])}},_getNumberOfMonths:function(inst){var numMonths=this._get(inst,"numberOfMonths");return(numMonths==null?[1,1]:(typeof numMonths=="number"?[1,numMonths]:numMonths))},_getMinMaxDate:function(inst,minMax,checkRange){var date=this._determineDate(this._get(inst,minMax+"Date"),null);return(!checkRange||!inst.rangeStart?date:(!date||inst.rangeStart>date?inst.rangeStart:date))},_getDaysInMonth:function(year,month){return 32-new Date(year,month,32).getDate()},_getFirstDayOfMonth:function(year,month){return new Date(year,month,1).getDay()},_canAdjustMonth:function(inst,offset,curYear,curMonth){var numMonths=this._getNumberOfMonths(inst);var date=this._daylightSavingAdjust(new Date(curYear,curMonth+(offset<0?offset:numMonths[1]),1));if(offset<0){date.setDate(this._getDaysInMonth(date.getFullYear(),date.getMonth()))}return this._isInRange(inst,date)},_isInRange:function(inst,date){var newMinDate=(!inst.rangeStart?null:this._daylightSavingAdjust(new Date(inst.selectedYear,inst.selectedMonth,inst.selectedDay)));newMinDate=(newMinDate&&inst.rangeStart<newMinDate?inst.rangeStart:newMinDate);var minDate=newMinDate||this._getMinMaxDate(inst,"min");var maxDate=this._getMinMaxDate(inst,"max");return((!minDate||date>=minDate)&&(!maxDate||date<=maxDate))},_getFormatConfig:function(inst){var shortYearCutoff=this._get(inst,"shortYearCutoff");shortYearCutoff=(typeof shortYearCutoff!="string"?shortYearCutoff:new Date().getFullYear()%100+parseInt(shortYearCutoff,10));return{shortYearCutoff:shortYearCutoff,dayNamesShort:this._get(inst,"dayNamesShort"),dayNames:this._get(inst,"dayNames"),monthNamesShort:this._get(inst,"monthNamesShort"),monthNames:this._get(inst,"monthNames")}},_formatDate:function(inst,day,month,year){if(!day){inst.currentDay=inst.selectedDay;inst.currentMonth=inst.selectedMonth;inst.currentYear=inst.selectedYear}var date=(day?(typeof day=="object"?day:this._daylightSavingAdjust(new Date(year,month,day))):this._daylightSavingAdjust(new Date(inst.currentYear,inst.currentMonth,inst.currentDay)));return this.formatDate(this._get(inst,"dateFormat"),date,this._getFormatConfig(inst))}});function extendRemove(target,props){$.extend(target,props);for(var name in props){if(props[name]==null||props[name]==undefined){target[name]=props[name]}}return target}function isArray(a){return(a&&(($.browser.safari&&typeof a=="object"&&a.length)||(a.constructor&&a.constructor.toString().match(/\Array\(\)/))))}$.fn.datepicker=function(options){if(!$.datepicker.initialized){$(document).mousedown($.datepicker._checkExternalClick).find("body").append($.datepicker.dpDiv);$.datepicker.initialized=true}var otherArgs=Array.prototype.slice.call(arguments,1);if(typeof options=="string"&&(options=="isDisabled"||options=="getDate")){return $.datepicker["_"+options+"Datepicker"].apply($.datepicker,[this[0]].concat(otherArgs))}return this.each(function(){typeof options=="string"?$.datepicker["_"+options+"Datepicker"].apply($.datepicker,[this].concat(otherArgs)):$.datepicker._attachDatepicker(this,options)})};$.datepicker=new Datepicker();$.datepicker.initialized=false;$.datepicker.uuid=new Date().getTime();$.datepicker.version="1.7.1";window.DP_jQuery=$})(jQuery);;/*
 + * jQuery UI Progressbar 1.7.1
 + *
 + * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
 + * Dual licensed under the MIT (MIT-LICENSE.txt)
 + * and GPL (GPL-LICENSE.txt) licenses.
 + *
 + * http://docs.jquery.com/UI/Progressbar
 + *
 + * Depends:
 + *   ui.core.js
 + */
(function(a){a.widget("ui.progressbar",{_init:function(){this.element.addClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").attr({role:"progressbar","aria-valuemin":this._valueMin(),"aria-valuemax":this._valueMax(),"aria-valuenow":this._value()});this.valueDiv=a('<div class="ui-progressbar-value ui-widget-header ui-corner-left"></div>').appendTo(this.element);this._refreshValue()},destroy:function(){this.element.removeClass("ui-progressbar ui-widget ui-widget-content ui-corner-all").removeAttr("role").removeAttr("aria-valuemin").removeAttr("aria-valuemax").removeAttr("aria-valuenow").removeData("progressbar").unbind(".progressbar");this.valueDiv.remove();a.widget.prototype.destroy.apply(this,arguments)},value:function(b){arguments.length&&this._setData("value",b);return this._value()},_setData:function(b,c){switch(b){case"value":this.options.value=c;this._refreshValue();this._trigger("change",null,{});break}a.widget.prototype._setData.apply(this,arguments)},_value:function(){var b=this.options.value;if(b<this._valueMin()){b=this._valueMin()}if(b>this._valueMax()){b=this._valueMax()}return b},_valueMin:function(){var b=0;return b},_valueMax:function(){var b=100;return b},_refreshValue:function(){var b=this.value();this.valueDiv[b==this._valueMax()?"addClass":"removeClass"]("ui-corner-right");this.valueDiv.width(b+"%");this.element.attr("aria-valuenow",b)}});a.extend(a.ui.progressbar,{version:"1.7.1",defaults:{value:0}})})(jQuery);;
\ No newline at end of file diff --git a/lib/jsl/jsl b/lib/jsl/jslBinary files differ new file mode 100755 index 00000000..97d17734 --- /dev/null +++ b/lib/jsl/jsl diff --git a/lib/jsl/jsl.default.conf b/lib/jsl/jsl.default.conf new file mode 100755 index 00000000..f494a35c --- /dev/null +++ b/lib/jsl/jsl.default.conf @@ -0,0 +1,128 @@ +# +# Configuration File for JavaScript Lint 0.3.0 +# Developed by Matthias Miller (http://www.JavaScriptLint.com) +# +# This configuration file can be used to lint a collection of scripts, or to enable +# or disable warnings for scripts that are linted via the command line. +# + +### Warnings +# Enable or disable warnings based on requirements. +# Use "+WarningName" to display or "-WarningName" to suppress. +# +-no_return_value              # function {0} does not always return a value ++duplicate_formal             # duplicate formal argument {0} +-equal_as_assign              # test for equality (==) mistyped as assignment (=)?{0} ++var_hides_arg                # variable {0} hides argument ++redeclared_var               # redeclaration of {0} {1} +-anon_no_return_value         # anonymous function does not always return a value ++missing_semicolon            # missing semicolon ++meaningless_block            # meaningless block; curly braces have no impact ++comma_separated_stmts        # multiple statements separated by commas (use semicolons?) ++unreachable_code             # unreachable code +-missing_break                # missing break statement ++missing_break_for_last_case  # missing break statement for last case in switch ++comparison_type_conv         # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) ++inc_dec_within_stmt          # increment (++) and decrement (--) operators used as part of greater statement ++useless_void                 # use of the void type may be unnecessary (void is always undefined) ++multiple_plus_minus          # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs ++use_of_label                 # use of label +-block_without_braces         # block statement without curly braces ++leading_decimal_point        # leading decimal point may indicate a number or an object member ++trailing_decimal_point       # trailing decimal point may indicate a number or an object member ++octal_number                 # leading zeros make an octal number ++nested_comment               # nested comment +-misplaced_regex              # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma ++ambiguous_newline            # unexpected end of line; it is ambiguous whether these lines are part of the same statement ++empty_statement              # empty statement or extra semicolon +-missing_option_explicit      # the "option explicit" control comment is missing ++partial_option_explicit      # the "option explicit" control comment, if used, must be in the first script tag ++dup_option_explicit          # duplicate "option explicit" control comment ++useless_assign               # useless assignment ++ambiguous_nested_stmt        # block statements containing block statements should use curly braces to resolve ambiguity ++ambiguous_else_stmt          # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent) ++missing_default_case         # missing default case in switch statement ++duplicate_case_in_switch     # duplicate case in switch statements ++default_not_at_end           # the default case is not at the end of the switch statement ++legacy_cc_not_understood     # couldn't understand control comment using /*@keyword@*/ syntax ++jsl_cc_not_understood        # couldn't understand control comment using /*jsl:keyword*/ syntax ++useless_comparison           # useless comparison; comparing identical expressions ++with_statement               # with statement hides undeclared variables; use temporary variable instead ++trailing_comma_in_array      # extra comma is not recommended in array initializers ++assign_to_function_call      # assignment to a function call ++parseint_missing_radix       # parseInt missing radix parameter + + +### Output format +# Customize the format of the error message. +#    __FILE__ indicates current file path +#    __FILENAME__ indicates current file name +#    __LINE__ indicates current line +#    __ERROR__ indicates error message +# +# Visual Studio syntax (default): ++output-format __FILE__(__LINE__): __ERROR__ +# Alternative syntax: +#+output-format __FILE__:__LINE__: __ERROR__ + + +### Context +# Show the in-line position of the error. +# Use "+context" to display or "-context" to suppress. +# ++context + + +### Semicolons +# By default, assignments of an anonymous function to a variable or +# property (such as a function prototype) must be followed by a semicolon. +# ++lambda_assign_requires_semicolon + + +### Control Comments +# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for +# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is +# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, +# although legacy control comments are enabled by default for backward compatibility. +# ++legacy_control_comments + + +### JScript Function Extensions +# JScript allows member functions to be defined like this: +#     function MyObj() { /*constructor*/ } +#     function MyObj.prototype.go() { /*member function*/ } +# +# It also allows events to be attached like this: +#     function window::onload() { /*init page*/ } +# +# This is a Microsoft-only JavaScript extension. Enable this setting to allow them. +# +-jscript_function_extensions + + +### Defining identifiers +# By default, "option explicit" is enabled on a per-file basis. +# To enable this for all files, use "+always_use_option_explicit" +-always_use_option_explicit + +# Define certain identifiers of which the lint is not aware. +# (Use this in conjunction with the "undeclared identifier" warning.) +# +# Common uses for webpages might be: +#+define window +#+define document + + +### Files +# Specify which files to lint +# Use "+recurse" to enable recursion (disabled by default). +# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", +# or "+process Folder\Path\*.htm". +# ++process src/*.js ++process src/test/*.js ++process test/*.js ++process test/test/*.js + diff --git a/lib/jstestdriver/JsTestDriver.jar b/lib/jstestdriver/JsTestDriver.jarBinary files differ new file mode 100644 index 00000000..256bfad9 --- /dev/null +++ b/lib/jstestdriver/JsTestDriver.jar diff --git a/lib/shrinksafe/js.jar b/lib/shrinksafe/js.jarBinary files differ new file mode 100644 index 00000000..c081d16b --- /dev/null +++ b/lib/shrinksafe/js.jar diff --git a/lib/shrinksafe/shrinksafe.jar b/lib/shrinksafe/shrinksafe.jarBinary files differ new file mode 100644 index 00000000..eeb7b14e --- /dev/null +++ b/lib/shrinksafe/shrinksafe.jar diff --git a/lib/swfobject/swfobject.js b/lib/swfobject/swfobject.js new file mode 100644 index 00000000..839d82f3 --- /dev/null +++ b/lib/swfobject/swfobject.js @@ -0,0 +1,4 @@ +/*	SWFObject v2.2 beta1 <http://code.google.com/p/swfobject/>  +	is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>  +*/ +var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
\ No newline at end of file diff --git a/lib/underscore/underscore.js b/lib/underscore/underscore.js new file mode 100644 index 00000000..047f01c5 --- /dev/null +++ b/lib/underscore/underscore.js @@ -0,0 +1,609 @@ +// Underscore.js +// (c) 2009 Jeremy Ashkenas, DocumentCloud Inc. +// Underscore is freely distributable under the terms of the MIT license. +// Portions of Underscore are inspired by or borrowed from Prototype.js, +// Oliver Steele's Functional, and John Resig's Micro-Templating. +// For all details and documentation: +// http://documentcloud.github.com/underscore/ + +(function() { + +  /*------------------------- Baseline setup ---------------------------------*/ + +  // Establish the root object, "window" in the browser, or "global" on the server. +  var root = this; + +  // Save the previous value of the "_" variable. +  var previousUnderscore = root._; + +  // If Underscore is called as a function, it returns a wrapped object that +  // can be used OO-style. This wrapper holds altered versions of all the +  // underscore functions. Wrapped objects may be chained. +  var wrapper = function(obj) { this._wrapped = obj; }; + +  // Establish the object that gets thrown to break out of a loop iteration. +  var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__'; + +  // Create a safe reference to the Underscore object for reference below. +  var _ = root._ = function(obj) { return new wrapper(obj); }; + +  // Export the Underscore object for CommonJS. +  if (typeof exports !== 'undefined') exports._ = _; + +  // Create quick reference variables for speed access to core prototypes. +  var slice                 = Array.prototype.slice, +      unshift               = Array.prototype.unshift, +      toString              = Object.prototype.toString, +      hasOwnProperty        = Object.prototype.hasOwnProperty, +      propertyIsEnumerable  = Object.prototype.propertyIsEnumerable; + +  // Current version. +  _.VERSION = '0.5.1'; + +  /*------------------------ Collection Functions: ---------------------------*/ + +  // The cornerstone, an each implementation. +  // Handles objects implementing forEach, arrays, and raw objects. +  _.each = function(obj, iterator, context) { +    var index = 0; +    try { +      if (obj.forEach) { +        obj.forEach(iterator, context); +      } else if (_.isArray(obj) || _.isArguments(obj)) { +        for (var i=0, l=obj.length; i<l; i++) iterator.call(context, obj[i], i, obj); +      } else { +        var keys = _.keys(obj), l = keys.length; +        for (var i=0; i<l; i++) iterator.call(context, obj[keys[i]], keys[i], obj); +      } +    } catch(e) { +      if (e != breaker) throw e; +    } +    return obj; +  }; + +  // Return the results of applying the iterator to each element. Use JavaScript +  // 1.6's version of map, if possible. +  _.map = function(obj, iterator, context) { +    if (obj && _.isFunction(obj.map)) return obj.map(iterator, context); +    var results = []; +    _.each(obj, function(value, index, list) { +      results.push(iterator.call(context, value, index, list)); +    }); +    return results; +  }; + +  // Reduce builds up a single result from a list of values. Also known as +  // inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible. +  _.reduce = function(obj, memo, iterator, context) { +    if (obj && _.isFunction(obj.reduce)) return obj.reduce(_.bind(iterator, context), memo); +    _.each(obj, function(value, index, list) { +      memo = iterator.call(context, memo, value, index, list); +    }); +    return memo; +  }; + +  // The right-associative version of reduce, also known as foldr. Uses +  // JavaScript 1.8's version of reduceRight, if available. +  _.reduceRight = function(obj, memo, iterator, context) { +    if (obj && _.isFunction(obj.reduceRight)) return obj.reduceRight(_.bind(iterator, context), memo); +    var reversed = _.clone(_.toArray(obj)).reverse(); +    _.each(reversed, function(value, index) { +      memo = iterator.call(context, memo, value, index, obj); +    }); +    return memo; +  }; + +  // Return the first value which passes a truth test. +  _.detect = function(obj, iterator, context) { +    var result; +    _.each(obj, function(value, index, list) { +      if (iterator.call(context, value, index, list)) { +        result = value; +        _.breakLoop(); +      } +    }); +    return result; +  }; + +  // Return all the elements that pass a truth test. Use JavaScript 1.6's +  // filter(), if it exists. +  _.select = function(obj, iterator, context) { +    if (obj && _.isFunction(obj.filter)) return obj.filter(iterator, context); +    var results = []; +    _.each(obj, function(value, index, list) { +      iterator.call(context, value, index, list) && results.push(value); +    }); +    return results; +  }; + +  // Return all the elements for which a truth test fails. +  _.reject = function(obj, iterator, context) { +    var results = []; +    _.each(obj, function(value, index, list) { +      !iterator.call(context, value, index, list) && results.push(value); +    }); +    return results; +  }; + +  // Determine whether all of the elements match a truth test. Delegate to +  // JavaScript 1.6's every(), if it is present. +  _.all = function(obj, iterator, context) { +    iterator = iterator || _.identity; +    if (obj && _.isFunction(obj.every)) return obj.every(iterator, context); +    var result = true; +    _.each(obj, function(value, index, list) { +      if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop(); +    }); +    return result; +  }; + +  // Determine if at least one element in the object matches a truth test. Use +  // JavaScript 1.6's some(), if it exists. +  _.any = function(obj, iterator, context) { +    iterator = iterator || _.identity; +    if (obj && _.isFunction(obj.some)) return obj.some(iterator, context); +    var result = false; +    _.each(obj, function(value, index, list) { +      if (result = iterator.call(context, value, index, list)) _.breakLoop(); +    }); +    return result; +  }; + +  // Determine if a given value is included in the array or object, +  // based on '==='. +  _.include = function(obj, target) { +    if (_.isArray(obj)) return _.indexOf(obj, target) != -1; +    var found = false; +    _.each(obj, function(value) { +      if (found = value === target) _.breakLoop(); +    }); +    return found; +  }; + +  // Invoke a method with arguments on every item in a collection. +  _.invoke = function(obj, method) { +    var args = _.rest(arguments, 2); +    return _.map(obj, function(value) { +      return (method ? value[method] : value).apply(value, args); +    }); +  }; + +  // Convenience version of a common use case of map: fetching a property. +  _.pluck = function(obj, key) { +    return _.map(obj, function(value){ return value[key]; }); +  }; + +  // Return the maximum item or (item-based computation). +  _.max = function(obj, iterator, context) { +    if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj); +    var result = {computed : -Infinity}; +    _.each(obj, function(value, index, list) { +      var computed = iterator ? iterator.call(context, value, index, list) : value; +      computed >= result.computed && (result = {value : value, computed : computed}); +    }); +    return result.value; +  }; + +  // Return the minimum element (or element-based computation). +  _.min = function(obj, iterator, context) { +    if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj); +    var result = {computed : Infinity}; +    _.each(obj, function(value, index, list) { +      var computed = iterator ? iterator.call(context, value, index, list) : value; +      computed < result.computed && (result = {value : value, computed : computed}); +    }); +    return result.value; +  }; + +  // Sort the object's values by a criteria produced by an iterator. +  _.sortBy = function(obj, iterator, context) { +    return _.pluck(_.map(obj, function(value, index, list) { +      return { +        value : value, +        criteria : iterator.call(context, value, index, list) +      }; +    }).sort(function(left, right) { +      var a = left.criteria, b = right.criteria; +      return a < b ? -1 : a > b ? 1 : 0; +    }), 'value'); +  }; + +  // Use a comparator function to figure out at what index an object should +  // be inserted so as to maintain order. Uses binary search. +  _.sortedIndex = function(array, obj, iterator) { +    iterator = iterator || _.identity; +    var low = 0, high = array.length; +    while (low < high) { +      var mid = (low + high) >> 1; +      iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid; +    } +    return low; +  }; + +  // Convert anything iterable into a real, live array. +  _.toArray = function(iterable) { +    if (!iterable)                return []; +    if (iterable.toArray)         return iterable.toArray(); +    if (_.isArray(iterable))      return iterable; +    if (_.isArguments(iterable))  return slice.call(iterable); +    return _.map(iterable, function(val){ return val; }); +  }; + +  // Return the number of elements in an object. +  _.size = function(obj) { +    return _.toArray(obj).length; +  }; + +  /*-------------------------- Array Functions: ------------------------------*/ + +  // Get the first element of an array. Passing "n" will return the first N +  // values in the array. Aliased as "head". The "guard" check allows it to work +  // with _.map. +  _.first = function(array, n, guard) { +    return n && !guard ? slice.call(array, 0, n) : array[0]; +  }; + +  // Returns everything but the first entry of the array. Aliased as "tail". +  // Especially useful on the arguments object. Passing an "index" will return +  // the rest of the values in the array from that index onward. The "guard" +   //check allows it to work with _.map. +  _.rest = function(array, index, guard) { +    return slice.call(array, _.isUndefined(index) || guard ? 1 : index); +  }; + +  // Get the last element of an array. +  _.last = function(array) { +    return array[array.length - 1]; +  }; + +  // Trim out all falsy values from an array. +  _.compact = function(array) { +    return _.select(array, function(value){ return !!value; }); +  }; + +  // Return a completely flattened version of an array. +  _.flatten = function(array) { +    return _.reduce(array, [], function(memo, value) { +      if (_.isArray(value)) return memo.concat(_.flatten(value)); +      memo.push(value); +      return memo; +    }); +  }; + +  // Return a version of the array that does not contain the specified value(s). +  _.without = function(array) { +    var values = _.rest(arguments); +    return _.select(array, function(value){ return !_.include(values, value); }); +  }; + +  // Produce a duplicate-free version of the array. If the array has already +  // been sorted, you have the option of using a faster algorithm. +  _.uniq = function(array, isSorted) { +    return _.reduce(array, [], function(memo, el, i) { +      if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el); +      return memo; +    }); +  }; + +  // Produce an array that contains every item shared between all the +  // passed-in arrays. +  _.intersect = function(array) { +    var rest = _.rest(arguments); +    return _.select(_.uniq(array), function(item) { +      return _.all(rest, function(other) { +        return _.indexOf(other, item) >= 0; +      }); +    }); +  }; + +  // Zip together multiple lists into a single array -- elements that share +  // an index go together. +  _.zip = function() { +    var args = _.toArray(arguments); +    var length = _.max(_.pluck(args, 'length')); +    var results = new Array(length); +    for (var i=0; i<length; i++) results[i] = _.pluck(args, String(i)); +    return results; +  }; + +  // If the browser doesn't supply us with indexOf (I'm looking at you, MSIE), +  // we need this function. Return the position of the first occurence of an +  // item in an array, or -1 if the item is not included in the array. +  _.indexOf = function(array, item) { +    if (array.indexOf) return array.indexOf(item); +    for (var i=0, l=array.length; i<l; i++) if (array[i] === item) return i; +    return -1; +  }; + +  // Provide JavaScript 1.6's lastIndexOf, delegating to the native function, +  // if possible. +  _.lastIndexOf = function(array, item) { +    if (array.lastIndexOf) return array.lastIndexOf(item); +    var i = array.length; +    while (i--) if (array[i] === item) return i; +    return -1; +  }; + +  // Generate an integer Array containing an arithmetic progression. A port of +  // the native Python range() function. See: +  // http://docs.python.org/library/functions.html#range +  _.range = function(start, stop, step) { +    var a     = _.toArray(arguments); +    var solo  = a.length <= 1; +    var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1; +    var len   = Math.ceil((stop - start) / step); +    if (len <= 0) return []; +    var range = new Array(len); +    for (var i = start, idx = 0; true; i += step) { +      if ((step > 0 ? i - stop : stop - i) >= 0) return range; +      range[idx++] = i; +    } +  }; + +  /* ----------------------- Function Functions: -----------------------------*/ + +  // Create a function bound to a given object (assigning 'this', and arguments, +  // optionally). Binding with arguments is also known as 'curry'. +  _.bind = function(func, obj) { +    var args = _.rest(arguments, 2); +    return function() { +      return func.apply(obj || root, args.concat(_.toArray(arguments))); +    }; +  }; + +  // Bind all of an object's methods to that object. Useful for ensuring that +  // all callbacks defined on an object belong to it. +  _.bindAll = function(obj) { +    var funcs = _.rest(arguments); +    if (funcs.length == 0) funcs = _.functions(obj); +    _.each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); +    return obj; +  }; + +  // Delays a function for the given number of milliseconds, and then calls +  // it with the arguments supplied. +  _.delay = function(func, wait) { +    var args = _.rest(arguments, 2); +    return setTimeout(function(){ return func.apply(func, args); }, wait); +  }; + +  // Defers a function, scheduling it to run after the current call stack has +  // cleared. +  _.defer = function(func) { +    return _.delay.apply(_, [func, 1].concat(_.rest(arguments))); +  }; + +  // Returns the first function passed as an argument to the second, +  // allowing you to adjust arguments, run code before and after, and +  // conditionally execute the original function. +  _.wrap = function(func, wrapper) { +    return function() { +      var args = [func].concat(_.toArray(arguments)); +      return wrapper.apply(wrapper, args); +    }; +  }; + +  // Returns a function that is the composition of a list of functions, each +  // consuming the return value of the function that follows. +  _.compose = function() { +    var funcs = _.toArray(arguments); +    return function() { +      var args = _.toArray(arguments); +      for (var i=funcs.length-1; i >= 0; i--) { +        args = [funcs[i].apply(this, args)]; +      } +      return args[0]; +    }; +  }; + +  /* ------------------------- Object Functions: ---------------------------- */ + +  // Retrieve the names of an object's properties. +  _.keys = function(obj) { +    if(_.isArray(obj)) return _.range(0, obj.length); +    var keys = []; +    for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key); +    return keys; +  }; + +  // Retrieve the values of an object's properties. +  _.values = function(obj) { +    return _.map(obj, _.identity); +  }; + +  // Return a sorted list of the function names available in Underscore. +  _.functions = function(obj) { +    return _.select(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort(); +  }; + +  // Extend a given object with all of the properties in a source object. +  _.extend = function(destination, source) { +    for (var property in source) destination[property] = source[property]; +    return destination; +  }; + +  // Create a (shallow-cloned) duplicate of an object. +  _.clone = function(obj) { +    if (_.isArray(obj)) return obj.slice(0); +    return _.extend({}, obj); +  }; + +  // Perform a deep comparison to check if two objects are equal. +  _.isEqual = function(a, b) { +    // Check object identity. +    if (a === b) return true; +    // Different types? +    var atype = typeof(a), btype = typeof(b); +    if (atype != btype) return false; +    // Basic equality test (watch out for coercions). +    if (a == b) return true; +    // One is falsy and the other truthy. +    if ((!a && b) || (a && !b)) return false; +    // One of them implements an isEqual()? +    if (a.isEqual) return a.isEqual(b); +    // Check dates' integer values. +    if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime(); +    // Both are NaN? +    if (_.isNaN(a) && _.isNaN(b)) return true; +    // Compare regular expressions. +    if (_.isRegExp(a) && _.isRegExp(b)) +      return a.source     === b.source && +             a.global     === b.global && +             a.ignoreCase === b.ignoreCase && +             a.multiline  === b.multiline; +    // If a is not an object by this point, we can't handle it. +    if (atype !== 'object') return false; +    // Check for different array lengths before comparing contents. +    if (a.length && (a.length !== b.length)) return false; +    // Nothing else worked, deep compare the contents. +    var aKeys = _.keys(a), bKeys = _.keys(b); +    // Different object sizes? +    if (aKeys.length != bKeys.length) return false; +    // Recursive comparison of contents. +    for (var key in a) if (!_.isEqual(a[key], b[key])) return false; +    return true; +  }; + +  // Is a given array or object empty? +  _.isEmpty = function(obj) { +    return _.keys(obj).length == 0; +  }; + +  // Is a given value a DOM element? +  _.isElement = function(obj) { +    return !!(obj && obj.nodeType == 1); +  }; + +  // Is a given variable an arguments object? +  _.isArguments = function(obj) { +    return obj && _.isNumber(obj.length) && !_.isArray(obj) && !propertyIsEnumerable.call(obj, 'length'); +  }; + +  // Is the given value NaN -- this one is interesting. NaN != NaN, and +  // isNaN(undefined) == true, so we make sure it's a number first. +  _.isNaN = function(obj) { +    return _.isNumber(obj) && isNaN(obj); +  }; + +  // Is a given value equal to null? +  _.isNull = function(obj) { +    return obj === null; +  }; + +  // Is a given variable undefined? +  _.isUndefined = function(obj) { +    return typeof obj == 'undefined'; +  }; + +  // Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString +  // functions based on their toString identifiers. +  var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String']; +  for (var i=0, l=types.length; i<l; i++) { +    (function() { +      var identifier = '[object ' + types[i] + ']'; +      _['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; }; +    })(); +  } + +  /* -------------------------- Utility Functions: -------------------------- */ + +  // Run Underscore.js in noConflict mode, returning the '_' variable to its +  // previous owner. Returns a reference to the Underscore object. +  _.noConflict = function() { +    root._ = previousUnderscore; +    return this; +  }; + +  // Keep the identity function around for default iterators. +  _.identity = function(value) { +    return value; +  }; + +  // Break out of the middle of an iteration. +  _.breakLoop = function() { +    throw breaker; +  }; + +  // Generate a unique integer id (unique within the entire client session). +  // Useful for temporary DOM ids. +  var idCounter = 0; +  _.uniqueId = function(prefix) { +    var id = idCounter++; +    return prefix ? prefix + id : id; +  }; + +  // JavaScript templating a-la ERB, pilfered from John Resig's +  // "Secrets of the JavaScript Ninja", page 83. +  _.template = function(str, data) { +    var fn = new Function('obj', +      'var p=[],print=function(){p.push.apply(p,arguments);};' + +      'with(obj){p.push(\'' + +      str +        .replace(/[\r\t\n]/g, " ") +        .split("<%").join("\t") +        .replace(/((^|%>)[^\t]*)'/g, "$1\r") +        .replace(/\t=(.*?)%>/g, "',$1,'") +        .split("\t").join("');") +        .split("%>").join("p.push('") +        .split("\r").join("\\'") +    + "');}return p.join('');"); +    return data ? fn(data) : fn; +  }; + +  /*------------------------------- Aliases ----------------------------------*/ + +  _.forEach  = _.each; +  _.foldl    = _.inject       = _.reduce; +  _.foldr    = _.reduceRight; +  _.filter   = _.select; +  _.every    = _.all; +  _.some     = _.any; +  _.head     = _.first; +  _.tail     = _.rest; +  _.methods  = _.functions; + +  /*------------------------ Setup the OOP Wrapper: --------------------------*/ + +  // Helper function to continue chaining intermediate results. +  var result = function(obj, chain) { +    return chain ? _(obj).chain() : obj; +  }; + +  // Add all of the Underscore functions to the wrapper object. +  _.each(_.functions(_), function(name) { +    var method = _[name]; +    wrapper.prototype[name] = function() { +      unshift.call(arguments, this._wrapped); +      return result(method.apply(_, arguments), this._chain); +    }; +  }); + +  // Add all mutator Array functions to the wrapper. +  _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { +    var method = Array.prototype[name]; +    wrapper.prototype[name] = function() { +      method.apply(this._wrapped, arguments); +      return result(this._wrapped, this._chain); +    }; +  }); + +  // Add all accessor Array functions to the wrapper. +  _.each(['concat', 'join', 'slice'], function(name) { +    var method = Array.prototype[name]; +    wrapper.prototype[name] = function() { +      return result(method.apply(this._wrapped, arguments), this._chain); +    }; +  }); + +  // Start chaining a wrapped Underscore object. +  wrapper.prototype.chain = function() { +    this._chain = true; +    return this; +  }; + +  // Extracts the result from a wrapped and chained object. +  wrapper.prototype.value = function() { +    return this._wrapped; +  }; + +})(); diff --git a/lib/webtoolkit/webtoolkit.base64.js b/lib/webtoolkit/webtoolkit.base64.js new file mode 100644 index 00000000..07db4d5d --- /dev/null +++ b/lib/webtoolkit/webtoolkit.base64.js @@ -0,0 +1,142 @@ +/** +* +*  Base64 encode / decode +*  http://www.webtoolkit.info/ +* +**/ + +var Base64 = { + +	// private property +	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=", + +	// public method for encoding +	encode : function (input) { +		var output = ""; +		var chr1, chr2, chr3, enc1, enc2, enc3, enc4; +		var i = 0; + +		input = Base64._utf8_encode(input); + +		while (i < input.length) { + +			chr1 = input.charCodeAt(i++); +			chr2 = input.charCodeAt(i++); +			chr3 = input.charCodeAt(i++); + +			enc1 = chr1 >> 2; +			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); +			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); +			enc4 = chr3 & 63; + +			if (isNaN(chr2)) { +				enc3 = enc4 = 64; +			} else if (isNaN(chr3)) { +				enc4 = 64; +			} + +			output = output + +			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + +			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + +		} + +		return output; +	}, + +	// public method for decoding +	decode : function (input) { +		var output = ""; +		var chr1, chr2, chr3; +		var enc1, enc2, enc3, enc4; +		var i = 0; + +		input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + +		while (i < input.length) { + +			enc1 = this._keyStr.indexOf(input.charAt(i++)); +			enc2 = this._keyStr.indexOf(input.charAt(i++)); +			enc3 = this._keyStr.indexOf(input.charAt(i++)); +			enc4 = this._keyStr.indexOf(input.charAt(i++)); + +			chr1 = (enc1 << 2) | (enc2 >> 4); +			chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); +			chr3 = ((enc3 & 3) << 6) | enc4; + +			output = output + String.fromCharCode(chr1); + +			if (enc3 != 64) { +				output = output + String.fromCharCode(chr2); +			} +			if (enc4 != 64) { +				output = output + String.fromCharCode(chr3); +			} + +		} + +		output = Base64._utf8_decode(output); + +		return output; + +	}, + +	// private method for UTF-8 encoding +	_utf8_encode : function (string) { +		string = string.replace(/\r\n/g,"\n"); +		var utftext = ""; + +		for (var n = 0; n < string.length; n++) { + +			var c = string.charCodeAt(n); + +			if (c < 128) { +				utftext += String.fromCharCode(c); +			} +			else if((c > 127) && (c < 2048)) { +				utftext += String.fromCharCode((c >> 6) | 192); +				utftext += String.fromCharCode((c & 63) | 128); +			} +			else { +				utftext += String.fromCharCode((c >> 12) | 224); +				utftext += String.fromCharCode(((c >> 6) & 63) | 128); +				utftext += String.fromCharCode((c & 63) | 128); +			} + +		} + +		return utftext; +	}, + +	// private method for UTF-8 decoding +	_utf8_decode : function (utftext) { +		var string = ""; +		var i = 0; +		var c = c1 = c2 = 0; + +		while ( i < utftext.length ) { + +			c = utftext.charCodeAt(i); + +			if (c < 128) { +				string += String.fromCharCode(c); +				i++; +			} +			else if((c > 191) && (c < 224)) { +				c2 = utftext.charCodeAt(i+1); +				string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); +				i += 2; +			} +			else { +				c2 = utftext.charCodeAt(i+1); +				c3 = utftext.charCodeAt(i+2); +				string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); +				i += 3; +			} + +		} + +		return string; +	} + +};
\ No newline at end of file diff --git a/src/API.js b/src/API.js new file mode 100644 index 00000000..c51fe01d --- /dev/null +++ b/src/API.js @@ -0,0 +1,318 @@ +angular.Global = { +  typeOf:function(obj){ +    var type = typeof obj; +    switch(type) { +    case "object": +      if (obj === null) return "null"; +      if (obj instanceof Array) return "array"; +      if (obj instanceof Date) return "date"; +      if (obj.nodeType == 1) return "element"; +    } +    return type; +  } +}; + +angular.Collection = {}; +angular.Object = {}; +angular.Array = { +  includeIf:function(array, value, condition) { +    var index = _.indexOf(array, value); +    if (condition) { +      if (index == -1) +        array.push(value); +    } else { +      array.splice(index, 1); +    } +    return array; +  }, +  sum:function(array, expression) { +    var fn = angular.Function.compile(expression); +    var sum = 0; +    for (var i = 0; i < array.length; i++) { +      var value = 1 * fn(array[i]); +      if (!isNaN(value)){ +        sum += value; +      } +    } +    return sum; +  }, +  remove:function(array, value) { +    var index = _.indexOf(array, value); +    if (index >=0) +      array.splice(index, 1); +    return value; +  }, +  find:function(array, condition, defaultValue) { +    if (!condition) return undefined; +    var fn = angular.Function.compile(condition); +    _.detect(array, function($){ +      if (fn($)){ +        defaultValue = $; +        return true; +      }       +    }); +    return defaultValue; +  }, +  findById:function(array, id) { +    return angular.Array.find(array, function($){return $.$id == id;}, null); +  }, +  filter:function(array, expression) { +    var predicates = []; +    predicates.check = function(value) { +      for (var j = 0; j < predicates.length; j++) { +        if(!predicates[j](value)) { +          return false; +        } +      } +      return true; +    }; +    var getter = nglr.Scope.getter; +    var search = function(obj, text){ +      if (text.charAt(0) === '!') { +        return !search(obj, text.substr(1)); +      } +      switch (typeof obj) { +      case "boolean": +      case "number": +      case "string": +        return ('' + obj).toLowerCase().indexOf(text) > -1; +      case "object": +        for ( var objKey in obj) { +          if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { +            return true; +          } +        } +        return false; +      case "array": +        for ( var i = 0; i < obj.length; i++) { +          if (search(obj[i], text)) { +            return true; +          } +        } +        return false; +      default: +        return false; +      } +    }; +    switch (typeof expression) { +      case "boolean": +      case "number": +      case "string": +        expression = {$:expression}; +      case "object": +        for (var key in expression) { +          if (key == '$') { +            (function(){ +              var text = (''+expression[key]).toLowerCase(); +              if (!text) return; +              predicates.push(function(value) { +                return search(value, text); +              }); +            })(); +          } else { +            (function(){ +              var path = key; +              var text = (''+expression[key]).toLowerCase(); +              if (!text) return; +              predicates.push(function(value) { +                return search(getter(value, path), text); +              }); +            })(); +          } +        } +        break; +      case "function": +        predicates.push(expression); +        break; +      default: +        return array; +    } +    var filtered = []; +    for ( var j = 0; j < array.length; j++) { +      var value = array[j]; +      if (predicates.check(value)) { +        filtered.push(value); +      } +    } +    return filtered; +  }, +  add:function(array, value) { +    array.push(_.isUndefined(value)? {} : value); +    return array; +  }, +  count:function(array, condition) { +    if (!condition) return array.length; +    var fn = angular.Function.compile(condition); +    return _.reduce(array, 0, function(count, $){return count + (fn($)?1:0);}); +  }, +  orderBy:function(array, expression, descend) { +    function reverse(comp, descending) { +      return nglr.toBoolean(descending) ?  +          function(a,b){return comp(b,a);} : comp; +    } +    function compare(v1, v2){ +      var t1 = typeof v1; +      var t2 = typeof v2; +      if (t1 == t2) { +        if (t1 == "string") v1 = v1.toLowerCase(); +        if (t1 == "string") v2 = v2.toLowerCase(); +        if (v1 === v2) return 0; +        return v1 < v2 ? -1 : 1; +      } else { +        return t1 < t2 ? -1 : 1; +      } +    } +    expression = _.isArray(expression) ? expression: [expression]; +    expression = _.map(expression, function($){ +      var descending = false; +      if (typeof $ == "string" && ($.charAt(0) == '+' || $.charAt(0) == '-')) { +        descending = $.charAt(0) == '-'; +        $ = $.substring(1); +      } +      var get = $ ? angular.Function.compile($) : _.identity; +      return reverse(function(a,b){ +        return compare(get(a),get(b)); +      }, descending); +    }); +    var comparator = function(o1, o2){ +      for ( var i = 0; i < expression.length; i++) { +        var comp = expression[i](o1, o2); +        if (comp != 0) return comp; +      } +      return 0; +    }; +    return _.clone(array).sort(reverse(comparator, descend)); +  }, +  orderByToggle:function(predicate, attribute) { +    var STRIP = /^([+|-])?(.*)/; +    var ascending = false; +    var index = -1; +    _.detect(predicate, function($, i){ +      if ($ == attribute) { +        ascending = true; +        index = i; +        return true; +      } +      if (($.charAt(0)=='+'||$.charAt(0)=='-') && $.substring(1) == attribute) { +        ascending = $.charAt(0) == '+'; +        index = i; +        return true; +      }; +    }); +    if (index >= 0) { +      predicate.splice(index, 1); +    } +    predicate.unshift((ascending ? "-" : "+") + attribute); +    return predicate; +  }, +  orderByDirection:function(predicate, attribute, ascend, descend) { +    ascend = ascend || 'ng-ascend'; +    descend = descend || 'ng-descend'; +    var att = predicate[0] || ''; +    var direction = true; +    if (att.charAt(0) == '-') { +      att = att.substring(1); +      direction = false; +    } else if(att.charAt(0) == '+') { +      att = att.substring(1); +    } +    return att == attribute ? (direction ? ascend : descend) : ""; +  }, +  merge:function(array, index, mergeValue) { +    var value = array[index]; +    if (!value) { +      value = {}; +      array[index] = value; +    } +    nglr.merge(mergeValue, value); +    return array; +  } +}; +angular.String = { +  quote:function(string) { +    return '"' + string.replace(/\\/g, '\\\\'). +                        replace(/"/g, '\\"'). +                        replace(/\n/g, '\\n'). +                        replace(/\f/g, '\\f'). +                        replace(/\r/g, '\\r'). +                        replace(/\t/g, '\\t'). +                        replace(/\v/g, '\\v') + +             '"'; +  }, +  quoteUnicode:function(string) { +    var str = angular.String.quote(string); +    var chars = []; +    for ( var i = 0; i < str.length; i++) { +      var ch = str.charCodeAt(i); +      if (ch < 128) { +        chars.push(str.charAt(i)); +      } else { +        var encode = "000" + ch.toString(16); +        chars.push("\\u" + encode.substring(encode.length - 4)); +      } +    } +    return chars.join(''); +  }, +  toDate:function(string){ +    var match; +    if (typeof string == 'string' &&  +        (match = string.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){ +      var date = new Date(0); +      date.setUTCFullYear(match[1], match[2] - 1, match[3]); +      date.setUTCHours(match[4], match[5], match[6], 0); +      return date; +    } +    return string; +  } +}; +angular.Date = { +    toString:function(date){ +      function pad(n) { return n < 10 ? "0" + n : n; } +      return  (date.getUTCFullYear()) + '-' + +        pad(date.getUTCMonth() + 1) + '-' + +        pad(date.getUTCDate()) + 'T' + +        pad(date.getUTCHours()) + ':' + +        pad(date.getUTCMinutes()) + ':' + +        pad(date.getUTCSeconds()) + 'Z'; +    } +  }; +angular.Function = { +  compile:function(expression) { +    if (_.isFunction(expression)){ +      return expression; +    } else if (expression){ +      var scope = new nglr.Scope(); +      return function($) { +        scope.state = $; +        return scope.eval(expression); +      }; +    } else { +      return function($){return $;}; +    } +  } +}; + +(function(){ +  function extend(dst, src, names){ +    _.extend(dst, src); +    _.each((names||[]), function(name){ +      dst[name] = _[name]; +    }); +  }; +  extend(angular.Global, {},  +      ['extend', 'clone','isEqual',  +       'isElement', 'isArray', 'isFunction', 'isUndefined']); +  extend(angular.Collection, angular.Global,  +      ['each', 'map', 'reduce', 'reduceRight', 'detect',  +       'select', 'reject', 'all', 'any', 'include',  +       'invoke', 'pluck', 'max', 'min', 'sortBy',  +       'sortedIndex', 'toArray', 'size']); +  extend(angular.Array, angular.Collection,  +      ['first', 'last', 'compact', 'flatten', 'without',  +       'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']); +  extend(angular.Object, angular.Collection, +      ['keys', 'values']); +  extend(angular.String, angular.Global); +  extend(angular.Function, angular.Global, +      ['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']); +})();
\ No newline at end of file diff --git a/src/Binder.js b/src/Binder.js new file mode 100644 index 00000000..86e99fb8 --- /dev/null +++ b/src/Binder.js @@ -0,0 +1,341 @@ +// Copyright (C) 2009 BRAT Tech LLC +nglr.Binder = function(doc, widgetFactory, urlWatcher, config) { +  this.doc = doc; +  this.urlWatcher = urlWatcher; +  this.anchor = {}; +  this.widgetFactory = widgetFactory; +  this.config = config || {}; +  this.updateListeners = []; +}; + +nglr.Binder.parseBindings = function(string) { +  var results = []; +  var lastIndex = 0; +  var index; +  while((index = string.indexOf('{{', lastIndex)) > -1) { +    if (lastIndex < index) +      results.push(string.substr(lastIndex, index - lastIndex)); +    lastIndex = index; + +    index = string.indexOf('}}', index); +    index = index < 0 ? string.length : index + 2; + +    results.push(string.substr(lastIndex, index - lastIndex)); +    lastIndex = index; +  } +  if (lastIndex != string.length) +    results.push(string.substr(lastIndex, string.length - lastIndex)); +  return results.length === 0 ? [ string ] : results; +}; + +nglr.Binder.hasBinding = function(string) { +  var bindings = nglr.Binder.parseBindings(string); +  return bindings.length > 1 || nglr.Binder.binding(bindings[0]) !== null; +}; + +nglr.Binder.binding = function(string) { +  var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/); +  return binding ? binding[1] : null; +}; + + +nglr.Binder.prototype.parseQueryString = function(query) { +  var params = {}; +  query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, +      function (match, left, right) { +        if (left) params[decodeURIComponent(left)] = decodeURIComponent(right); +      }); +  return params; +}; + +nglr.Binder.prototype.parseAnchor = function(url) { +  var self = this; +  url = url || this.urlWatcher.getUrl(); + +  var anchorIndex = url.indexOf('#'); +  if (anchorIndex < 0) return; +  var anchor = url.substring(anchorIndex + 1); + +  var anchorQuery = this.parseQueryString(anchor); +  jQuery.each(self.anchor, function(key, newValue) { +    delete self.anchor[key]; +  }); +  jQuery.each(anchorQuery, function(key, newValue) { +    self.anchor[key] = newValue; +  }); +}; + +nglr.Binder.prototype.onUrlChange = function (url) { +  console.log("URL change detected", url); +  this.parseAnchor(url); +  this.updateView(); +}; + +nglr.Binder.prototype.updateAnchor = function() { +  var url = this.urlWatcher.getUrl(); +  var anchorIndex = url.indexOf('#'); +  if (anchorIndex > -1) +    url = url.substring(0, anchorIndex); +  url += "#"; +  var sep = ''; +  for (var key in this.anchor) { +    var value = this.anchor[key]; +    if (typeof value === 'undefined' || value === null) { +      delete this.anchor[key]; +    } else { +      url += sep + encodeURIComponent(key); +      if (value !== true) +        url += "=" + encodeURIComponent(value); +      sep = '&'; +    } +  } +  this.urlWatcher.setUrl(url); +  return url; +}; + +nglr.Binder.prototype.updateView = function() { +  var start = new Date().getTime(); +  var scope = jQuery(this.doc).scope(); +  scope.set("$invalidWidgets", []); +  scope.updateView(); +  var end = new Date().getTime(); +  this.updateAnchor(); +  _.each(this.updateListeners, function(fn) {fn();}); +}; + +nglr.Binder.prototype.executeInit = function() { +  jQuery("[ng-init]", this.doc).each(function() { +    var jThis = jQuery(this); +    var scope = jThis.scope(); +    try { +      scope.eval(jThis.attr('ng-init')); +    } catch (e) { +      nglr.alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + nglr.toJson(e, true)); +    } +  }); +}; + +nglr.Binder.prototype.entity = function (scope) { +  jQuery("[ng-entity]", this.doc).attr("ng-watch", function() { +    try { +      var jNode = jQuery(this); +      var decl = scope.entity(jNode.attr("ng-entity")); +      return decl + (jNode.attr('ng-watch') || ""); +    } catch (e) { +      nglr.alert(e); +    } +  }); +}; + +nglr.Binder.prototype.compile = function() { +  var jNode = jQuery(this.doc); +  var self = this; +  if (this.config.autoSubmit) { +    var submits = jQuery(":submit", this.doc).not("[ng-action]"); +    submits.attr("ng-action", "$save()"); +    submits.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr", '{disabled:"{{$invalidWidgets}}"}'); +  } +  this.precompile(this.doc)(this.doc, jNode.scope(), ""); +  jQuery("a[ng-action]", this.doc).live('click', function (event) { +    var jNode = jQuery(this); +    try { +      jNode.scope().eval(jNode.attr('ng-action')); +      jNode.removeAttr('ng-error'); +      jNode.removeClass("ng-exception"); +    } catch (e) { +      jNode.addClass("ng-exception"); +      jNode.attr('ng-error', nglr.toJson(e, true)); +    } +    self.updateView(); +    return false; +  }); +}; + +nglr.Binder.prototype.translateBinding = function(node, parentPath, factories) { +  var path = parentPath.concat(); +  var offset = path.pop(); +  var parts = nglr.Binder.parseBindings(node.nodeValue); +  if (parts.length > 1 || nglr.Binder.binding(parts[0])) { +    var parent = node.parentNode; +    if (nglr.isLeafNode(parent)) { +      parent.setAttribute('ng-bind-template', node.nodeValue); +      factories.push({path:path, fn:function(node, scope, prefix) { +        return new nglr.BindUpdater(node, node.getAttribute('ng-bind-template')); +      }}); +    } else { +      for (var i = 0; i < parts.length; i++) { +        var part = parts[i]; +        var binding = nglr.Binder.binding(part); +        var newNode; +        if (binding) { +          newNode = document.createElement("span"); +          var jNewNode = jQuery(newNode); +          jNewNode.attr("ng-bind", binding); +          if (i === 0) { +            factories.push({path:path.concat(offset + i), fn:nglr.Binder.prototype.ng_bind}); +          } +        } else if (nglr.msie && part.charAt(0) == ' ') { +          newNode = document.createElement("span"); +          newNode.innerHTML = ' ' + part.substring(1); +        } else { +          newNode = document.createTextNode(part); +        } +        parent.insertBefore(newNode, node); +      } +    } +    parent.removeChild(node); +  } +}; + +nglr.Binder.prototype.precompile = function(root) { +  var factories = []; +  this.precompileNode(root, [], factories); +  return function (template, scope, prefix) { +    var len = factories.length; +    for (var i = 0; i < len; i++) { +      var factory = factories[i]; +      var node = template; +      var path = factory.path; +      for (var j = 0; j < path.length; j++) { +        node = node.childNodes[path[j]]; +      } +      try { +        scope.addWidget(factory.fn(node, scope, prefix)); +      } catch (e) { +        nglr.alert(e); +      } +    } +  }; +}; + +nglr.Binder.prototype.precompileNode = function(node, path, factories) { +  var nodeType = node.nodeType; +  if (nodeType == Node.TEXT_NODE) { +    this.translateBinding(node, path, factories); +    return; +  } else if (nodeType != Node.ELEMENT_NODE && nodeType != Node.DOCUMENT_NODE) { +    return; +  } + +  if (!node.getAttribute) return; +  var nonBindable = node.getAttribute('ng-non-bindable'); +  if (nonBindable || nonBindable === "") return; + +  var attributes = node.attributes; +  if (attributes) { +    var bindings = node.getAttribute('ng-bind-attr'); +    node.removeAttribute('ng-bind-attr'); +    bindings = bindings ? nglr.fromJson(bindings) : {}; +    var attrLen = attributes.length; +    for (var i = 0; i < attrLen; i++) { +      var attr = attributes[i]; +      var attrName = attr.name; +      // http://www.glennjones.net/Post/809/getAttributehrefbug.htm +      var attrValue = nglr.msie && attrName == 'href' ? +                      decodeURI(node.getAttribute(attrName, 2)) : attr.value; +      if (nglr.Binder.hasBinding(attrValue)) { +        bindings[attrName] = attrValue; +      } +    } +    var json = nglr.toJson(bindings); +    if (json.length > 2) { +      node.setAttribute("ng-bind-attr", json); +    } +  } + +  if (!node.getAttribute) console.log(node); +  var repeaterExpression = node.getAttribute('ng-repeat'); +  if (repeaterExpression) { +    node.removeAttribute('ng-repeat'); +    var precompiled = this.precompile(node); +    var view = document.createComment("ng-repeat: " + repeaterExpression); +    var parentNode = node.parentNode; +    parentNode.insertBefore(view, node); +    parentNode.removeChild(node); +    var template = function(childScope, prefix, i) { +      var clone = jQuery(node).clone(); +      clone.css('display', ''); +      clone.attr('ng-repeat-index', "" + i); +      clone.data('scope', childScope); +      precompiled(clone[0], childScope, prefix + i + ":"); +      return clone; +    }; +    factories.push({path:path, fn:function(node, scope, prefix) { +      return new nglr.RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix); +    }}); +    return; +  } + +  if (node.getAttribute('ng-eval')) factories.push({path:path, fn:this.ng_eval}); +  if (node.getAttribute('ng-bind')) factories.push({path:path, fn:this.ng_bind}); +  if (node.getAttribute('ng-bind-attr')) factories.push({path:path, fn:this.ng_bind_attr}); +  if (node.getAttribute('ng-hide')) factories.push({path:path, fn:this.ng_hide}); +  if (node.getAttribute('ng-show')) factories.push({path:path, fn:this.ng_show}); +  if (node.getAttribute('ng-class')) factories.push({path:path, fn:this.ng_class}); +  if (node.getAttribute('ng-class-odd')) factories.push({path:path, fn:this.ng_class_odd}); +  if (node.getAttribute('ng-class-even')) factories.push({path:path, fn:this.ng_class_even}); +  if (node.getAttribute('ng-style')) factories.push({path:path, fn:this.ng_style}); +  if (node.getAttribute('ng-watch')) factories.push({path:path, fn:this.ng_watch}); +  var nodeName = node.nodeName; +  if ((nodeName == 'INPUT' ) || +      nodeName == 'TEXTAREA' || +      nodeName == 'SELECT' || +      nodeName == 'BUTTON') { +    var self = this; +    factories.push({path:path, fn:function(node, scope, prefix) { +      node.name = prefix + node.name.split(":").pop(); +      return self.widgetFactory.createController(jQuery(node), scope); +    }}); +  } +  if (nodeName == 'OPTION') { +    var html = jQuery('<select/>').append(jQuery(node).clone()).html(); +    if (!html.match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi)) { +      node.value = node.text; +    } +  } + +  var children = node.childNodes; +  for (var k = 0; k < children.length; k++) { +    this.precompileNode(children[k], path.concat(k), factories); +  } +}; + +nglr.Binder.prototype.ng_eval = function(node) { +  return new nglr.EvalUpdater(node, node.getAttribute('ng-eval')); +}; + +nglr.Binder.prototype.ng_bind = function(node) { +  return new nglr.BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}"); +}; + +nglr.Binder.prototype.ng_bind_attr = function(node) { +  return new nglr.BindAttrUpdater(node, nglr.fromJson(node.getAttribute('ng-bind-attr'))); +}; + +nglr.Binder.prototype.ng_hide = function(node) { +  return new nglr.HideUpdater(node, node.getAttribute('ng-hide')); +}; + +nglr.Binder.prototype.ng_show = function(node) { +  return new nglr.ShowUpdater(node, node.getAttribute('ng-show')); +}; + +nglr.Binder.prototype.ng_class = function(node) { +  return new nglr.ClassUpdater(node, node.getAttribute('ng-class')); +}; + +nglr.Binder.prototype.ng_class_even = function(node) { +  return new nglr.ClassEvenUpdater(node, node.getAttribute('ng-class-even')); +}; + +nglr.Binder.prototype.ng_class_odd = function(node) { +  return new nglr.ClassOddUpdater(node, node.getAttribute('ng-class-odd')); +}; + +nglr.Binder.prototype.ng_style = function(node) { +  return new nglr.StyleUpdater(node, node.getAttribute('ng-style')); +}; + +nglr.Binder.prototype.ng_watch = function(node, scope) { +  scope.watch(node.getAttribute('ng-watch')); +}; diff --git a/src/ControlBar.js b/src/ControlBar.js new file mode 100644 index 00000000..3e1f0b57 --- /dev/null +++ b/src/ControlBar.js @@ -0,0 +1,71 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC + +nglr.ControlBar = function (document, serverUrl) { +  this.document = document; +  this.serverUrl = serverUrl; +  this.window = window; +  this.callbacks = []; +}; + +nglr.ControlBar.prototype.bind = function () { +}; + +nglr.ControlBar.HTML = +  '<div>' + +    '<div class="ui-widget-overlay"></div>' + +    '<div id="ng-login" ng-non-bindable="true">' + +      '<div class="ng-login-container"></div>' + +    '</div>' + +  '</div>'; + +nglr.ControlBar.prototype.login = function (loginSubmitFn) { +  this.callbacks.push(loginSubmitFn); +  if (this.callbacks.length == 1) { +    this.doTemplate("/user_session/new.mini?return_url=" + encodeURIComponent(this.urlWithoutAnchor())); +  } +}; + +nglr.ControlBar.prototype.logout = function (loginSubmitFn) { +  this.callbacks.push(loginSubmitFn); +  if (this.callbacks.length == 1) { +    this.doTemplate("/user_session/do_destroy.mini"); +  } +}; + +nglr.ControlBar.prototype.urlWithoutAnchor = function (path) { +  return this.window.location.href.split("#")[0]; +}; + +nglr.ControlBar.prototype.doTemplate = function (path) { +  var self = this; +  var id = new Date().getTime(); +  var url = this.urlWithoutAnchor(); +  url += "#$iframe_notify=" + id; +  var iframeHeight = 330; +  var loginView = jQuery('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+ url +'" src="'+this.serverUrl + path + '" width="500" height="'+ iframeHeight +'"/></div>'); +  this.document.append(loginView); +  loginView.dialog({ +    height:iframeHeight + 33, width:500, +    resizable: false, modal:true, +    title: 'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>' +  }); +  nglr["_iframe_notify_" + id] = function() { +    loginView.dialog("destroy"); +    loginView.remove(); +    jQuery.each(self.callbacks, function(i, callback){ +      callback(); +    }); +    self.callbacks = []; +  }; +}; + +nglr.ControlBar.FORBIDEN = +  '<div ng-non-bindable="true" title="Permission Error:">' + +    'Sorry, you do not have permission for this!'+ +  '</div>'; + +nglr.ControlBar.prototype.notAuthorized = function () { +  if (this.forbidenView) return; +  this.forbidenView = jQuery(nglr.ControlBar.FORBIDEN); +  this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); +}; diff --git a/src/DataStore.js b/src/DataStore.js new file mode 100644 index 00000000..97ab92ff --- /dev/null +++ b/src/DataStore.js @@ -0,0 +1,332 @@ +// Copyright (C) 2009 BRAT Tech LLC + +nglr.DataStore = function(post, users, anchor) { +  this.post = post; +  this.users = users; +  this._cache = {$collections:[]}; +  this.anchor = anchor; +  this.bulkRequest = []; +}; + +nglr.DataStore.prototype.cache = function(document) { +  if (document.constructor != nglr.Model) { +    throw "Parameter must be an instance of Entity! " + nglr.toJson(document); +  } +  var key = document.$entity + '/' + document.$id; +  var cachedDocument = this._cache[key]; +  if (cachedDocument) { +    nglr.Model.copyDirectFields(document, cachedDocument); +  } else { +    this._cache[key] = document; +    cachedDocument = document; +  } +  return cachedDocument; +}; + +nglr.DataStore.prototype.load = function(instance, id, callback, failure) { +  if (id && id !== '*') { +    var self = this; +    this._jsonRequest(["GET", instance.$entity + "/" + id], function(response) { +      instance.$loadFrom(response); +      instance.$migrate(); +      var clone = instance.$$entity(instance); +      self.cache(clone); +      (callback||nglr.noop)(instance); +    }, failure); +  } +  return instance; +}; + +nglr.DataStore.prototype.loadMany = function(entity, ids, callback) { +  var self=this; +  var list = []; +  var callbackCount = 0; +  jQuery.each(ids, function(i, id){ +    list.push(self.load(entity(), id, function(){ +      callbackCount++; +      if (callbackCount == ids.length) { +        (callback||nglr.noop)(list); +      } +    })); +  }); +  return list; +} + +nglr.DataStore.prototype.loadOrCreate = function(instance, id, callback) { +  var self=this; +  return this.load(instance, id, callback, function(response){ +    if (response.$status_code == 404) { +      instance.$id = id; +      (callback||nglr.noop)(instance); +    } else { +      throw response; +    } +  }); +}; + +nglr.DataStore.prototype.loadAll = function(entity, callback) { +  var self = this; +  var list = []; +  list.$$accept = function(doc){ +    return doc.$entity == entity.title; +  }; +  this._cache.$collections.push(list); +  this._jsonRequest(["GET", entity.title], function(response) { +    var rows = response; +    for ( var i = 0; i < rows.length; i++) { +      var document = entity(); +      document.$loadFrom(rows[i]); +      list.push(self.cache(document)); +    } +    (callback||nglr.noop)(list); +  }); +  return list; +}; + +nglr.DataStore.prototype.save = function(document, callback) { +  var self = this; +  var data = {}; +  document.$saveTo(data); +  this._jsonRequest(["POST", "", data], function(response) { +    document.$loadFrom(response); +    var cachedDoc = self.cache(document); +    _.each(self._cache.$collections, function(collection){ +      if (collection.$$accept(document)) { +        angular.Array.includeIf(collection, cachedDoc, true); +      } +    }); +    if (document.$$anchor) { +      self.anchor[document.$$anchor] = document.$id; +    } +    if (callback) +      callback(document); +  }); +}; + +nglr.DataStore.prototype.remove = function(document, callback) { +  var self = this; +  var data = {}; +  document.$saveTo(data); +  this._jsonRequest(["DELETE", "", data], function(response) { +    delete self._cache[document.$entity + '/' + document.$id]; +    _.each(self._cache.$collections, function(collection){ +      for ( var i = 0; i < collection.length; i++) { +        var item = collection[i]; +        if (item.$id == document.$id) { +          collection.splice(i, 1); +        } +      } +    }); +    (callback||nglr.noop)(response); +  }); +}; + +nglr.DataStore.prototype._jsonRequest = function(request, callback, failure) { +  request.$$callback = callback; +  request.$$failure = failure||function(response){ +    throw response; +  }; +  this.bulkRequest.push(request); +}; + +nglr.DataStore.prototype.flush = function() { +  if (this.bulkRequest.length === 0) return; +  var self = this; +  var bulkRequest = this.bulkRequest; +  this.bulkRequest = []; +  console.log('REQUEST:', bulkRequest); +  function callback(code, bulkResponse){ +    console.log('RESPONSE[' + code + ']: ', bulkResponse); +    if(bulkResponse.$status_code == 401) { +      self.users.login(function(){ +        self.post(bulkRequest, callback); +      }); +    } else if(bulkResponse.$status_code) { +      nglr.alert(nglr.toJson(bulkResponse)); +    } else { +      for ( var i = 0; i < bulkResponse.length; i++) { +        var response = bulkResponse[i]; +        var request = bulkRequest[i]; +        var code = response.$status_code; +        if(code) { +          if(code == 403) { +            self.users.notAuthorized(); +          } else { +            request.$$failure(response); +          } +        } else { +          request.$$callback(response); +        } +      } +    } +  } +  this.post(bulkRequest, callback); +}; + +nglr.DataStore.prototype.saveScope = function(scope, callback) { +  var saveCounter = 1; +  function onSaveDone() { +    saveCounter--; +    if (saveCounter === 0 && callback) +      callback(); +  } +  for(var key in scope) { +    var item = scope[key]; +    if (item && item.$save == nglr.Model.prototype.$save) { +      saveCounter++; +      item.$save(onSaveDone); +    } +  } +  onSaveDone(); +}; + +nglr.DataStore.prototype.query = function(type, query, arg, callback){ +  var self = this; +  var queryList = []; +  queryList.$$accept = function(doc){ +    return false; +  }; +  this._cache.$collections.push(queryList); +  var request = type.title + '/' + query + '=' + arg; +  this._jsonRequest(["GET", request], function(response){ +    var list = response; +    for(var i = 0; i < list.length; i++) { +      var document = new type().$loadFrom(list[i]); +      queryList.push(self.cache(document)); +    } +    if (callback) +      callback(queryList); +  }); +  return queryList; +}; + +nglr.DataStore.prototype.entities = function(callback) { +  var entities = []; +  var self = this; +  this._jsonRequest(["GET", "$entities"], function(response) { +    for (var entityName in response) { +      entities.push(self.entity(entityName)); +    } +    entities.sort(function(a,b){return a.title > b.title ? 1 : -1;}); +    if (callback) callback(entities); +  }); +  return entities; +}; + +nglr.DataStore.prototype.documentCountsByUser = function(){ +  var counts = {}; +  var self = this; +  self.post([["GET", "$users"]], function(code, response){ +    jQuery.each(response[0], function(key, value){ +      counts[key] = value; +    }); +  }); +  return counts; +}; + +nglr.DataStore.prototype.userDocumentIdsByEntity = function(user){ +  var ids = {}; +  var self = this; +  self.post([["GET", "$users/" + user]], function(code, response){ +    jQuery.each(response[0], function(key, value){ +      ids[key] = value; +    }); +  }); +  return ids; +}; + +nglr.DataStore.NullEntity = function(){}; +nglr.DataStore.NullEntity.all = function(){return [];}; +nglr.DataStore.NullEntity.query = function(){return [];}; +nglr.DataStore.NullEntity.load = function(){return {};}; +nglr.DataStore.NullEntity.title = undefined; + +nglr.DataStore.prototype.entity = function(name, defaults){ +  if (!name) { +    return nglr.DataStore.NullEntity; +  } +  var self = this; +  var entity =  function(initialState){ +    return new nglr.Model(entity, initialState); +  }; +  // entity.name does not work as name seems to be reserved for functions +  entity.title = name; +  entity.$$factory = true; +  entity.datastore = this; +  entity.defaults = defaults || {}; +  entity.load = function(id, callback){ +    return self.load(entity(), id, callback); +  }; +  entity.loadMany = function(ids, callback){ +    return self.loadMany(entity, ids, callback); +  }; +  entity.loadOrCreate = function(id, callback){ +    return self.loadOrCreate(entity(), id, callback); +  }; +  entity.all = function(callback){ +    return self.loadAll(entity, callback); +  }; +  entity.query = function(query, queryArgs, callback){ +    return self.query(entity, query, queryArgs, callback); +  }; +  entity.properties = function(callback) { +    self._jsonRequest(["GET", name + "/$properties"], callback); +  }; +  return entity; +}; + +nglr.DataStore.prototype.join = function(join){ +  var fn = function(){ +    throw "Joined entities can not be instantiated into a document."; +  }; +  function base(name){return name ? name.substring(0, name.indexOf('.')) : undefined;} +  function next(name){return name.substring(name.indexOf('.') + 1);} +  var joinOrder = _(join).chain(). +    map(function($, name){ +      return name;}). +    sortBy(function(name){ +      var path = []; +      do { +        if (_(path).include(name)) throw "Infinite loop in join: " + path.join(" -> "); +        path.push(name); +        if (!join[name]) throw _("Named entity '<%=name%>' is undefined.").template({name:name}); +        name = base(join[name].on); +      } while(name); +      return path.length; +    }). +    value(); +  if (_(joinOrder).select(function($){return join[$].on;}).length != joinOrder.length - 1) +    throw "Exactly one entity needs to be primary."; +  fn.query = function(exp, value) { +    var joinedResult = []; +    var baseName = base(exp); +    if (baseName != joinOrder[0]) throw _("Named entity '<%=name%>' is not a primary entity.").template({name:baseName}); +    var Entity = join[baseName].join; +    var joinIndex = 1; +    Entity.query(next(exp), value, function(result){ +      var nextJoinName = joinOrder[joinIndex++]; +      var nextJoin = join[nextJoinName]; +      var nextJoinOn = nextJoin.on; +      var joinIds = {}; +      _(result).each(function(doc){ +        var row = {}; +        joinedResult.push(row); +        row[baseName] = doc; +        var id = nglr.Scope.getter(row, nextJoinOn); +        joinIds[id] = id; +      }); +      nextJoin.join.loadMany(_.toArray(joinIds), function(result){ +        var byId = {}; +        _(result).each(function(doc){ +          byId[doc.$id] = doc; +        }); +        _(joinedResult).each(function(row){ +          var id = nglr.Scope.getter(row, nextJoinOn); +          row[nextJoinName] = byId[id]; +        }); +      }); +    }); +    return joinedResult; +  }; +  return fn; +}; diff --git a/src/Filters.js b/src/Filters.js new file mode 100644 index 00000000..f75f3603 --- /dev/null +++ b/src/Filters.js @@ -0,0 +1,290 @@ +// Copyright (C) 2009 BRAT Tech LLC + +angular.filter.Meta = function(obj){ +  if (obj) { +    for ( var key in obj) { +      this[key] = obj[key]; +    } +  } +}; +angular.filter.Meta.get = function(obj, attr){ +  attr = attr || 'text'; +  switch(typeof obj) { +  case "string": +    return attr == "text" ? obj : undefined; +  case "object": +    if (obj && typeof obj[attr] !== "undefined") { +      return obj[attr]; +    } +    return undefined; +  default: +    return obj; +  } +}; + +angular.filter.currency = function(amount){ +  jQuery(this.element).toggleClass('ng-format-negative', amount < 0); +  return '$' + angular.filter.number.apply(this, [amount, 2]); +}; + +angular.filter.number = function(amount, fractionSize){ +  if (isNaN(amount) || !isFinite(amount)) { +    return ''; +  } +  fractionSize = typeof fractionSize == 'undefined' ? 2 : fractionSize; +  var isNegative = amount < 0; +  amount = Math.abs(amount); +  var pow = Math.pow(10, fractionSize); +  var text = "" + Math.round(amount * pow); +  var whole = text.substring(0, text.length - fractionSize); +  whole = whole || '0'; +  var frc = text.substring(text.length - fractionSize); +  text = isNegative ? '-' : ''; +  for (var i = 0; i < whole.length; i++) { +    if ((whole.length - i)%3 === 0 && i !== 0) { +      text += ','; +    } +    text += whole.charAt(i); +  } +  if (fractionSize > 0) { +    for (var j = frc.length; j < fractionSize; j++) { +      frc += '0'; +    } +    text += '.' + frc.substring(0, fractionSize); +  } +  return text; +}; + +angular.filter.date = function(amount) { +}; + +angular.filter.json = function(object) { +  jQuery(this.element).addClass("ng-monospace"); +  return nglr.toJson(object, true); +}; + +angular.filter.trackPackage = function(trackingNo, noMatch) { +  trackingNo = nglr.trim(trackingNo); +  var tNo = trackingNo.replace(/ /g, ''); +  var MATCHERS = angular.filter.trackPackage.MATCHERS; +  for ( var i = 0; i < MATCHERS.length; i++) { +    var carrier = MATCHERS[i]; +    for ( var j = 0; j < carrier.regexp.length; j++) { +      var regexp = carrier.regexp[j]; +      if (regexp.test(tNo)) { +        var text = carrier.name + ": " + trackingNo; +        var url = carrier.url + trackingNo; +        return new angular.filter.Meta({ +          text:text, +          url:url, +          html: '<a href="' + nglr.escapeAttr(url) + '">' + text + '</a>', +          trackingNo:trackingNo}); +      } +    } +  } +  if (trackingNo) +    return noMatch || +      new angular.filter.Meta({text:trackingNo + " is not recognized"}); +  else +    return null; +}; + +angular.filter.trackPackage.MATCHERS = [ +    { name: "UPS", +      url: "http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=en_US&track.x=0&track.y=0&InquiryNumber1=", +      regexp: [ +        /^1Z[0-9A-Z]{16}$/i]}, +    { name: "FedEx", +      url: "http://www.fedex.com/Tracking?tracknumbers=", +      regexp: [ +        /^96\d{10}?$/i, +        /^96\d{17}?$/i, +        /^96\d{20}?$/i, +        /^\d{15}$/i, +        /^\d{12}$/i]}, +    { name: "USPS", +      url: "http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?origTrackNum=", +      regexp: [ +        /^(91\d{20})$/i, +        /^(91\d{18})$/i]}]; + +angular.filter.link = function(obj, title) { +  var text = title || angular.filter.Meta.get(obj); +  var url = angular.filter.Meta.get(obj, "url") || angular.filter.Meta.get(obj); +  if (url) { +    if (angular.validator.email(url) === null) { +      url = "mailto:" + url; +    } +    var html = '<a href="' + nglr.escapeHtml(url) + '">' + text + '</a>'; +    return new angular.filter.Meta({text:text, url:url, html:html}); +  } +  return obj; +}; + + +angular.filter.bytes = function(size) { +  if(size === null) return ""; + +  var suffix = 0; +  while (size > 1000) { +    size = size / 1024; +    suffix++; +  } +  var txt = "" + size; +  var dot = txt.indexOf('.'); +  if (dot > -1 && dot + 2 < txt.length) { +    txt = txt.substring(0, dot + 2); +  } +  return txt + " " + angular.filter.bytes.SUFFIX[suffix]; +}; +angular.filter.bytes.SUFFIX = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; + +angular.filter.image = function(obj, width, height) { +  if (obj && obj.url) { +    var style = ""; +    if (width) { +      style = ' style="max-width: ' + width + +              'px; max-height: ' + (height || width) + 'px;"'; +    } +    return new angular.filter.Meta({url:obj.url, text:obj.url, +      html:'<img src="'+obj.url+'"' + style + '/>'}); +  } +  return null; +}; + +angular.filter.lowercase = function (obj) { +  var text = angular.filter.Meta.get(obj); +  return text ? ("" + text).toLowerCase() : text; +}; + +angular.filter.uppercase = function (obj) { +  var text = angular.filter.Meta.get(obj); +  return text ? ("" + text).toUpperCase() : text; +}; + +angular.filter.linecount = function (obj) { +  var text = angular.filter.Meta.get(obj); +  if (text==='' || !text) return 1; +  return text.split(/\n|\f/).length; +}; + +angular.filter['if'] = function (result, expression) { +  return expression ? result : undefined; +}; + +angular.filter.unless = function (result, expression) { +  return expression ? undefined : result; +}; + +angular.filter.googleChartApi = function(type, data, width, height) { +  data = data || {}; +  var api = angular.filter.googleChartApi; +  var chart = { +      cht:type,  +      chco:api.collect(data, 'color'), +      chtt:api.title(data), +      chdl:api.collect(data, 'label'), +      chd:api.values(data), +      chf:'bg,s,FFFFFF00' +    }; +  if (_.isArray(data.xLabels)) { +    chart.chxt='x'; +    chart.chxl='0:|' + data.xLabels.join('|'); +  } +  return angular.filter.googleChartApi.encode(chart, width, height); +}; + +angular.filter.googleChartApi.values = function(data){ +  var seriesValues = []; +  _.each(data.series||[], function(serie){ +    var values = []; +    _.each(serie.values||[], function(value){ +      values.push(value); +    }); +    seriesValues.push(values.join(',')); +  }); +  var values = seriesValues.join('|'); +  return values === "" ? null : "t:" + values; +}; + +angular.filter.googleChartApi.title = function(data){ +  var titles = []; +  var title = data.title || []; +  _.each(_.isArray(title)?title:[title], function(text){ +    titles.push(encodeURIComponent(text)); +  }); +  return titles.join('|'); +}; + +angular.filter.googleChartApi.collect = function(data, key){ +  var outterValues = []; +  var count = 0; +  _.each(data.series||[], function(serie){ +    var innerValues = []; +    var value = serie[key] || []; +    _.each(_.isArray(value)?value:[value], function(color){ +        innerValues.push(encodeURIComponent(color)); +        count++; +      }); +    outterValues.push(innerValues.join('|')); +  }); +  return count?outterValues.join(','):null; +}; + +angular.filter.googleChartApi.encode= function(params, width, height) { +  width = width || 200; +  height = height || width; +  var url = "http://chart.apis.google.com/chart?"; +  var urlParam = []; +  params.chs = width + "x" + height; +  for ( var key in params) { +    var value = params[key]; +    if (value) { +      urlParam.push(key + "=" + value); +    } +  } +  urlParam.sort(); +  url += urlParam.join("&"); +  return new angular.filter.Meta({url:url, text:value, +    html:'<img width="' + width + '" height="' + height + '" src="'+url+'"/>'}); +}; + +angular.filter.qrcode = function(value, width, height) { +  return angular.filter.googleChartApi.encode({cht:'qr', chl:encodeURIComponent(value)}, width, height); +}; +angular.filter.chart = { +  pie:function(data, width, height) { +    return angular.filter.googleChartApi('p', data, width, height); +  }, +  pie3d:function(data, width, height) { +    return angular.filter.googleChartApi('p3', data, width, height); +  }, +  pieConcentric:function(data, width, height) { +    return angular.filter.googleChartApi('pc', data, width, height); +  }, +  barHorizontalStacked:function(data, width, height) { +    return angular.filter.googleChartApi('bhs', data, width, height); +  }, +  barHorizontalGrouped:function(data, width, height) { +    return angular.filter.googleChartApi('bhg', data, width, height); +  }, +  barVerticalStacked:function(data, width, height) { +    return angular.filter.googleChartApi('bvs', data, width, height); +  }, +  barVerticalGrouped:function(data, width, height) { +    return angular.filter.googleChartApi('bvg', data, width, height); +  }, +  line:function(data, width, height) { +    return angular.filter.googleChartApi('lc', data, width, height); +  }, +  sparkline:function(data, width, height) { +    return angular.filter.googleChartApi('ls', data, width, height); +  }, +  scatter:function(data, width, height) { +    return angular.filter.googleChartApi('s', data, width, height); +  } +}; + +angular.filter.html = function(html){ +  return new angular.filter.Meta({html:html}); +}; diff --git a/src/JSON.js b/src/JSON.js new file mode 100644 index 00000000..2b6393bf --- /dev/null +++ b/src/JSON.js @@ -0,0 +1,92 @@ +nglr.array = [].constructor; + +nglr.toJson = function(obj, pretty){ +  var buf = []; +  nglr.toJsonArray(buf, obj, pretty ? "\n  " : null); +  return buf.join(''); +}; + +nglr.toPrettyJson = function(obj) { +  return nglr.toJson(obj, true); +}; + +nglr.fromJson = function(json) { +  try { +    var parser = new nglr.Parser(json, true); +    var expression =  parser.primary(); +    parser.assertAllConsumed(); +    return expression(); +  } catch (e) { +    console.error("fromJson error: ", json, e); +    throw e; +  } +}; + + +nglr.toJsonArray = function(buf, obj, pretty){ +  var type = typeof obj; +  if (obj === null) { +    buf.push("null"); +  } else if (type === 'function') { +    return; +  } else if (type === 'boolean') { +    buf.push('' + obj); +  } else if (type === 'number') { +    if (isNaN(obj)) { +      buf.push('null'); +    } else { +      buf.push('' + obj); +    } +  } else if (type === 'string') { +    return buf.push(angular.String.quoteUnicode(obj)); +  } else if (type === 'object') { +    if (obj instanceof Array) { +      buf.push("["); +      var len = obj.length; +      var sep = false; +      for(var i=0; i<len; i++) { +        var item = obj[i]; +        if (sep) buf.push(","); +        if (typeof item == 'function' || typeof item == 'undefined') { +          buf.push("null"); +        } else { +          nglr.toJsonArray(buf, item, pretty); +        } +        sep = true; +      } +      buf.push("]"); +    } else if (obj instanceof Date) { +      buf.push(angular.String.quoteUnicode(angular.Date.toString(obj))); +    } else { +      buf.push("{"); +      if (pretty) buf.push(pretty); +      var comma = false; +      var childPretty = pretty ? pretty + "  " : false; +      var keys = []; +      for(var k in obj) { +        if (k.indexOf('$$') === 0) +          continue; +        keys.push(k); +      } +      keys.sort(); +      for ( var keyIndex = 0; keyIndex < keys.length; keyIndex++) { +        var key = keys[keyIndex]; +        try { +          var value = obj[key]; +          if (typeof value != 'function') { +            if (comma) { +              buf.push(","); +              if (pretty) buf.push(pretty); +            } +            buf.push(angular.String.quote(key)); +            buf.push(":"); +            nglr.toJsonArray(buf, value, childPretty); +            comma = true; +          } +        } catch (e) { +        } +      } +      buf.push("}"); +    } +  } +}; diff --git a/src/Loader.js b/src/Loader.js new file mode 100644 index 00000000..fdcfa3cc --- /dev/null +++ b/src/Loader.js @@ -0,0 +1,389 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC + +// IE compatibility + +if (typeof document.getAttribute == 'undefined') +  document.getAttribute = function() { +  }; +if (typeof Node == 'undefined') { +  Node = { +    ELEMENT_NODE : 1, +    ATTRIBUTE_NODE : 2, +    TEXT_NODE : 3, +    CDATA_SECTION_NODE : 4, +    ENTITY_REFERENCE_NODE : 5, +    ENTITY_NODE : 6, +    PROCESSING_INSTRUCTION_NODE : 7, +    COMMENT_NODE : 8, +    DOCUMENT_NODE : 9, +    DOCUMENT_TYPE_NODE : 10, +    DOCUMENT_FRAGMENT_NODE : 11, +    NOTATION_NODE : 12 +  }; +} + +if (_.isUndefined(window.nglr))       nglr = {}; +if (_.isUndefined(window.angular))    angular = {}; +if (_.isUndefined(angular.validator)) angular.validator = {}; +if (_.isUndefined(angular.filter))    angular.filter = {}; +if (_.isUndefined(window.console)) +  window.console = { +    log:function() {}, +    error:function() {} +  }; +if (_.isUndefined(nglr.alert)) { +  nglr.alert = function(){console.log(arguments); window.alert.apply(window, arguments); }; +} + +nglr.consoleLog = function(level, objs) { +  var log = document.createElement("div"); +  log.className = level; +  var msg = ""; +  var sep = ""; +  for ( var i = 0; i < objs.length; i++) { +    var obj = objs[i]; +    msg += sep + (typeof obj == 'string' ? obj : nglr.toJson(obj)); +    sep = " "; +  } +  log.appendChild(document.createTextNode(msg)); +  nglr.consoleNode.appendChild(log); +}; + +nglr.isNode = function(inp) { +  return inp && +      inp.tagName && +      inp.nodeName && +      inp.ownerDocument && +      inp.removeAttribute; +}; + +nglr.isLeafNode = function(node) { +  switch (node.nodeName) { +  case "OPTION": +  case "PRE": +  case "TITLE": +    return true; +  default: +    return false; +  } +}; + +nglr.noop = function() { +}; +nglr.setHtml = function(node, html) { +  if (nglr.isLeafNode(node)) { +    if (nglr.msie) { +      node.innerText = html; +    } else { +      node.textContent = html; +    } +  } else { +    node.innerHTML = html; +  } +}; + +nglr.escapeHtml = function(html) { +  if (!html || !html.replace) +    return html; +  return html. +      replace(/&/g, '&'). +      replace(/</g, '<'). +      replace(/>/g, '>'); +}; + +nglr.escapeAttr = function(html) { +  if (!html || !html.replace) +    return html; +  return html.replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, +      '"'); +}; + +nglr.bind = function(_this, _function) { +  if (!_this) +    throw "Missing this"; +  if (!_.isFunction(_function)) +    throw "Missing function"; +  return function() { +    return _function.apply(_this, arguments); +  }; +}; + +nglr.shiftBind = function(_this, _function) { +  return function() { +    var args = [ this ]; +    for ( var i = 0; i < arguments.length; i++) { +      args.push(arguments[i]); +    } +    return _function.apply(_this, args); +  }; +}; + +nglr.outerHTML = function(node) { +  var temp = document.createElement('div'); +  temp.appendChild(node); +  var outerHTML = temp.innerHTML; +  temp.removeChild(node); +  return outerHTML; +}; + +nglr.trim = function(str) { +  return str.replace(/^ */, '').replace(/ *$/, ''); +}; + +nglr.toBoolean = function(value) { +  var v = ("" + value).toLowerCase(); +  if (v == 'f' || v == '0' || v == 'false' || v == 'no') +    value = false; +  return !!value; +}; + +nglr.merge = function(src, dst) { +  for ( var key in src) { +    var value = dst[key]; +    var type = typeof value; +    if (type == 'undefined') { +      dst[key] = nglr.fromJson(nglr.toJson(src[key])); +    } else if (type == 'object' && value.constructor != nglr.array && +        key.substring(0, 1) != "$") { +      nglr.merge(src[key], value); +    } +  } +}; + +// //////////////////////////// +// Loader +// //////////////////////////// + +nglr.Loader = function(document, head, config) { +  this.document = jQuery(document); +  this.head = jQuery(head); +  this.config = config; +  this.location = window.location; +}; + +nglr.Loader.prototype.load = function() { +  this.configureLogging(); +  this.loadCss('/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css'); +  this.loadCss('/stylesheets/nglr.css'); +  console.log("Server: " + this.config.server); +  jQuery.noConflict(); +  nglr.msie = jQuery.browser.msie; +  this.configureJQueryPlugins(); +  this.computeConfiguration(); +  this.bindHtml(); +}; + +nglr.Loader.prototype.configureJQueryPlugins = function() { +  console.log('Loader.configureJQueryPlugins()'); +  jQuery.fn.removeNode = function() { +    var node = this.get(0); +    node.parentNode.removeChild(node); +  }; +  jQuery.fn.scope = function() { +    var element = this; +    while (element && element.get(0)) { +      var scope = element.data("scope"); +      if (scope) +        return scope; +      element = element.parent(); +    } +    return null; +  }; +  jQuery.fn.controller = function() { +    return this.data('controller') || nglr.NullController.instance; +  }; +}; + +nglr.Loader.prototype.uid = function() { +  return "" + new Date().getTime(); +}; + +nglr.Loader.prototype.computeConfiguration = function() { +  var config = this.config; +  if (!config.database) { +    var match = config.server.match(/https?:\/\/([\w]*)/) +    config.database = match ? match[1] : "$MEMORY"; +  } +}; + +nglr.Loader.prototype.bindHtml = function() { +  console.log('Loader.bindHtml()'); +  var watcher = new nglr.UrlWatcher(this.location); +  var document = this.document; +  var widgetFactory = new nglr.WidgetFactory(this.config.server, this.config.database); +  var binder = new nglr.Binder(document[0], widgetFactory, watcher, this.config); +  widgetFactory.onChangeListener = nglr.shiftBind(binder, binder.updateModel); +  var controlBar = new nglr.ControlBar(document.find('body'), this.config.server); +  var onUpdate = function(){binder.updateView();}; +  var server = this.config.database=="$MEMORY" ? +      new nglr.FrameServer(this.window) : +      new nglr.Server(this.config.server, jQuery.getScript); +  server = new nglr.VisualServer(server, new nglr.Status(jQuery(document.body)), onUpdate); +  var users = new nglr.Users(server, controlBar); +  var databasePath = '/data/' + this.config.database; +  var post = function(request, callback){ +    server.request("POST", databasePath, request, callback); +  }; +  var datastore = new nglr.DataStore(post, users, binder.anchor); +  binder.updateListeners.push(function(){datastore.flush();}); +  var scope = new nglr.Scope( { +    $anchor : binder.anchor, +    $binder : binder, +    $config : this.config, +    $console : window.console, +    $datastore : datastore, +    $save : function(callback) { +      datastore.saveScope(scope.state, callback, binder.anchor); +    }, +    $window : window, +    $uid : this.uid, +    $users : users +  }, "ROOT"); + +  jQuery.each(["get", "set", "eval", "addWatchListener", "updateView"], +    function(i, method){ +      angular[method] = nglr.bind(scope, scope[method]); +    }); + +  document.data('scope', scope); +  console.log('$binder.entity()'); +  binder.entity(scope); + +  console.log('$binder.compile()'); +  binder.compile(); + +  console.log('ControlBar.bind()'); +  controlBar.bind(); + +  console.log('$users.fetchCurrentUser()'); +  function fetchCurrentUser() { +    users.fetchCurrentUser(function(u) { +      if (!u && document.find("[ng-auth=eager]").length) { +        users.login(); +      } +    }); +  } +  fetchCurrentUser(); + +  console.log('PopUp.bind()'); +  new nglr.PopUp(document).bind(); + +  console.log('$binder.parseAnchor()'); +  binder.parseAnchor(); + +  console.log('$binder.executeInit()'); +  binder.executeInit(); + +  console.log('$binder.updateView()'); +  binder.updateView(); + +  watcher.listener = nglr.bind(binder, binder.onUrlChange, watcher); +  watcher.onUpdate = function(){nglr.alert("update");}; +  watcher.watch(); +  document.find("body").show(); +  console.log('ready()'); + +}; + +nglr.Loader.prototype.visualPost = function(delegate) { +  var status = new nglr.Status(jQuery(document.body)); +  return function(request, delegateCallback) { +    status.beginRequest(request); +    var callback = function() { +      status.endRequest(); +      try { +        delegateCallback.apply(this, arguments); +      } catch (e) { +        nglr.alert(nglr.toJson(e)); +      } +    }; +    delegate(request, callback); +  }; +}; + +nglr.Loader.prototype.configureLogging = function() { +  var url = window.location.href + '#'; +  url = url.split('#')[1]; +  var config = { +    debug : null +  }; +  var configs = url.split('&'); +  for ( var i = 0; i < configs.length; i++) { +    var part = (configs[i] + '=').split('='); +    config[part[0]] = part[1]; +  } +  if (config.debug == 'console') { +    nglr.consoleNode = document.createElement("div"); +    nglr.consoleNode.id = 'ng-console'; +    document.getElementsByTagName('body')[0].appendChild(nglr.consoleNode); +    console.log = function() { +      nglr.consoleLog('ng-console-info', arguments); +    }; +    console.error = function() { +      nglr.consoleLog('ng-console-error', arguments); +    }; +  } +}; + +nglr.Loader.prototype.loadCss = function(css) { +  var cssTag = document.createElement('link'); +  cssTag.rel = "stylesheet"; +  cssTag.type = "text/css"; +  if (!css.match(/^http:/)) +    css = this.config.server + css; +  cssTag.href = css; +  this.head[0].appendChild(cssTag); +}; + +nglr.UrlWatcher = function(location) { +  this.location = location; +  this.delay = 25; +  this.setTimeout = function(fn, delay) { +    window.setTimeout(fn, delay); +  }; +  this.listener = function(url) { +    return url; +  }; +  this.expectedUrl = location.href; +}; + +nglr.UrlWatcher.prototype.watch = function() { +  var self = this; +  var pull = function() { +    if (self.expectedUrl !== self.location.href) { +      var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/); +      if (notify) { +        if (!self.expectedUrl.match(/#/)) { +          self.expectedUrl += "#"; +        } +        self.location.href = self.expectedUrl; +        var id = '_iframe_notify_' + notify[1]; +        var notifyFn = nglr[id]; +        delete nglr[id]; +        try { +          (notifyFn||nglr.noop)(); +        } catch (e) { +          nglr.alert(e); +        } +      } else { +        self.listener(self.location.href); +        self.expectedUrl = self.location.href; +      } +    } +    self.setTimeout(pull, self.delay); +  }; +  pull(); +}; + +nglr.UrlWatcher.prototype.setUrl = function(url) { +  var existingURL = window.location.href; +  if (!existingURL.match(/#/)) +    existingURL += '#'; +  if (existingURL != url) +    window.location.href = url; +  self.existingURL = url; +}; + +nglr.UrlWatcher.prototype.getUrl = function() { +  return window.location.href; +}; diff --git a/src/Model.js b/src/Model.js new file mode 100644 index 00000000..5e48251f --- /dev/null +++ b/src/Model.js @@ -0,0 +1,65 @@ +// Copyright (C) 2009 BRAT Tech LLC + +// Single $ is special and does not get searched +// Double $$ is special an is client only (does not get sent to server) + +nglr.Model = function(entity, initial) { +  this.$$entity = entity; +  this.$loadFrom(initial||{}); +  this.$entity = entity.title; +  this.$migrate(); +}; + +nglr.Model.copyDirectFields = function(src, dst) { +  if (src === dst || !src || !dst) return; +  var isDataField = function(src, dst, field) { +    return (field.substring(0,2) !== '$$') && +        (typeof src[field] !== 'function') && +        (typeof dst[field] !== 'function'); +  }; +  for (var field in dst) { +    if (isDataField(src, dst, field)) +      delete dst[field]; +  } +  for (field in src) { +    if (isDataField(src, dst, field)) +      dst[field] = src[field]; +  } +}; + +nglr.Model.prototype.$migrate = function() { +  nglr.merge(this.$$entity.defaults, this); +  return this; +}; + +nglr.Model.prototype.$merge = function(other) { +  nglr.merge(other, this); +  return this; +}; + +nglr.Model.prototype.$save = function(callback) { +  this.$$entity.datastore.save(this, callback === true ? undefined : callback); +  if (callback === true) this.$$entity.datastore.flush(); +  return this; +}; + +nglr.Model.prototype.$delete = function(callback) { +  this.$$entity.datastore.remove(this, callback === true ? undefined : callback); +  if (callback === true) this.$$entity.datastore.flush(); +  return this; +}; + +nglr.Model.prototype.$loadById = function(id, callback) { +  this.$$entity.datastore.load(this, id, callback); +  return this; +}; + +nglr.Model.prototype.$loadFrom = function(other) { +  nglr.Model.copyDirectFields(other, this); +  return this; +}; + +nglr.Model.prototype.$saveTo = function(other) { +  nglr.Model.copyDirectFields(this, other); +  return this; +}; diff --git a/src/Parser.js b/src/Parser.js new file mode 100644 index 00000000..3d72bebf --- /dev/null +++ b/src/Parser.js @@ -0,0 +1,741 @@ +nglr.Lexer = function(text, parsStrings){ +  this.text = text; +  // UTC dates have 20 characters, we send them through parser +  this.dateParseLength = parsStrings ? 20 : -1; +  this.tokens = []; +  this.index = 0; +}; + +nglr.Lexer.OPERATORS = { +    'null':function(self){return null;}, +    'true':function(self){return true;}, +    'false':function(self){return false;}, +    '+':function(self, a,b){return (a||0)+(b||0);}, +    '-':function(self, a,b){return (a||0)-(b||0);}, +    '*':function(self, a,b){return a*b;}, +    '/':function(self, a,b){return a/b;}, +    '%':function(self, a,b){return a%b;}, +    '^':function(self, a,b){return a^b;}, +    '=':function(self, a,b){return self.scope.set(a, b);}, +    '==':function(self, a,b){return a==b;}, +    '!=':function(self, a,b){return a!=b;}, +    '<':function(self, a,b){return a<b;}, +    '>':function(self, a,b){return a>b;}, +    '<=':function(self, a,b){return a<=b;}, +    '>=':function(self, a,b){return a>=b;}, +    '&&':function(self, a,b){return a&&b;}, +    '||':function(self, a,b){return a||b;}, +    '&':function(self, a,b){return a&b;}, +//    '|':function(self, a,b){return a|b;}, +    '|':function(self, a,b){return b(self, a);}, +    '!':function(self, a){return !a;} +}; + +nglr.Lexer.prototype.peek = function() { +  if (this.index + 1 < this.text.length) { +    return this.text.charAt(this.index + 1); +  } else { +    return false; +  } +}; + +nglr.Lexer.prototype.parse = function() { +  var tokens = this.tokens; +  var OPERATORS = nglr.Lexer.OPERATORS; +  var canStartRegExp = true; +  while (this.index < this.text.length) { +    var ch = this.text.charAt(this.index); +    if (ch == '"' || ch == "'") { +      this.readString(ch); +      canStartRegExp = true; +    } else if (ch == '(' || ch == '[') { +      tokens.push({index:this.index, text:ch}); +      this.index++; +    } else if (ch == '{' ) { +      var peekCh = this.peek(); +      if (peekCh == ':' || peekCh == '(') { +        tokens.push({index:this.index, text:ch + peekCh}); +        this.index++; +      } else { +        tokens.push({index:this.index, text:ch}); +      } +      this.index++; +      canStartRegExp = true; +    } else if (ch == ')' || ch == ']' || ch == '}' ) { +      tokens.push({index:this.index, text:ch}); +      this.index++; +      canStartRegExp = false; +    } else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') { +      tokens.push({index:this.index, text:ch}); +      this.index++; +      canStartRegExp = true; +    } else if ( canStartRegExp && ch == '/' ) { +      this.readRegexp(); +      canStartRegExp = false; +    } else if ( this.isNumber(ch) ) { +      this.readNumber(); +      canStartRegExp = false; +    } else if (this.isIdent(ch)) { +      this.readIdent(); +      canStartRegExp = false; +    } else if (this.isWhitespace(ch)) { +      this.index++; +    } else { +      var ch2 = ch + this.peek(); +      var fn = OPERATORS[ch]; +      var fn2 = OPERATORS[ch2]; +      if (fn2) { +        tokens.push({index:this.index, text:ch2, fn:fn2}); +        this.index += 2; +      } else if (fn) { +        tokens.push({index:this.index, text:ch, fn:fn}); +        this.index += 1; +      } else { +        throw "Lexer Error: Unexpected next character [" + +            this.text.substring(this.index) + +            "] in expression '" + this.text + +            "' at column '" + (this.index+1) + "'."; +      } +      canStartRegExp = true; +    } +  } +  return tokens; +}; + +nglr.Lexer.prototype.isNumber = function(ch) { +  return '0' <= ch && ch <= '9'; +}; + +nglr.Lexer.prototype.isWhitespace = function(ch) { +  return ch == ' ' || ch == '\r' || ch == '\t' || +         ch == '\n' || ch == '\v'; +}; + +nglr.Lexer.prototype.isIdent = function(ch) { +  return 'a' <= ch && ch <= 'z' || +         'A' <= ch && ch <= 'Z' || +         '_' == ch || ch == '$'; +}; + +nglr.Lexer.prototype.readNumber = function() { +  var number = ""; +  var start = this.index; +  while (this.index < this.text.length) { +    var ch = this.text.charAt(this.index); +    if (ch == '.' || this.isNumber(ch)) { +      number += ch; +    } else { +      break; +    } +    this.index++; +  } +  number = 1 * number; +  this.tokens.push({index:start, text:number, +    fn:function(){return number;}}); +}; + +nglr.Lexer.prototype.readIdent = function() { +  var ident = ""; +  var start = this.index; +  while (this.index < this.text.length) { +    var ch = this.text.charAt(this.index); +    if (ch == '.' || this.isIdent(ch) || this.isNumber(ch)) { +      ident += ch; +    } else { +      break; +    } +    this.index++; +  } +  var fn = nglr.Lexer.OPERATORS[ident]; +  if (!fn) { +    fn = function(self){ +      return self.scope.get(ident); +    }; +    fn.isAssignable = ident; +  } +  this.tokens.push({index:start, text:ident, fn:fn}); +}; +nglr.Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; +nglr.Lexer.prototype.readString = function(quote) { +  var start = this.index; +  var dateParseLength = this.dateParseLength; +  this.index++; +  var string = ""; +  var escape = false; +  while (this.index < this.text.length) { +    var ch = this.text.charAt(this.index); +    if (escape) { +      if (ch == 'u') { +        var hex = this.text.substring(this.index + 1, this.index + 5); +        this.index += 4; +        string += String.fromCharCode(parseInt(hex, 16)); +      } else { +        var rep = nglr.Lexer.ESCAPE[ch]; +        if (rep) { +          string += rep; +        } else { +          string += ch; +        } +      } +      escape = false; +    } else if (ch == '\\') { +      escape = true; +    } else if (ch == quote) { +      this.index++; +      this.tokens.push({index:start, text:string, +        fn:function(){ +          return (string.length == dateParseLength) ? +            angular.String.toDate(string) : string; +        }}); +      return; +    } else { +      string += ch; +    } +    this.index++; +  } +  throw "Lexer Error: Unterminated quote [" + +      this.text.substring(start) + "] starting at column '" + +      (start+1) + "' in expression '" + this.text + "'."; +}; + +nglr.Lexer.prototype.readRegexp = function(quote) { +  var start = this.index; +  this.index++; +  var regexp = ""; +  var escape = false; +  while (this.index < this.text.length) { +    var ch = this.text.charAt(this.index); +    if (escape) { +      regexp += ch; +      escape = false; +    } else if (ch === '\\') { +      regexp += ch; +      escape = true; +    } else if (ch === '/') { +      this.index++; +      var flags = ""; +      if (this.isIdent(this.text.charAt(this.index))) { +        this.readIdent(); +        flags = this.tokens.pop().text; +      } +      var compiledRegexp = new RegExp(regexp, flags); +      this.tokens.push({index:start, text:regexp, flags:flags, +        fn:function(){return compiledRegexp;}}); +      return; +    } else { +      regexp += ch; +    } +    this.index++; +  } +  throw "Lexer Error: Unterminated RegExp [" + +      this.text.substring(start) + "] starting at column '" + +      (start+1) + "' in expression '" + this.text + "'."; +}; + + +nglr.Parser = function(text, parseStrings){ +  this.text = text; +  this.tokens = new nglr.Lexer(text, parseStrings).parse(); +  this.index = 0; +}; + +nglr.Parser.ZERO = function(){ +  return 0; +}; + +nglr.Parser.prototype.error = function(msg, token) { +  throw "Token '" + token.text +  +    "' is " + msg + " at column='" +  +    (token.index + 1) + "' of expression '" +  +    this.text + "' starting at '" + this.text.substring(token.index) + "'."; +}; + +nglr.Parser.prototype.peekToken = function() { +  if (this.tokens.length === 0)  +    throw "Unexpected end of expression: " + this.text; +  return this.tokens[0]; +}; + +nglr.Parser.prototype.peek = function(e1, e2, e3, e4) { +  var tokens = this.tokens; +  if (tokens.length > 0) { +    var token = tokens[0]; +    var t = token.text; +    if (t==e1 || t==e2 || t==e3 || t==e4 || +        (!e1 && !e2 && !e3 && !e4)) { +      return token; +    } +  } +  return false; +}; + +nglr.Parser.prototype.expect = function(e1, e2, e3, e4){ +  var token = this.peek(e1, e2, e3, e4); +  if (token) { +    this.tokens.shift(); +    this.currentToken = token; +    return token; +  } +  return false; +}; + +nglr.Parser.prototype.consume = function(e1){ +  if (!this.expect(e1)) { +    var token = this.peek(); +    throw "Expecting '" + e1 + "' at column '" + +        (token.index+1) + "' in '" + +        this.text + "' got '" + +        this.text.substring(token.index) + "'."; +  } +}; + +nglr.Parser.prototype._unary = function(fn, parse) { +  var right = parse.apply(this); +  return function(self) { +    return fn(self, right(self)); +  }; +}; + +nglr.Parser.prototype._binary = function(left, fn, parse) { +  var right = parse.apply(this); +  return function(self) { +    return fn(self, left(self), right(self)); +  }; +}; + +nglr.Parser.prototype.hasTokens = function () { +  return this.tokens.length > 0; +}; + +nglr.Parser.prototype.assertAllConsumed = function(){ +  if (this.tokens.length !== 0) { +    throw "Did not understand '" + this.text.substring(this.tokens[0].index) + +        "' while evaluating '" + this.text + "'."; +  } +}; + +nglr.Parser.prototype.statements = function(){ +  var statements = []; +  while(true) { +    if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']')) +      statements.push(this.filterChain()); +    if (!this.expect(';')) { +      return function (self){ +        var value; +        for ( var i = 0; i < statements.length; i++) { +          var statement = statements[i]; +          if (statement) +            value = statement(self); +        } +        return value; +      }; +    } +  } +}; + +nglr.Parser.prototype.filterChain = function(){ +  var left = this.expression(); +  var token; +  while(true) { +    if ((token = this.expect('|'))) { +      left = this._binary(left, token.fn, this.filter); +    } else { +      return left; +    } +  } +}; + +nglr.Parser.prototype.filter = function(){ +  return this._pipeFunction(angular.filter); +}; + +nglr.Parser.prototype.validator = function(){ +  return this._pipeFunction(angular.validator); +}; + +nglr.Parser.prototype._pipeFunction = function(fnScope){ +  var fn = this.functionIdent(fnScope); +  var argsFn = []; +  var token; +  while(true) { +    if ((token = this.expect(':'))) { +      argsFn.push(this.expression()); +    } else { +      var fnInvoke = function(self, input){ +        var args = [input]; +        for ( var i = 0; i < argsFn.length; i++) { +          args.push(argsFn[i](self)); +        } +        return fn.apply(self, args); +      }; +      return function(){ +        return fnInvoke; +      }; +    } +  } +}; + +nglr.Parser.prototype.expression = function(){ +  return this.throwStmt(); +}; + +nglr.Parser.prototype.throwStmt = function(){ +  if (this.expect('throw')) { +    var throwExp = this.assignment(); +    return function (self) { +      throw throwExp(self); +    }; +  } else { +   return this.assignment(); +  } +}; + +nglr.Parser.prototype.assignment = function(){ +  var left = this.logicalOR(); +  var token; +  if (token = this.expect('=')) { +    if (!left.isAssignable) { +      throw "Left hand side '" + +          this.text.substring(0, token.index) + "' of assignment '" + +          this.text.substring(token.index) + "' is not assignable."; +    } +    var ident = function(){return left.isAssignable;}; +    return this._binary(ident, token.fn, this.logicalOR); +  } else { +   return left; +  } +}; + +nglr.Parser.prototype.logicalOR = function(){ +  var left = this.logicalAND(); +  var token; +  while(true) { +    if ((token = this.expect('||'))) { +      left = this._binary(left, token.fn, this.logicalAND); +    } else { +      return left; +    } +  } +}; + +nglr.Parser.prototype.logicalAND = function(){ +  var left = this.negated(); +  var token; +  while(true) { +    if ((token = this.expect('&&'))) { +      left = this._binary(left, token.fn, this.negated); +    } else { +      return left; +    } +  } +}; + +nglr.Parser.prototype.negated = function(){ +  var token; +  if (token = this.expect('!')) { +    return this._unary(token.fn, this.equality); +  } else { +   return this.equality(); +  } +}; + +nglr.Parser.prototype.equality = function(){ +  var left = this.relational(); +  var token; +  while(true) { +    if ((token = this.expect('==','!='))) { +      left = this._binary(left, token.fn, this.relational); +    } else { +      return left; +    } +  } +}; + +nglr.Parser.prototype.relational = function(){ +  var left = this.additive(); +  var token; +  while(true) { +    if ((token = this.expect('<', '>', '<=', '>='))) { +      left = this._binary(left, token.fn, this.additive); +    } else { +      return left; +    } +  } +}; + +nglr.Parser.prototype.additive = function(){ +  var left = this.multiplicative(); +  var token; +  while(token = this.expect('+','-')) { +    left = this._binary(left, token.fn, this.multiplicative); +  } +  return left; +}; + +nglr.Parser.prototype.multiplicative = function(){ +  var left = this.unary(); +  var token; +  while(token = this.expect('*','/','%')) { +      left = this._binary(left, token.fn, this.unary); +  } +  return left; +}; + +nglr.Parser.prototype.unary = function(){ +  var token; +  if (this.expect('+')) { +    return this.primary(); +  } else if (token = this.expect('-')) { +    return this._binary(nglr.Parser.ZERO, token.fn, this.multiplicative); +  } else { +   return this.primary(); +  } +}; + +nglr.Parser.prototype.functionIdent = function(fnScope) { +  var token = this.expect(); +  var element = token.text.split('.'); +  var instance = fnScope; +  var key; +  for ( var i = 0; i < element.length; i++) { +    key = element[i]; +    if (instance) +      instance = instance[key]; +  } +  if (typeof instance != 'function') { +    throw "Function '" + token.text + "' at column '" + +    (token.index+1)  + "' in '" + this.text + "' is not defined."; +  } +  return instance; +}; + +nglr.Parser.prototype.primary = function() { +  var primary; +  if (this.expect('(')) { +    var expression = this.filterChain(); +    this.consume(')'); +    primary = expression; +  } else if (this.expect('[')) { +    primary = this.arrayDeclaration(); +  } else if (this.expect('{')) { +    primary = this.object(); +  } else if (this.expect('{:')) { +    primary = this.closure(false); +  } else if (this.expect('{(')) { +    primary = this.closure(true); +  } else { +    var token = this.expect(); +    primary = token.fn; +    if (!primary) { +      this.error("not a primary expression", token); +    } +  } +  var next; +  while (next = this.expect('(', '[', '.')) { +    if (next.text === '(') { +      primary = this.functionCall(primary); +    } else if (next.text === '[') { +      primary = this.objectIndex(primary); +    } else if (next.text === '.') { +      primary = this.fieldAccess(primary); +    } else { +      throw "IMPOSSIBLE"; +    } +  } +  return primary; +}; + +nglr.Parser.prototype.closure = function(hasArgs) { +  var args = []; +  if (hasArgs) { +    if (!this.expect(')')) { +      args.push(this.expect().text); +      while(this.expect(',')) { +        args.push(this.expect().text); +      } +      this.consume(')'); +    } +    this.consume(":"); +  } +  var statements = this.statements(); +  this.consume("}"); +  return function(self){ +    return function($){ +      var scope = new nglr.Scope(self.scope.state); +      scope.set('$', $); +      for ( var i = 0; i < args.length; i++) { +        scope.set(args[i], arguments[i]); +      } +      return statements({scope:scope}); +    }; +  }; +}; + +nglr.Parser.prototype.fieldAccess = function(object) { +  var field = this.expect().text; +  var fn = function (self){ +    return nglr.Scope.getter(object(self), field); +  }; +  fn.isAssignable = field; +  return fn; +}; + +nglr.Parser.prototype.objectIndex = function(obj) { +  var indexFn = this.expression(); +  this.consume(']'); +  if (this.expect('=')) { +    var rhs = this.expression(); +    return function (self){ +      return obj(self)[indexFn(self)] = rhs(self); +    }; +  } else { +    return function (self){ +      var o = obj(self); +      var i = indexFn(self); +      return (o) ? o[i] : undefined; +    }; +  } +}; + +nglr.Parser.prototype.functionCall = function(fn) { +  var argsFn = []; +  if (this.peekToken().text != ')') { +    do { +      argsFn.push(this.expression()); +    } while (this.expect(',')); +  } +  this.consume(')'); +  return function (self){ +    var args = []; +    for ( var i = 0; i < argsFn.length; i++) { +      args.push(argsFn[i](self)); +    } +    var fnPtr = fn(self); +    if (typeof fnPtr === 'function') { +      return fnPtr.apply(self, args); +    } else { +      throw "Expression '" + fn.isAssignable + "' is not a function."; +    } +  }; +}; + +// This is used with json array declaration +nglr.Parser.prototype.arrayDeclaration = function () { +  var elementFns = []; +  if (this.peekToken().text != ']') { +    do { +      elementFns.push(this.expression()); +    } while (this.expect(',')); +  } +  this.consume(']'); +  return function (self){ +    var array = []; +    for ( var i = 0; i < elementFns.length; i++) { +      array.push(elementFns[i](self)); +    } +    return array; +  }; +}; + +nglr.Parser.prototype.object = function () { +  var keyValues = []; +  if (this.peekToken().text != '}') { +    do { +      var key = this.expect().text; +      this.consume(":"); +      var value = this.expression(); +      keyValues.push({key:key, value:value}); +    } while (this.expect(',')); +  } +  this.consume('}'); +  return function (self){ +    var object = {}; +    for ( var i = 0; i < keyValues.length; i++) { +      var keyValue = keyValues[i]; +      var value = keyValue.value(self); +      object[keyValue.key] = value; +    } +    return object; +  }; +}; + +nglr.Parser.prototype.entityDeclaration = function () { +  var decl = []; +  while(this.hasTokens()) { +    decl.push(this.entityDecl()); +    if (!this.expect(';')) { +      this.assertAllConsumed(); +    } +  } +  return function (self){ +    var code = ""; +    for ( var i = 0; i < decl.length; i++) { +      code += decl[i](self); +    } +    return code; +  }; +}; + +nglr.Parser.prototype.entityDecl = function () { +  var entity = this.expect().text; +  var instance; +  var defaults; +  if (this.expect('=')) { +    instance = entity; +    entity = this.expect().text; +  } +  if (this.expect(':')) { +    defaults = this.primary()(null); +  } +  return function(self) { +    var datastore = self.scope.get('$datastore'); +    var Entity = datastore.entity(entity, defaults); +    self.scope.set(entity, Entity); +    if (instance) { +      var document = Entity(); +      document.$$anchor = instance; +      self.scope.set(instance, document); +      return "$anchor." + instance + ":{" +  +          instance + "=" + entity + ".load($anchor." + instance + ");" + +          instance + ".$$anchor=" + angular.String.quote(instance) + ";" +  +        "};"; +    } else { +      return ""; +    } +  }; +}; + +nglr.Parser.prototype.watch = function () { +  var decl = []; +  while(this.hasTokens()) { +    decl.push(this.watchDecl()); +    if (!this.expect(';')) { +      this.assertAllConsumed(); +    } +  } +  this.assertAllConsumed(); +  return function (self){ +    for ( var i = 0; i < decl.length; i++) { +      var d = decl[i](self); +      self.addListener(d.name, d.fn); +    } +  }; +}; + +nglr.Parser.prototype.watchDecl = function () { +  var anchorName = this.expect().text; +  this.consume(":"); +  var expression; +  if (this.peekToken().text == '{') { +    this.consume("{"); +    expression = this.statements(); +    this.consume("}"); +  } else { +    expression = this.expression(); +  } +  return function(self) { +    return {name:anchorName, fn:expression}; +  }; +}; + + diff --git a/src/Scope.js b/src/Scope.js new file mode 100644 index 00000000..45dd15a4 --- /dev/null +++ b/src/Scope.js @@ -0,0 +1,198 @@ +// Copyright (C) 2009 BRAT Tech LLC + +nglr.Scope = function(initialState, name) { +  this.widgets = []; +  this.watchListeners = {}; +  this.name = name; +  initialState = initialState || {}; +  var State = function(){}; +  State.prototype = initialState; +  this.state = new State(); +  this.state.$parent = initialState; +  if (name == "ROOT") { +    this.state.$root = this.state; +  } +}; + +nglr.Scope.expressionCache = {}; + +nglr.Scope.prototype.updateView = function() { +  var self = this; +  this.fireWatchers(); +  _.each(this.widgets, function(widget){ +    self.evalWidget(widget, "", {}, function(){ +      this.updateView(self); +    }); +  }); +}; + +nglr.Scope.prototype.addWidget = function(controller) { +  if (controller) this.widgets.push(controller); +}; + +nglr.Scope.prototype.isProperty = function(exp) { +  for ( var i = 0; i < exp.length; i++) { +    var ch = exp.charAt(i); +    if (ch!='.'  && !nglr.Lexer.prototype.isIdent(ch)) { +      return false; +    } +  } +  return true; +}; + +nglr.Scope.getter = function(instance, path) { +  if (!path) return instance; +  var element = path.split('.'); +  var key; +  var lastInstance = instance; +  var len = element.length; +  for ( var i = 0; i < len; i++) { +    key = element[i]; +    if (!key.match(/^[\$\w][\$\w\d]*$/)) +        throw "Expression '" + path + "' is not a valid expression for accesing variables."; +    if (instance) { +      lastInstance = instance; +      instance = instance[key]; +    } +    if (_.isUndefined(instance)  && key.charAt(0) == '$') { +      var type = angular.Global.typeOf(lastInstance); +      type = angular[type.charAt(0).toUpperCase()+type.substring(1)]; +      var fn = type ? type[[key.substring(1)]] : undefined; +      if (fn) { +        instance = _.bind(fn, lastInstance, lastInstance); +        return instance; +      } +    } +  } +  if (typeof instance === 'function' && !instance.$$factory) { +    return nglr.bind(lastInstance, instance); +  } +  return instance; +}; + +nglr.Scope.prototype.get = function(path) { +  return nglr.Scope.getter(this.state, path); +}; + +nglr.Scope.prototype.set = function(path, value) { +  var element = path.split('.'); +  var instance = this.state; +  for ( var i = 0; element.length > 1; i++) { +    var key = element.shift(); +    var newInstance = instance[key]; +    if (!newInstance) { +      newInstance = {}; +      instance[key] = newInstance; +    } +    instance = newInstance; +  } +  instance[element.shift()] = value; +  return value; +}; + +nglr.Scope.prototype.setEval = function(expressionText, value) { +  this.eval(expressionText + "=" + nglr.toJson(value)); +}; + +nglr.Scope.prototype.eval = function(expressionText, context) { +  var expression = nglr.Scope.expressionCache[expressionText]; +  if (!expression) { +    var parser = new nglr.Parser(expressionText); +    expression = parser.statements(); +    parser.assertAllConsumed(); +    nglr.Scope.expressionCache[expressionText] = expression; +  } +  context = context || {}; +  context.scope = this; +  return expression(context); +}; + +//TODO: Refactor. This function needs to be an execution closure for widgets +// move to widgets +// remove expression, just have inner closure. +nglr.Scope.prototype.evalWidget = function(widget, expression, context, onSuccess, onFailure) { +  try { +    var value = this.eval(expression, context); +    if (widget.hasError) { +      widget.hasError = false; +      jQuery(widget.view). +        removeClass('ng-exception'). +        removeAttr('ng-error'); +    } +    if (onSuccess) { +      value = onSuccess.apply(widget, [value]); +    } +    return true; +  } catch (e){ +    console.error('Eval Widget Error:', e); +    var jsonError = nglr.toJson(e, true); +    widget.hasError = true; +    jQuery(widget.view). +      addClass('ng-exception'). +      attr('ng-error', jsonError); +    if (onFailure) { +      onFailure.apply(widget, [e, jsonError]); +    } +    return false; +  } +}; + +nglr.Scope.prototype.validate = function(expressionText, value) { +  var expression = nglr.Scope.expressionCache[expressionText]; +  if (!expression) { +    expression = new nglr.Parser(expressionText).validator(); +    nglr.Scope.expressionCache[expressionText] = expression; +  } +  var self = {scope:this}; +  return expression(self)(self, value); +}; + +nglr.Scope.prototype.entity = function(entityDeclaration) { +  var expression = new nglr.Parser(entityDeclaration).entityDeclaration(); +  return expression({scope:this}); +}; + +nglr.Scope.prototype.markInvalid = function(widget) { +  this.state.$invalidWidgets.push(widget); +}; + +nglr.Scope.prototype.watch = function(declaration) { +  var self = this; +  new nglr.Parser(declaration).watch()({ +    scope:this, +    addListener:function(watch, exp){ +      self.addWatchListener(watch, function(n,o){ +        try { +          return exp({scope:self}, n, o); +        } catch(e) { +          nglr.alert(e); +        } +      }); +    } +  }); +}; + +nglr.Scope.prototype.addWatchListener = function(watchExpression, listener) { +  var watcher = this.watchListeners[watchExpression]; +  if (!watcher) { +    watcher = {listeners:[], expression:watchExpression}; +    this.watchListeners[watchExpression] = watcher; +  } +  watcher.listeners.push(listener); +}; + +nglr.Scope.prototype.fireWatchers = function() { +  var self = this; +  var fired = false; +  jQuery.each(this.watchListeners, function(name, watcher) { +    var value = self.eval(watcher.expression); +    if (value !== watcher.lastValue) { +      jQuery.each(watcher.listeners, function(i, listener){ +        listener(value, watcher.lastValue); +        fired = true; +      }); +      watcher.lastValue = value; +    } +  }); +  return fired; +}; diff --git a/src/Server.js b/src/Server.js new file mode 100644 index 00000000..94b0cc10 --- /dev/null +++ b/src/Server.js @@ -0,0 +1,69 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC + +nglr.Server = function(url, getScript) { +  this.url = url; +  this.nextId = 0; +  this.getScript = getScript; +  this.uuid = "_" + ("" + Math.random()).substr(2) + "_"; +  this.maxSize = 1800; +}; + +nglr.Server.prototype.base64url = function(txt) { +  return Base64.encode(txt); +}; + +nglr.Server.prototype.request = function(method, url, request, callback) { +  var requestId = this.uuid + (this.nextId++); +  nglr[requestId] = function(response) { +    delete nglr[requestId]; +    callback(200, response); +  }; +  var payload = {u:url, m:method, p:request}; +  payload = this.base64url(nglr.toJson(payload)); +  var totalPockets = Math.ceil(payload.length / this.maxSize); +  var baseUrl = this.url + "/$/" + requestId +  "/" + totalPockets + "/"; +  for ( var pocketNo = 0; pocketNo < totalPockets; pocketNo++) { +    var pocket = payload.substr(pocketNo * this.maxSize, this.maxSize); +    this.getScript(baseUrl + (pocketNo+1) + "?h=" + pocket, nglr.noop); +  } +}; + +nglr.FrameServer = function(frame) { +  this.frame = frame; +}; +nglr.FrameServer.PREFIX = "$DATASET:"; + +nglr.FrameServer.prototype = { +  read:function(){ +    this.data = nglr.fromJson(this.frame.name.substr(nglr.FrameServer.PREFIX.length)); +  }, +  write:function(){ +    this.frame.name = nglr.FrameServer.PREFIX +  nglr.toJson(this.data); +  },  +  request: function(method, url, request, callback) { +    //alert(method + " " + url + " " + nglr.toJson(request) + " " + nglr.toJson(callback)); +  } +}; + + +nglr.VisualServer = function(delegate, status, update) { +  this.delegate = delegate; +  this.update = update; +  this.status = status; +}; + +nglr.VisualServer.prototype = { +  request:function(method, url, request, callback) { +    var self = this; +    this.status.beginRequest(request); +    this.delegate.request(method, url, request, function() { +      self.status.endRequest(); +      try { +        callback.apply(this, arguments); +      } catch (e) { +        nglr.alert(nglr.toJson(e)); +      } +      self.update(); +    }); +  } +}; diff --git a/src/Users.js b/src/Users.js new file mode 100644 index 00000000..c0c15848 --- /dev/null +++ b/src/Users.js @@ -0,0 +1,36 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC +nglr.Users = function(server, controlBar) { +  this.server = server; +  this.controlBar = controlBar; +}; + +nglr.Users.prototype = { +  fetchCurrentUser:function(callback) { +    var self = this; +    this.server.request("GET", "/account.json", {}, function(code, response){ +      self.current = response.user; +      callback(response.user); +    }); +  }, +   +  logout: function(callback) { +    var self = this; +    this.controlBar.logout(function(){ +      delete self.current; +      (callback||nglr.noop)(); +    }); +  }, +   +  login: function(callback) { +    var self = this; +    this.controlBar.login(function(){ +      self.fetchCurrentUser(function(){ +        (callback||nglr.noop)(); +      }); +    }); +  }, + +  notAuthorized: function(){ +    this.controlBar.notAuthorized(); +  } +}; diff --git a/src/Validators.js b/src/Validators.js new file mode 100644 index 00000000..94cb1d52 --- /dev/null +++ b/src/Validators.js @@ -0,0 +1,80 @@ +// Copyright (C) 2009 BRAT Tech LLC + +angular.validator.regexp = function(value, regexp, msg) { +  if (!value.match(regexp)) { +    return msg || +      "Value does not match expected format " + regexp + "."; +  } else { +    return null; +  } +}; + +angular.validator.number = function(value, min, max) { +  var num = 1 * value; +  if (num == value) { +    if (typeof min != 'undefined' && num < min) { +      return "Value can not be less than " + min + "."; +    } +    if (typeof min != 'undefined' && num > max) { +      return "Value can not be greater than " + max + "."; +    } +    return null; +  } else { +    return "Value is not a number."; +  } +}; + +angular.validator.integer = function(value, min, max) { +  var number = angular.validator.number(value, min, max); +  if (number === null && value != Math.round(value)) { +    return "Value is not a whole number."; +  } +  return number; +}; + +angular.validator.date = function(value, min, max) { +  if (value.match(/^\d\d?\/\d\d?\/\d\d\d\d$/)) { +    return null; +  } +  return "Value is not a date. (Expecting format: 12/31/2009)."; +}; + +angular.validator.ssn = function(value) { +  if (value.match(/^\d\d\d-\d\d-\d\d\d\d$/)) { +    return null; +  } +  return "SSN needs to be in 999-99-9999 format."; +}; + +angular.validator.email = function(value) { +  if (value.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/)) { +    return null; +  } +  return "Email needs to be in username@host.com format."; +}; + +angular.validator.phone = function(value) { +  if (value.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/)) { +    return null; +  } +  if (value.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/)) { +    return null; +  } +  return "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."; +}; + +angular.validator.url = function(value) { +  if (value.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/)) { +    return null; +  } +  return "URL needs to be in http://server[:port]/path format."; +}; + +angular.validator.json = function(value) { +  try { +    nglr.fromJson(value); +    return null; +  } catch (e) { +    return e.toString(); +  } +}; diff --git a/src/Widgets.js b/src/Widgets.js new file mode 100644 index 00000000..de74533a --- /dev/null +++ b/src/Widgets.js @@ -0,0 +1,774 @@ +// Copyright (C) 2009 BRAT Tech LLC + + +nglr.WidgetFactory = function(serverUrl, database) { +  this.nextUploadId = 0; +  this.serverUrl = serverUrl; +  this.database = database; +  this.createSWF = swfobject.createSWF; +  this.onChangeListener = function(){}; +}; + +nglr.WidgetFactory.prototype.createController = function(input, scope) { +  var controller; +  var type = input.attr('type').toLowerCase(); +  var exp = input.attr('name'); +  if (exp) exp = exp.split(':').pop(); +  var event = "change"; +  var bubbleEvent = true; +  if (type == 'button' || type == 'submit' || type == 'reset' || type == 'image') { +    controller = new nglr.ButtonController(input[0], exp); +    event = "click"; +    bubbleEvent = false; +  } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { +    controller = new nglr.TextController(input[0], exp); +    event = "keyup change"; +  } else if (type == 'checkbox') { +    controller = new nglr.CheckboxController(input[0], exp); +    event = "click"; +  } else if (type == 'radio') { +    controller = new nglr.RadioController(input[0], exp); +    event="click"; +  } else if (type == 'select-one') { +    controller = new nglr.SelectController(input[0], exp); +  } else if (type == 'select-multiple') { +    controller = new nglr.MultiSelectController(input[0], exp); +  } else if (type == 'file') { +    controller = this.createFileController(input, exp); +  } else { +    throw 'Unknown type: ' + type; +  } +  input.data('controller', controller); +  var binder = scope.get('$binder'); +  var action = function() { +    if (controller.updateModel(scope)) { +      var action = jQuery(controller.view).attr('ng-action') || ""; +      if (scope.evalWidget(controller, action)) { +        binder.updateView(scope); +      } +    } +    return bubbleEvent; +  }; +  jQuery(controller.view, ":input"). +    bind(event, action); +  return controller; +}; + +nglr.WidgetFactory.prototype.createFileController = function(fileInput) { +  var uploadId = '__uploadWidget_' + (this.nextUploadId++); +  var view = nglr.FileController.template(uploadId); +  fileInput.after(view); +  var att = { +      data:this.serverUrl + "/admin/ServerAPI.swf", +      width:"95", height:"20", align:"top", +      wmode:"transparent"}; +  var par = { +      flashvars:"uploadWidgetId=" + uploadId, +      allowScriptAccess:"always"}; +  var swfNode = this.createSWF(att, par, uploadId); +  fileInput.remove(); +  var cntl = new nglr.FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database); +  jQuery(swfNode).data('controller', cntl); +  return cntl; +}; + +nglr.WidgetFactory.prototype.createTextWidget = function(textInput) { +  var controller = new nglr.TextController(textInput); +  controller.onChange(this.onChangeListener); +  return controller; +}; + +///////////////////// +// FileController +/////////////////////// + +nglr.FileController = function(view, scopeName, uploader, databaseUrl) { +  this.view = view; +  this.uploader = uploader; +  this.scopeName = scopeName; +  this.attachmentsPath = databaseUrl + '/_attachments'; +  this.value = null; +  this.lastValue = undefined; +}; + +nglr.FileController.dispatchEvent = function(id, event, args) { +  var object = document.getElementById(id); +  var controller = jQuery(object).data("controller"); +  nglr.FileController.prototype['_on_' + event].apply(controller, args); +}; + +nglr.FileController.template = function(id) { +  return jQuery('<span class="ng-upload-widget">' + +      '<input type="checkbox" ng-non-bindable="true"/>' + +      '<object id="' + id + '" />' + +      '<a></a>' + +      '<span/>' + +    '</span>'); +}; + +nglr.FileController.prototype._on_cancel = function() { +}; + +nglr.FileController.prototype._on_complete = function() { +}; + +nglr.FileController.prototype._on_httpStatus = function(status) { +  nglr.alert("httpStatus:" + this.scopeName + " status:" + status); +}; + +nglr.FileController.prototype._on_ioError = function() { +  nglr.alert("ioError:" + this.scopeName); +}; + +nglr.FileController.prototype._on_open = function() { +  nglr.alert("open:" + this.scopeName); +}; + +nglr.FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) { +}; + +nglr.FileController.prototype._on_securityError = function() { +  nglr.alert("securityError:" + this.scopeName); +}; + +nglr.FileController.prototype._on_uploadCompleteData = function(data) { +  var value = nglr.fromJson(data); +  value.url = this.attachmentsPath + '/' + value.id + '/' + value.text; +  this.view.find("input").attr('checked', true); +  var scope = this.view.scope(); +  this.value = value; +  this.updateModel(scope); +  this.value = null; +  scope.get('$binder').updateView(); +}; + +nglr.FileController.prototype._on_select = function(name, size, type) { +  this.name = name; +  this.view.find("a").text(name).attr('href', name); +  this.view.find("span").text(angular.filter.bytes(size)); +  this.upload(); +}; + +nglr.FileController.prototype.updateModel = function(scope) { +  var isChecked = this.view.find("input").attr('checked'); +  var value = isChecked ? this.value : null; +  if (this.lastValue === value) { +    return false; +  } else { +    scope.set(this.scopeName, value); +    return true; +  } +}; + +nglr.FileController.prototype.updateView = function(scope) { +  var modelValue = scope.get(this.scopeName); +  if (modelValue && this.value !== modelValue) { +    this.value = modelValue; +    this.view.find("a"). +      attr("href", this.value.url). +      text(this.value.text); +    this.view.find("span").text(angular.filter.bytes(this.value.size)); +  } +  this.view.find("input").attr('checked', !!modelValue); +}; + +nglr.FileController.prototype.upload = function() { +  if (this.name) { +    this.uploader.uploadFile(this.attachmentsPath); +  } +}; + + +/////////////////////// +// NullController +/////////////////////// +nglr.NullController = function(view) {this.view = view;}; +nglr.NullController.prototype.updateModel = function() { return true; }; +nglr.NullController.prototype.updateView = function() { }; +nglr.NullController.instance = new nglr.NullController(); + + +/////////////////////// +// ButtonController +/////////////////////// +nglr.ButtonController = function(view) {this.view = view;}; +nglr.ButtonController.prototype.updateModel = function(scope) { return true; }; +nglr.ButtonController.prototype.updateView = function(scope) {}; + +/////////////////////// +// TextController +/////////////////////// +nglr.TextController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.validator = view.getAttribute('ng-validate'); +  this.required = typeof view.attributes['ng-required'] != "undefined"; +  this.lastErrorText = null; +  this.lastValue = undefined; +  this.initialValue = view.value; +  var widget = view.getAttribute('ng-widget'); +  if (widget === 'datepicker') { +    jQuery(view).datepicker(); +  } +}; + +nglr.TextController.prototype.updateModel = function(scope) { +  var value = this.view.value; +  if (this.lastValue === value) { +    return false; +  } else { +    scope.setEval(this.exp, value); +    this.lastValue = value; +    return true; +  } +}; + +nglr.TextController.prototype.updateView = function(scope) { +  var view = this.view; +  var value = scope.get(this.exp); +  if (typeof value === "undefined") { +    value = this.initialValue; +    scope.setEval(this.exp, value); +  } +  value = value ? value : ''; +  if (this.lastValue != value) { +    view.value = value; +    this.lastValue = value; +  } +  var isValidationError = false; +  view.removeAttribute('ng-error'); +  if (this.required) { +    isValidationError = !(value && value.length > 0); +  } +  var errorText = isValidationError ? "Required Value" : null; +  if (!isValidationError && this.validator && value) { +    errorText = scope.validate(this.validator, value); +    isValidationError = !!errorText; +  } +  if (this.lastErrorText !== errorText) { +    this.lastErrorText = isValidationError; +    if (errorText !== null) { +      view.setAttribute('ng-error', errorText); +      scope.markInvalid(this); +    } +    jQuery(view).toggleClass('ng-validation-error', isValidationError); +  } +}; + +/////////////////////// +// CheckboxController +/////////////////////// +nglr.CheckboxController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastValue = undefined; +  this.initialValue = view.checked ? view.value : ""; +}; + +nglr.CheckboxController.prototype.updateModel = function(scope) { +  var input = this.view; +  var value = input.checked ? input.value : ''; +  if (this.lastValue === value) { +    return false; +  } else { +    scope.setEval(this.exp, value); +    this.lastValue = value; +    return true; +  } +}; + +nglr.CheckboxController.prototype.updateView = function(scope) { +  var input = this.view; +  var value = scope.eval(this.exp); +  if (typeof value === "undefined") { +    value = this.initialValue; +    scope.setEval(this.exp, value); +  } +  input.checked = input.value == (''+value); +}; + +/////////////////////// +// SelectController +/////////////////////// +nglr.SelectController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastValue = undefined; +  this.initialValue = view.value; +}; + +nglr.SelectController.prototype.updateModel = function(scope) { +  var input = this.view; +  if (input.selectedIndex < 0) { +    scope.setEval(this.exp, null); +  } else { +    var value = this.view.value; +    if (this.lastValue === value) { +      return false; +    } else { +      scope.setEval(this.exp, value); +      this.lastValue = value; +      return true; +    } +  } +}; + +nglr.SelectController.prototype.updateView = function(scope) { +  var input = this.view; +  var value = scope.get(this.exp); +  if (typeof value === 'undefined') { +    value = this.initialValue; +    scope.setEval(this.exp, value); +  } +  if (value !== this.lastValue) { +    input.value = value ? value : ""; +    this.lastValue = value; +  } +}; + +/////////////////////// +// MultiSelectController +/////////////////////// +nglr.MultiSelectController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastValue = undefined; +  this.initialValue = this.selected(); +}; + +nglr.MultiSelectController.prototype.selected = function () { +  var value = []; +  var options = this.view.options; +  for ( var i = 0; i < options.length; i++) { +    var option = options[i]; +    if (option.selected) { +      value.push(option.value); +    } +  } +  return value; +}; + +nglr.MultiSelectController.prototype.updateModel = function(scope) { +  var value = this.selected(); +  // TODO: This is wrong! no caching going on here as we are always comparing arrays +  if (this.lastValue === value) { +    return false; +  } else { +    scope.setEval(this.exp, value); +    this.lastValue = value; +    return true; +  } +}; + +nglr.MultiSelectController.prototype.updateView = function(scope) { +  var input = this.view; +  var selected = scope.get(this.exp); +  if (typeof selected === "undefined") { +    selected = this.initialValue; +    scope.setEval(this.exp, selected); +  } +  if (selected !== this.lastValue) { +    var options = input.options; +    for ( var i = 0; i < options.length; i++) { +      var option = options[i]; +      option.selected = _.include(selected, option.value); +    } +    this.lastValue = selected; +  } +}; + +/////////////////////// +// RadioController +/////////////////////// +nglr.RadioController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastChecked = undefined; +  this.lastValue = undefined; +  this.inputValue = view.value; +  this.initialValue = view.checked ? view.value : null; +}; + +nglr.RadioController.prototype.updateModel = function(scope) { +  var input = this.view; +  if (this.lastChecked) { +    return false; +  } else { +    input.checked = true; +    this.lastValue = scope.setEval(this.exp, this.inputValue); +    this.lastChecked = true; +    return true; +  } +}; + +nglr.RadioController.prototype.updateView = function(scope) { +  var input = this.view; +  var value = scope.get(this.exp); +  if (this.initialValue && typeof value === "undefined") { +    value = this.initialValue; +    scope.setEval(this.exp, value); +  } +  if (this.lastValue != value) { +    this.lastChecked = input.checked = this.inputValue == (''+value); +    this.lastValue = value; +  } +}; + +/////////////////////// +//ElementController +/////////////////////// +nglr.BindUpdater = function(view, exp) { +  this.view = view; +  this.exp = nglr.Binder.parseBindings(exp); +  this.hasError = false; +  this.scopeSelf = {element:view}; +}; + +nglr.BindUpdater.toText = function(obj) { +  var e = nglr.escapeHtml; +  switch(typeof obj) { +    case "string": +    case "boolean": +    case "number": +      return e(obj); +    case "function": +      return nglr.BindUpdater.toText(obj()); +    case "object": +      if (nglr.isNode(obj)) { +        return nglr.outerHTML(obj); +      } else if (obj instanceof angular.filter.Meta) { +        switch(typeof obj.html) { +          case "string": +          case "number": +            return obj.html; +          case "function": +            return obj.html(); +          case "object": +            if (nglr.isNode(obj.html)) +              return nglr.outerHTML(obj.html); +          default: +            break; +        } +        switch(typeof obj.text) { +          case "string": +          case "number": +            return e(obj.text); +          case "function": +            return e(obj.text()); +          default: +            break; +        } +      } +      if (obj === null) +        return ""; +      return e(nglr.toJson(obj, true)); +    default: +      return ""; +  } +}; + +nglr.BindUpdater.prototype.updateModel = function(scope) {}; +nglr.BindUpdater.prototype.updateView = function(scope) { +  var html = []; +  var parts = this.exp; +  var length = parts.length; +  for(var i=0; i<length; i++) { +    var part = parts[i]; +    var binding = nglr.Binder.binding(part); +    if (binding) { +      scope.evalWidget(this, binding, this.scopeSelf, function(value){ +        html.push(nglr.BindUpdater.toText(value)); +      }, function(e, text){ +        nglr.setHtml(this.view, text); +      }); +      if (this.hasError) { +        return; +      } +    } else { +      html.push(nglr.escapeHtml(part)); +    } +  } +  nglr.setHtml(this.view, html.join('')); +}; + +nglr.BindAttrUpdater = function(view, attrs) { +  this.view = view; +  this.attrs = attrs; +}; + +nglr.BindAttrUpdater.prototype.updateModel = function(scope) {}; +nglr.BindAttrUpdater.prototype.updateView = function(scope) { +  var jNode = jQuery(this.view); +  var attributeTemplates = this.attrs; +  if (this.hasError) { +    this.hasError = false; +    jNode. +      removeClass('ng-exception'). +      removeAttr('ng-error'); +  } +  var isImage = jNode.is('img'); +  for (var attrName in attributeTemplates) { +    var attributeTemplate = nglr.Binder.parseBindings(attributeTemplates[attrName]); +    var attrValues = []; +    for ( var i = 0; i < attributeTemplate.length; i++) { +      var binding = nglr.Binder.binding(attributeTemplate[i]); +      if (binding) { +        try { +          var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); +          if (value && (value.constructor !== nglr.array || value.length !== 0)) +            attrValues.push(value); +        } catch (e) { +          this.hasError = true; +          console.error('BindAttrUpdater', e); +          var jsonError = nglr.toJson(e, true); +          attrValues.push('[' + jsonError + ']'); +          jNode. +            addClass('ng-exception'). +            attr('ng-error', jsonError); +        } +      } else { +        attrValues.push(attributeTemplate[i]); +      } +    } +    var attrValue = attrValues.length ? attrValues.join('') : null; +    if(isImage && attrName == 'src' && !attrValue) +      attrValue = scope.get('config.server') + '/images/blank.gif'; +    jNode.attr(attrName, attrValue); +  } +}; + +nglr.EvalUpdater = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.hasError = false; +}; +nglr.EvalUpdater.prototype.updateModel = function(scope) {}; +nglr.EvalUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp); +}; + +nglr.HideUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.HideUpdater.prototype.updateModel = function(scope) {}; +nglr.HideUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(hideValue){ +    var view = jQuery(this.view); +    if (nglr.toBoolean(hideValue)) { +      view.hide(); +    } else { +      view.show(); +    } +  }); +}; + +nglr.ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ShowUpdater.prototype.updateModel = function(scope) {}; +nglr.ShowUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(hideValue){ +    var view = jQuery(this.view); +    if (nglr.toBoolean(hideValue)) { +      view.show(); +    } else { +      view.hide(); +    } +  }); +}; + +nglr.ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ClassUpdater.prototype.updateModel = function(scope) {}; +nglr.ClassUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(classValue){ +    if (classValue !== null && classValue !== undefined) { +      this.view.className = classValue; +    } +  }); +}; + +nglr.ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ClassEvenUpdater.prototype.updateModel = function(scope) {}; +nglr.ClassEvenUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(classValue){ +    var index = scope.get('$index'); +    jQuery(this.view).toggleClass(classValue, index % 2 === 1); +  }); +}; + +nglr.ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ClassOddUpdater.prototype.updateModel = function(scope) {}; +nglr.ClassOddUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(classValue){ +    var index = scope.get('$index'); +    jQuery(this.view).toggleClass(classValue, index % 2 === 0); +  }); +}; + +nglr.StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.StyleUpdater.prototype.updateModel = function(scope) {}; +nglr.StyleUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(styleValue){ +    jQuery(this.view).attr('style', "").css(styleValue); +  }); +}; + +/////////////////////// +// RepeaterUpdater +/////////////////////// +nglr.RepeaterUpdater = function(view, repeaterExpression, template, prefix) { +  this.view = view; +  this.template = template; +  this.prefix = prefix; +  this.children = []; +  var match = repeaterExpression.match(/^\s*(.+)\s+in\s+(.*)\s*$/); +  if (! match) { +    throw "Expected ng-repeat in form of 'item in collection' but got '" + +      repeaterExpression + "'."; +  } +  var keyValue = match[1]; +  this.iteratorExp = match[2]; +  match = keyValue.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/); +  if (!match) { +    throw "'item' in 'item in collection' should be identifier or (key, value) but get '" + +      keyValue + "'."; +  } +  this.valueExp = match[3] || match[1]; +  this.keyExp = match[2]; +}; + +nglr.RepeaterUpdater.prototype.updateModel = function(scope) {}; +nglr.RepeaterUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.iteratorExp, {}, function(iterator){ +    var self = this; +    if (!iterator) { +      iterator = []; +      if (scope.isProperty(this.iteratorExp)) { +        scope.set(this.iteratorExp, iterator); +      } +    } +    var iteratorLength = iterator.length; +    var childrenLength = this.children.length; +    var cursor = this.view; +    var time = 0; +    var child = null; +    var keyExp = this.keyExp; +    var valueExp = this.valueExp; +    var i = 0; +    jQuery.each(iterator, function(key, value){ +      if (i < childrenLength) { +        // reuse children +        child = self.children[i]; +        child.scope.set(valueExp, value); +      } else { +        // grow children +        var name = self.prefix + +          valueExp + " in " + self.iteratorExp + "[" + i + "]"; +        var childScope = new nglr.Scope(scope.state, name); +        childScope.set('$index', i); +        if (keyExp) +          childScope.set(keyExp, key); +        childScope.set(valueExp, value); +        child = { scope:childScope, element:self.template(childScope, self.prefix, i) }; +        cursor.after(child.element); +        self.children.push(child); +      } +      cursor = child.element; +      var s = new Date().getTime(); +      child.scope.updateView(); +      time += new Date().getTime() - s; +      i++; +    }); +    // shrink children +    for ( var r = childrenLength; r > iteratorLength; --r) { +      var unneeded = this.children.pop(); +      unneeded.element.removeNode(); +    } +    // Special case for option in select +    if (child && child.element[0].nodeName === "OPTION") { +      var select = jQuery(child.element[0].parentNode); +      var cntl = select.data('controller'); +      if (cntl) { +        cntl.lastValue = undefined; +        cntl.updateView(scope); +      } +    } +  }); +}; + +////////////////////////////////// +// PopUp +////////////////////////////////// + +nglr.PopUp = function(doc) { +  this.doc = doc; +}; + +nglr.PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; + +nglr.PopUp.prototype.bind = function () { +  var self = this; +  this.doc.find('.ng-validation-error,.ng-exception'). +    live("mouseover", nglr.PopUp.onOver); +}; + +nglr.PopUp.onOver = function(e) { +  nglr.PopUp.onOut(); +  var jNode = jQuery(this); +  jNode.bind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut); +  var position = jNode.position(); +  var de = document.documentElement; +  var w = self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth; +  var hasArea = w - position.left; +  var width = 300; +  var title = jNode.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error..."; +  var msg = jNode.attr("ng-error"); + +  var x; +  var arrowPos = hasArea>(width+75) ? "left" : "right"; +  var tip = jQuery( +    "<div id='ng-callout' style='width:"+width+"px'>" + +      "<div class='ng-arrow-"+arrowPos+"'/>" + +      "<div class='ng-title'>"+title+"</div>" + +      "<div class='ng-content'>"+msg+"</div>" + +    "</div>"); +  jQuery("body").append(tip); +  if(arrowPos === 'left'){ +    x = position.left + this.offsetWidth + 11; +  }else{ +    x = position.left - (width + 15); +    tip.find('.ng-arrow-right').css({left:width+1}); +  } + +  tip.css({left: x+"px", top: (position.top - 3)+"px"}); +  return true; +}; + +nglr.PopUp.onOut = function() { +  jQuery('#ng-callout'). +    unbind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut). +    remove(); +  return true; +}; + +////////////////////////////////// +// Status +////////////////////////////////// + + +nglr.Status = function(body) { +  this.loader = body.append(nglr.Status.DOM).find("#ng-loading"); +  this.requestCount = 0; +}; + +nglr.Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>'; + +nglr.Status.prototype.beginRequest = function () { +  if (this.requestCount === 0) { +    this.loader.show(); +  } +  this.requestCount++; +}; + +nglr.Status.prototype.endRequest = function () { +  this.requestCount--; +  if (this.requestCount === 0) { +    this.loader.hide("fold"); +  } +}; diff --git a/src/Widgets.js.orig b/src/Widgets.js.orig new file mode 100644 index 00000000..df1d3e40 --- /dev/null +++ b/src/Widgets.js.orig @@ -0,0 +1,764 @@ +   // Copyright (C) 2009 BRAT Tech LLC + + +nglr.WidgetFactory = function(serverUrl) { +  this.nextUploadId = 0; +  this.serverUrl = serverUrl; +  this.createSWF = swfobject.createSWF; +  this.onChangeListener = function(){}; +}; + +nglr.WidgetFactory.prototype.createController = function(input, scope) { +  var controller; +  var type = input.attr('type').toLowerCase(); +  var exp = input.attr('name'); +  if (exp) exp = exp.split(':').pop(); +  var event = "change"; +  var bubbleEvent = true; +  if (type == 'button' || type == 'submit' || type == 'reset') { +    controller = new nglr.ButtonController(input[0], exp); +    event = "click"; +    bubbleEvent = false; +  } else if (type == 'text' || type == 'textarea') { +    controller = new nglr.TextController(input[0], exp); +    event = "keyup change"; +  } else if (type == 'checkbox') { +    controller = new nglr.CheckboxController(input[0], exp); +    event = "click"; +  } else if (type == 'radio') { +    controller = new nglr.RadioController(input[0], exp); +    event="click"; +  } else if (type == 'select-one') { +    controller = new nglr.SelectController(input[0], exp); +  } else if (type == 'select-multiple') { +    controller = new nglr.MultiSelectController(input[0], exp); +  } else if (type == 'file') { +    controller = this.createFileController(input, exp); +  } else { +    throw 'Unknown type: ' + type; +  } +  input.data('controller', controller); +  var binder = scope.get('$binder'); +  var action = function() { +    if (controller.updateModel(scope)) { +      var action = jQuery(controller.view).attr('ng-action') || ""; +      if (scope.evalWidget(controller, action)) { +        binder.updateView(scope); +      } +    } +    return bubbleEvent; +  }; +  jQuery(controller.view, ":input"). +    bind(event, action); +  return controller; +}; + +nglr.WidgetFactory.prototype.createFileController = function(fileInput) { +  var uploadId = '__uploadWidget_' + (this.nextUploadId++); +  var view = nglr.FileController.template(uploadId); +  fileInput.after(view); +  var att = { +      data:this.serverUrl + "/admin/ServerAPI.swf", +      width:"95", height:"20", align:"top", +      wmode:"transparent"}; +  var par = { +      flashvars:"uploadWidgetId=" + uploadId, +      allowScriptAccess:"always"}; +  var swfNode = this.createSWF(att, par, uploadId); +  fileInput.remove(); +  var cntl = new nglr.FileController(view, fileInput[0].name, swfNode, this.serverUrl); +  jQuery(swfNode).data('controller', cntl); +  return cntl; +}; + +nglr.WidgetFactory.prototype.createTextWidget = function(textInput) { +  var controller = new nglr.TextController(textInput); +  controller.onChange(this.onChangeListener); +  return controller; +}; + +///////////////////// +// FileController +/////////////////////// + +nglr.FileController = function(view, scopeName, uploader, serverUrl) { +  this.view = view; +  this.uploader = uploader; +  this.scopeName = scopeName; +  this.uploadUrl = serverUrl + '/upload'; +  this.attachmentBase = serverUrl + '/attachments'; +  this.value = null; +  this.lastValue = undefined; +}; + +nglr.FileController.dispatchEvent = function(id, event, args) { +  var object = document.getElementById(id); +  var controller = jQuery(object).data("controller"); +  nglr.FileController.prototype['_on_' + event].apply(controller, args); +}; + +nglr.FileController.template = function(id) { +  return jQuery('<span class="ng-upload-widget">' + +      '<input type="checkbox" ng-non-bindable="true"/>' + +      '<object id="' + id + '" />' + +      '<a></a>' + +      '<span/>' + +    '</span>'); +}; + +nglr.FileController.prototype._on_cancel = function() { +}; + +nglr.FileController.prototype._on_complete = function() { +}; + +nglr.FileController.prototype._on_httpStatus = function(status) { +  nglr.alert("httpStatus:" + this.scopeName + " status:" + status); +}; + +nglr.FileController.prototype._on_ioError = function() { +  nglr.alert("ioError:" + this.scopeName); +}; + +nglr.FileController.prototype._on_open = function() { +  nglr.alert("open:" + this.scopeName); +}; + +nglr.FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) { +}; + +nglr.FileController.prototype._on_securityError = function() { +  nglr.alert("securityError:" + this.scopeName); +}; + +nglr.FileController.prototype._on_uploadCompleteData = function(data) { +  this.value = nglr.fromJson(data); +  this.value.url = this.attachmentBase + '/' + this.value.id + '/' + this.value.text; +  this.view.find("input").attr('checked', true); +  var scope = this.view.scope(); +  this.updateModel(scope); +  scope.get('$binder').updateView(); +}; + +nglr.FileController.prototype._on_select = function(name, size, type) { +  this.name = name; +  this.view.find("a").text(name).attr('href', name); +  this.view.find("span").text(filters.bytes(size)); +  this.upload(); +}; + +nglr.FileController.prototype.updateModel = function(scope) { +  var isChecked = this.view.find("input").attr('checked'); +  var value = isChecked ? this.value : null; +  if (this.lastValue === value) { +    return false; +  } else { +    scope.set(this.scopeName, value); +    return true; +  } +}; + +nglr.FileController.prototype.updateView = function(scope) { +  var modelValue = scope.get(this.scopeName); +  if (modelValue && this.value !== modelValue) { +    this.value = modelValue; +    this.view.find("a"). +    attr("href", this.value.url). +    text(this.value.name); +    this.view.find("span").text(filters.bytes(this.value.size)); +  } +  this.view.find("input").attr('checked', !!modelValue); +}; + +nglr.FileController.prototype.upload = function() { +  if (this.name) { +    this.uploader.uploadFile(this.uploadUrl); +  } +}; + + +/////////////////////// +// NullController +/////////////////////// +nglr.NullController = function(view) {this.view = view;}; +nglr.NullController.prototype.updateModel = function() { return true; }; +nglr.NullController.prototype.updateView = function() { }; +nglr.NullController.instance = new nglr.NullController(); + + +/////////////////////// +// ButtonController +/////////////////////// +nglr.ButtonController = function(view) {this.view = view;}; +nglr.ButtonController.prototype.updateModel = function(scope) { return true; }; +nglr.ButtonController.prototype.updateView = function(scope) {}; + +/////////////////////// +// TextController +/////////////////////// +nglr.TextController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.validator = view.getAttribute('ng-validate'); +  this.required = typeof view.attributes['ng-required'] != "undefined"; +  this.lastErrorText = null; +  this.lastValue = undefined; +  this.initialValue = view.value; +  var widget = view.getAttribute('ng-widget'); +  if (widget === 'datepicker') { +    jQuery(view).datepicker(); +  } +}; + +nglr.TextController.prototype.updateModel = function(scope) { +  var value = this.view.value; +  if (this.lastValue === value) { +    return false; +  } else { +    scope.set(this.exp, value); +    this.lastValue = value; +    return true; +  } +}; + +nglr.TextController.prototype.updateView = function(scope) { +  var view = this.view; +  var value = scope.get(this.exp); +  if (typeof value === "undefined") { +    value = this.initialValue; +    scope.set(this.exp, value); +  } +  value = value ? value : ''; +  if (this.lastValue != value) { +    view.value = value; +    this.lastValue = value; +  } +  var isValidationError = false; +  view.removeAttribute('ng-error'); +  if (this.required) { +    isValidationError = !(value && value.length > 0); +  } +  var errorText = isValidationError ? "Required Value" : null; +  if (!isValidationError && this.validator && value) { +    errorText = scope.validate(this.validator, value); +    isValidationError = !!errorText; +  } +  if (this.lastErrorText !== errorText) { +    this.lastErrorText = isValidationError; +    if (errorText !== null) { +      view.setAttribute('ng-error', errorText); +      scope.markInvalid(this); +    } +    jQuery(view).toggleClass('ng-validation-error', isValidationError); +  } +}; + +/////////////////////// +// CheckboxController +/////////////////////// +nglr.CheckboxController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastValue = undefined; +  this.initialValue = view.checked ? view.value : ""; +}; + +nglr.CheckboxController.prototype.updateModel = function(scope) { +  var input = this.view; +  var value = input.checked ? input.value : ''; +  if (this.lastValue === value) { +    return false; +  } else { +    scope.setEval(this.exp, value); +    this.lastValue = value; +    return true; +  } +}; + +nglr.CheckboxController.prototype.updateView = function(scope) { +  var input = this.view; +  var value = scope.eval(this.exp); +  if (typeof value === "undefined") { +    value = this.initialValue; +    scope.setEval(this.exp, value); +  } +  input.checked = input.value == (''+value); +}; + +/////////////////////// +// SelectController +/////////////////////// +nglr.SelectController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastValue = undefined; +  this.initialValue = view.value; +}; + +nglr.SelectController.prototype.updateModel = function(scope) { +  var input = this.view; +  if (input.selectedIndex < 0) { +    scope.set(this.exp, null); +  } else { +    var value = this.view.value; +    if (this.lastValue === value) { +      return false; +    } else { +      scope.set(this.exp, value); +      this.lastValue = value; +      return true; +    } +  } +}; + +nglr.SelectController.prototype.updateView = function(scope) { +  var input = this.view; +  var value = scope.get(this.exp); +  if (typeof value === 'undefined') { +    value = this.initialValue; +    scope.set(this.exp, value); +  } +  if (value !== this.lastValue) { +    input.value = value ? value : ""; +    this.lastValue = value; +  } +}; + +/////////////////////// +// MultiSelectController +/////////////////////// +nglr.MultiSelectController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastValue = undefined; +  this.initialValue = this.selected(); +}; + +nglr.MultiSelectController.prototype.selected = function () { +  var value = []; +  var options = this.view.options; +  for ( var i = 0; i < options.length; i++) { +    var option = options[i]; +    if (option.selected) { +      value.push(option.value); +    } +  } +  return value; +}; + +nglr.MultiSelectController.prototype.updateModel = function(scope) { +  var value = this.selected(); +  // TODO: This is wrong! no caching going on here as we are always comparing arrays +  if (this.lastValue === value) { +    return false; +  } else { +    scope.set(this.exp, value); +    this.lastValue = value; +    return true; +  } +}; + +nglr.MultiSelectController.prototype.updateView = function(scope) { +  var input = this.view; +  var selected = scope.get(this.exp); +  if (typeof selected === "undefined") { +    selected = this.initialValue; +    scope.set(this.exp, selected); +  } +  if (selected !== this.lastValue) { +    var options = input.options; +    for ( var i = 0; i < options.length; i++) { +      var option = options[i]; +      option.selected = selected.contains(option.value); +    } +    this.lastValue = selected; +  } +}; + +/////////////////////// +// RadioController +/////////////////////// +nglr.RadioController = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.lastChecked = undefined; +  this.lastValue = undefined; +  this.inputValue = view.value; +  this.initialValue = view.checked ? view.value : null; +}; + +nglr.RadioController.prototype.updateModel = function(scope) { +  var input = this.view; +  if (this.lastChecked) { +    return false; +  } else { +    input.checked = true; +    this.lastValue = scope.set(this.exp, this.inputValue); +    this.lastChecked = true; +    return true; +  } +}; + +nglr.RadioController.prototype.updateView = function(scope) { +  var input = this.view; +  var value = scope.get(this.exp); +  if (this.initialValue && typeof value === "undefined") { +    value = this.initialValue; +    scope.set(this.exp, value); +  } +  if (this.lastValue != value) { +    this.lastChecked = input.checked = this.inputValue == (''+value); +    this.lastValue = value; +  } +}; + +/////////////////////// +//ElementController +/////////////////////// +nglr.BindUpdater = function(view, exp) { +  this.view = view; +  this.exp = exp.parseBindings(); +  this.hasError = false; +  this.scopeSelf = {element:view}; +}; + +nglr.BindUpdater.toText = function(obj) { +  var e = nglr.escapeHtml; +  switch(typeof obj) { +    case "string": +    case "boolean": +    case "number": +      return e(obj); +    case "function": +      return nglr.BindUpdater.toText(obj()); +    case "object": +      if (nglr.isNode(obj)) { +        return nglr.outerHTML(obj); +      } else if (obj && obj.TAG === filters.Meta.TAG) { +        switch(typeof obj.html) { +          case "string": +          case "number": +            return obj.html; +          case "function": +            return obj.html(); +          default: +            break; +        } +        switch(typeof obj.text) { +          case "string": +          case "number": +            return e(obj.text); +          case "function": +            return e(obj.text()); +          default: +            break; +        } +      } +      if (obj === null) +        return ""; +      return e(nglr.toJson(obj, true)); +    default: +      return ""; +  } +}; + +nglr.BindUpdater.prototype.updateModel = function(scope) {}; +nglr.BindUpdater.prototype.updateView = function(scope) { +  var html = []; +  var parts = this.exp; +  var length = parts.length; +  for(var i=0; i<length; i++) { +    var part = parts[i]; +    var binding = part.binding(); +    if (binding) { +      scope.evalWidget(this, binding, this.scopeSelf, function(value){ +        html.push(nglr.BindUpdater.toText(value)); +      }, function(e, text){ +        nglr.setHtml(this.view, text); +      }); +      if (this.hasError) { +        return; +      } +    } else { +      html.push(nglr.escapeHtml(part)); +    } +  } +  nglr.setHtml(this.view, html.join('')); +}; + +nglr.BindAttrUpdater = function(view, attrs) { +  this.view = view; +  this.attrs = attrs; +}; + +nglr.BindAttrUpdater.prototype.updateModel = function(scope) {}; +nglr.BindAttrUpdater.prototype.updateView = function(scope) { +  var jNode = jQuery(this.view); +  var attributeTemplates = this.attrs; +  if (this.hasError) { +    this.hasError = false; +    jNode. +      removeClass('ng-exception'). +      removeAttr('ng-error'); +  } +  var isImage = jNode.is('img'); +  for (var attrName in attributeTemplates) { +    var attributeTemplate = attributeTemplates[attrName].parseBindings(); +    var attrValues = []; +    for ( var i = 0; i < attributeTemplate.length; i++) { +      var binding = attributeTemplate[i].binding(); +      if (binding) { +        try { +          var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); +          if (value && (value.constructor !== nglr.array || value.length !== 0)) +            attrValues.push(value); +        } catch (e) { +          this.hasError = true; +          console.error('BindAttrUpdater', e); +          var jsonError = nglr.toJson(e, true); +          attrValues.push('[' + jsonError + ']'); +          jNode. +            addClass('ng-exception'). +            attr('ng-error', jsonError); +        } +      } else { +        attrValues.push(attributeTemplate[i]); +      } +    } +    var attrValue = attrValues.length ? attrValues.join('') : null; +    if(isImage && attrName == 'src' && !attrValue) +      attrValue = scope.get('config.server') + '/images/blank.gif'; +    jNode.attr(attrName, attrValue); +  } +}; + +nglr.EvalUpdater = function(view, exp) { +  this.view = view; +  this.exp = exp; +  this.hasError = false; +}; +nglr.EvalUpdater.prototype.updateModel = function(scope) {}; +nglr.EvalUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp); +}; + +nglr.HideUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.HideUpdater.prototype.updateModel = function(scope) {}; +nglr.HideUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(hideValue){ +    var view = jQuery(this.view); +    if (nglr.toBoolean(hideValue)) { +      view.hide(); +    } else { +      view.show(); +    } +  }); +}; + +nglr.ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ShowUpdater.prototype.updateModel = function(scope) {}; +nglr.ShowUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(hideValue){ +    var view = jQuery(this.view); +    if (nglr.toBoolean(hideValue)) { +      view.show(); +    } else { +      view.hide(); +    } +  }); +}; + +nglr.ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ClassUpdater.prototype.updateModel = function(scope) {}; +nglr.ClassUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(classValue){ +    if (classValue !== null && classValue !== undefined) { +      this.view.className = classValue; +    } +  }); +}; + +nglr.ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ClassEvenUpdater.prototype.updateModel = function(scope) {}; +nglr.ClassEvenUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(classValue){ +    var index = scope.get('$index'); +    jQuery(this.view).toggleClass(classValue, index % 2 === 1); +  }); +}; + +nglr.ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.ClassOddUpdater.prototype.updateModel = function(scope) {}; +nglr.ClassOddUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(classValue){ +    var index = scope.get('$index'); +    jQuery(this.view).toggleClass(classValue, index % 2 === 0); +  }); +}; + +nglr.StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; }; +nglr.StyleUpdater.prototype.updateModel = function(scope) {}; +nglr.StyleUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.exp, {}, function(styleValue){ +    jQuery(this.view).attr('style', "").css(styleValue); +  }); +}; + +/////////////////////// +// RepeaterUpdater +/////////////////////// +nglr.RepeaterUpdater = function(view, repeaterExpression, template, prefix) { +  this.view = view; +  this.template = template; +  this.prefix = prefix; +  this.children = []; +  var match = repeaterExpression.match(/^\s*(.+)\s+in\s+(.*)\s*$/); +  if (! match) { +    throw "Expected ng-repeat in form of 'item in collection' but got '" + repeaterExpression + "'."; +  } +  this.itemExp = match[1]; +  this.iteratorExp = match[2]; +}; + +nglr.RepeaterUpdater.prototype.updateModel = function(scope) {}; +nglr.RepeaterUpdater.prototype.updateView = function(scope) { +  scope.evalWidget(this, this.iteratorExp, {}, function(iterator){ +    if (!iterator) { +      iterator = []; +      if (scope.isProperty(this.iteratorExp)) { +        scope.set(this.iteratorExp, iterator); +      } +    } +    var iteratorLength = iterator.length; +    var childrenLength = this.children.length; +    var cursor = this.view; +    var time = 0; +    var child = null; +    var itemExp = this.itemExp; +    for ( var i = 0; i < iteratorLength; i++) { +      if (i < iteratorLength) { +        if (i < childrenLength) { // reuse children +          child = this.children[i]; +          child.scope.set(itemExp, iterator[i]); +        } else { // grow children +          var name = this.prefix + +          itemExp + " in " + this.iteratorExp + "[" + i + "]"; +          var childScope = new nglr.Scope(scope.state, name); +          childScope.set('$index', i); +          childScope.set(itemExp, iterator[i]); +          child = { scope:childScope, element:this.template(childScope, this.prefix, i) }; +          cursor.after(child.element); +          this.children.push(child); +        } +        cursor = child.element; +        var s = new Date().getTime(); +        child.scope.updateView(); +        time += new Date().getTime() - s; +      } +    } +    // shrink children +    for ( var r = childrenLength; r > iteratorLength; --r) { +      var unneeded = this.children.pop(); +      unneeded.element.removeNode(); +    } +    // Special case for option in select +    if (child && child.element[0].nodeName === "OPTION") { +      var select = jQuery(child.element[0].parentNode); +      var cntl = select.data('controller'); +      if (cntl) { +        cntl.lastValue = undefined; +        cntl.updateView(scope); +      } +    } +  }); +}; + +////////////////////////////////// +// PopUp +////////////////////////////////// + +nglr.PopUp = function(doc) { +  this.doc = doc; +}; + +nglr.PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; + +nglr.PopUp.prototype.bind = function () { +  var self = this; +  this.doc.find('.ng-validation-error,.ng-exception'). +    live("mouseover", nglr.PopUp.onOver); +}; + +nglr.PopUp.onOver = function(e) { +  nglr.PopUp.onOut(); +  var jNode = jQuery(this); +  jNode.bind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut); +  var position = jNode.position(); +  var de = document.documentElement; +  var w = self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth; +  var hasArea = w - position.left; +  var width = 300; +  var title = jNode.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error..."; +  var msg = jNode.attr("ng-error"); + +  var x; +  var arrowPos = hasArea>(width+75) ? "left" : "right"; +  var tip = jQuery( +    "<div id='ng-callout' style='width:"+width+"px'>" + +      "<div class='ng-arrow-"+arrowPos+"'/>" + +      "<div class='ng-title'>"+title+"</div>" + +      "<div class='ng-content'>"+msg+"</div>" + +    "</div>"); +  jQuery("body").append(tip); +  if(arrowPos === 'left'){ +    x = position.left + this.offsetWidth + 11; +  }else{ +    x = position.left - (width + 15); +    tip.find('.ng-arrow-right').css({left:width+1}); +  } + +  tip.css({left: x+"px", top: (position.top - 3)+"px"}); +  return true; +}; + +nglr.PopUp.onOut = function() { +  jQuery('#ng-callout'). +    unbind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut). +    remove(); +  return true; +}; + +////////////////////////////////// +// Status +////////////////////////////////// + +nglr.Status = function (body) { +  this.body = body; +  this.requestCount = 0; +}; +nglr.Status.ANGULAR = "<a class='ng-angular-logo' href='http://www.getangular.com'>&lt;angular/&gt;</a>™"; + +nglr.Status.prototype.beginRequest = function () { +  if (this.requestCount === 0) { +<<<<<<< HEAD:public/javascripts/nglr/Widgets.js +    this.dialogView = jQuery('<div class="ng-dialog" title="'+nglr.ControlBar.ANGULAR+' Server Communication:">Please Wait...<div/><div class="loader"></div></div>'); +======= +    this.dialogView = jQuery('<div title="'+nglr.Status.ANGULAR+' Server Communication:">Please Wait...<div/></div>'); +    this.progressWidget = this.dialogView.find("div"); +    this.progressWidget.progressbar({value:0}); +>>>>>>> master:public/javascripts/nglr/Widgets.js +    this.dialogView.dialog({bgiframe:true, minHeight:50, modal:true}); +    this.maxRequestCount = 0; +  } +  this.requestCount++; +  this.maxRequestCount++; +}; + +nglr.Status.prototype.endRequest = function () { +  this.requestCount--; +  if (this.requestCount === 0) { +    this.dialogView.dialog("destroy"); +    this.dialogView.remove(); +    this.dialogView = null; +  } +}; diff --git a/src/XSitePost.js b/src/XSitePost.js new file mode 100644 index 00000000..7d81e207 --- /dev/null +++ b/src/XSitePost.js @@ -0,0 +1,100 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC + +if (typeof nglr == 'undefined') nglr = {}; + +if (typeof console == 'undefined') console = {}; +if (typeof console.log == 'undefined') +  console.log = function() {}; +if (typeof console.error == 'undefined') +  console.error = function() {}; + +nglr.XSitePost = function(baseUrl, window, prefix) { +  this.baseUrl = baseUrl; +  this.post = jQuery.post; +  this.window = window; +  this.inQueue = {}; +  this.outQueue = []; +  this.maxMsgSize = 100000; +  this.delay = 20; +  this.prefix = prefix; +  this.setTimeout=function(fn, delay){window.setTimeout(fn, delay);}; +}; + +nglr.XSitePost.prototype.init = function() { +  this.window.name = ''; +  this.response('ready', 'null'); +}; + +nglr.XSitePost.prototype.incomingFragment = function(fragment) { +  var parts = fragment.split(";"); +  this.incomingMsg(parts.shift(), 1*parts.shift(), 1*parts.shift(), parts.shift()); +}; + +nglr.XSitePost.prototype.incomingMsg = function(id, partNo, totalParts, msgPart) { +  var msg = this.inQueue[id]; +  if (!msg) { +    msg = {id:id, parts:[], count:0}; +    this.inQueue[id] = msg; +  } +  msg.parts[partNo] = msgPart; +  msg.count++; +  if (totalParts === msg.count) { +    delete this.inQueue[id]; +    var request = this.decodePost(msg.parts.join('')); +    var self = this; +    this.post(this.baseUrl + request.url, request.params, function(response, status){ +      self.response(id, response, status); +    }); +  } +}; + +nglr.XSitePost.prototype.response = function(id, response, status) { +  var start = 0; +  var end; +  var msg = Base64.encode(response); +  var msgLen = msg.length; +  var total = Math.ceil(msgLen / this.maxMsgSize); +  var part = 0; +  while (start < msgLen) { +    end = Math.min(msgLen, start + this.maxMsgSize); +    this.outQueue.push(id + ':'+part+':'+total+':' + msg.substring(start, end)); +    start = end; +    part++; +  } +}; + +nglr.XSitePost.prototype.decodePost = function(post) { +  var parts = post.split(':'); +  var url = Base64.decode(parts.shift()); +  var params = {}; +  while(parts.length !== 0) { +    var key = parts.shift(); +    var value = Base64.decode(parts.shift()); +    params[key] = value; +  } +  return {url:url, params:params}; +}; + +nglr.XSitePost.prototype.listen = function() { +  console.log("listen()"); +  var self = this; +  var window = this.window; +  var outQueue = this.outQueue; +  var setTimeout = this.setTimeout; +  var prefix = this.prefix; +  var prefixLen = prefix.length; +  var prefixRec = prefix + '>'; +  var prefixRecLen = prefixRec.length; +  window.name = prefix; +  var pull = function(){ +    var value = window.name; +    if (value == prefix && outQueue.length > 0) { +      window.name = prefix + '<' + outQueue.shift(); +    } else if (value.substr(0, prefixRecLen) == prefixRec) { +      self.incomingFragment(value.substr(prefixRecLen)); +      window.name = prefix; +    } +    setTimeout(pull, self.delay); +  }; +  pull(); +}; diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js new file mode 100644 index 00000000..b7ae6a38 --- /dev/null +++ b/src/angular-bootstrap.js @@ -0,0 +1,100 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC + +(function(previousOnLoad){ +  var filename = /(.*)\/angular-(.*).js(#(.*))?/; +  var scripts = document.getElementsByTagName("script"); +  var scriptConfig = { +      autoSubmit:true, +      autoBind:true, +      autoLoadDependencies:false +  }; +  for(var j = 0; j < scripts.length; j++) { +    var src = scripts[j].src; +    if (src && src.match(filename)) { +      var parts = src.match(filename); +      if (parts[2] == 'bootstrap') { +        scriptConfig.autoLoadDependencies = true; +      } +      scriptConfig.server = parts[1] || ''; +      if (!scriptConfig.server) { +        scriptConfig.server = window.location.toString().split(window.location.pathname)[0]; +      } +      if (parts[4]) { +        var directive = parts[4].split('&'); +        for ( var i = 0; i < directive.length; i++) { +          var keyValue = directive[i].split('='); +          var key = keyValue[0]; +          var value = keyValue.length == 1 ? true : keyValue[1]; +          if (value == 'false') value = false; +          if (value == 'true') value = true; +          scriptConfig[key] = value; +        } +      } +    } +  } + +  var addScript = function(path, server){ +    server = server || scriptConfig.server; +    document.write('<script type="text/javascript" src="' + server + path +'"></script>'); +  }; + +  if (scriptConfig.autoLoadDependencies) { +    addScript("/javascripts/webtoolkit.base64.js"); +    addScript("/javascripts/swfobject.js"); +    addScript("/javascripts/jQuery/jquery-1.3.2.js"); +    addScript("/javascripts/jQuery/jquery-ui-1.7.1.custom.min.js"); +    addScript("/javascripts/underscore/underscore.js"); +    addScript("/javascripts/nglr/Loader.js"); +    addScript("/javascripts/nglr/API.js"); +    addScript("/javascripts/nglr/Binder.js"); +    addScript("/javascripts/nglr/ControlBar.js"); +    addScript("/javascripts/nglr/DataStore.js"); +    addScript("/javascripts/nglr/Filters.js"); +    addScript("/javascripts/nglr/JSON.js"); +    addScript("/javascripts/nglr/Model.js"); +    addScript("/javascripts/nglr/Parser.js"); +    addScript("/javascripts/nglr/Scope.js"); +    addScript("/javascripts/nglr/Server.js"); +    addScript("/javascripts/nglr/Users.js"); +    addScript("/javascripts/nglr/Validators.js"); +    addScript("/javascripts/nglr/Widgets.js"); +  } else { +    addScript("/ajax/libs/swfobject/2.2/swfobject.js", "http://ajax.googleapis.com"); +    addScript("/ajax/libs/jquery/1.3.2/jquery.min.js", "http://ajax.googleapis.com"); +    addScript("/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js", "http://ajax.googleapis.com"); +  } + +  window.onload = function() { +    window.angular.init = function(root, config){ +      var cnfgMerged = _.clone(scriptConfig||{}); +      _.extend(cnfgMerged, config); +      new nglr.Loader(root, jQuery("head"), cnfgMerged).load(); +    }; + +    var doc = window.document; +    if (scriptConfig.bindRootId) { +      doc = null; +      var ids = scriptConfig.bindRootId.split('|'); +      for ( var i = 0; i < ids.length && !doc; i++) { +        var idCond = ids[i].split('?'); +        var id = idCond[0]; +        if (idCond.length > 1) { +          if (!window.document.getElementById(idCond[1])) { +            continue; +          } +        } +        doc = window.document.getElementById(id); +      } +    } +    if (scriptConfig.autoBind && doc) { +      window.angular.init(doc); +    } +    if (typeof previousOnLoad === 'function') { +      try { +      previousOnLoad.apply(this, arguments); +      } catch (e) {} +    } +  }; +})(window.onload); + + diff --git a/src/test/Runner.js b/src/test/Runner.js new file mode 100644 index 00000000..478ef73e --- /dev/null +++ b/src/test/Runner.js @@ -0,0 +1,160 @@ +nglr.test.ScenarioRunner = function(scenarios, body) { +  this.scenarios = scenarios; +  this.body = body; +}; + +nglr.test.ScenarioRunner.prototype = { +  run:function(){ +    this.setUpUI(); +    this.runScenarios(); +  }, +  setUpUI:function(){ +    this.body.html( +      '<div id="runner">' + +        '<div class="console"></div>' + +      '</div>' + +      '<div id="testView">' + +        '<iframe></iframe>' + +      '</div>'); +    this.console = this.body.find(".console"); +    this.testFrame = this.body.find("iframe"); +    this.console.find(".run").live("click", function(){ +      jQuery(this).parent().find('.log').toggle(); +    }); +  }, +  runScenarios:function(){ +    var runner = new nglr.test.Runner(this.console, this.testFrame); +    _.stepper(this.scenarios, function(next, scenario, name){ +        new nglr.test.Scenario(name, scenario).run(runner, next); +      }, function(){ +      } +    ); +  } +}; + +nglr.test.Runner = function(console, frame){ +  this.console = console; +  this.current = null; +  this.tests = []; +  this.frame = frame; +}; +nglr.test.Runner.prototype = { +  start:function(name){ +    var current = this.current = { +      name:name, +      start:new Date().getTime(), +      scenario:jQuery('<div class="scenario"></div>') +    }; +    current.run = current.scenario.append( +      '<div class="run">' +  +        '<span class="name">.</span>' +  +        '<span class="time">.</span>' +  +        '<span class="state">.</span>' +  +      '</run>').find(".run"); +    current.log = current.scenario.append('<div class="log"></div>').find(".log"); +    current.run.find(".name").text(name); +    this.tests.push(current); +    this.console.append(current.scenario); +  }, +  end:function(name){ +    var current = this.current; +    var run = current.run; +    this.current = null; +    current.end = new Date().getTime(); +    current.time = current.end - current.start; +    run.find(".time").text(current.time); +    run.find(".state").text(current.error ? "FAIL" : "PASS"); +    run.addClass(current.error ? "fail" : "pass"); +    if (current.error) +      run.find(".run").append('<span div="error"></span>').text(current.error); +    current.scenario.find(".log").hide(); +  }, +  log:function(level) { +    var buf = []; +    for ( var i = 1; i < arguments.length; i++) { +      var arg = arguments[i]; +      buf.push(typeof arg == "string" ?arg:nglr.toJson(arg)); +    } +    var log = jQuery('<div class="' + level + '"></div>'); +    log.text(buf.join(" ")); +    this.current.log.append(log); +    this.console.scrollTop(this.console[0].scrollHeight); +    if (level == "error")  +      this.current.error = buf.join(" "); +  } +}; + +nglr.test.Scenario = function(name, scenario){ +  this.name = name; +  this.scenario = scenario; +}; +nglr.test.Scenario.prototype = { +  run:function(runner, callback) { +    var self = this; +    _.stepper(this.scenario, function(next, steps, name){ +      if (name.charAt(0) == '$') { +        next(); +      } else { +        runner.start(self.name + "::" + name); +        var allSteps = (self.scenario.$before||[]).concat(steps); +        _.stepper(allSteps, function(next, step){ +          self.executeStep(runner, step, next); +        }, function(){ +          runner.end(); +          next(); +        }); +      } +    }, callback); +  }, +  verb:function(step){ +    var fn = null; +  if (!step) fn = function (){ throw "Step is null!"; } +  else if (step.Given) fn = angular.test.GIVEN[step.Given]; +  else if (step.When) fn = angular.test.WHEN[step.When]; +  else if (step.Then) fn = angular.test.THEN[step.Then]; +    return fn || function (){ +             throw "ERROR: Need Given/When/Then got: " + nglr.toJson(step); +           };     +  }, +  context: function(runner) { +    var frame = runner.frame; +    var window = frame[0].contentWindow; +    var document; +    if (window.jQuery)  +      document = window.jQuery(window.document); +    var context = { +        frame:frame,  +        window:window, +        log:_.bind(runner.log, runner, "info"), +        document:document, +        assert:function(element, path){ +          if (element.size() != 1) { +            throw "Expected to find '1' found '"+ +              element.size()+"' for '"+path+"'."; +          } +          return element; +        }, +        element:function(path){ +          var exp = path.replace("{{","[ng-bind=").replace("}}", "]"); +          var element = document.find(exp); +          return context.assert(element, path); +        } +    }; +    return context; +  }, +  executeStep:function(runner, step, callback) { +    if (!step) { +    callback(); +    return; +  } +    runner.log("info", nglr.toJson(step)); +    var fn = this.verb(step); +    var context = this.context(runner); +    _.extend(context, step); +    try { +      (fn.call(context)||function(c){c();})(callback); +    } catch (e) { +      runner.log("error", "ERROR: " + nglr.toJson(e)); +    } +  } +}; diff --git a/src/test/Steps.js b/src/test/Steps.js new file mode 100644 index 00000000..af4b84d6 --- /dev/null +++ b/src/test/Steps.js @@ -0,0 +1,57 @@ +angular.test.GIVEN = { +  browser:function(){ +    var self = this; +    if (jQuery.browser.safari && this.frame.attr('src') == this.at) { +      this.window.location.reload(); +    } else { +      this.frame.attr('src', this.at); +    } +    return function(done){ +      self.frame.load(function(){ +        self.frame.unbind(); +        done(); +      }); +    }; +  }, +  dataset:function(){ +    this.frame.name="$DATASET:" + nglr.toJson({dataset:this.dataset}); +  } +}; +angular.test.WHEN = { +  enter:function(){ +    var element = this.element(this.at); +    element.attr('value', this.text); +    element.change(); +  }, +  click:function(){ +    var element = this.element(this.at); +    var input = element[0]; +    // emulate the browser behavior which causes it +    // to be overridden at the end. +    var checked = input.checked = !input.checked; +    element.click(); +    input.checked = checked; +  }, +  select:function(){ +    var element = this.element(this.at); +    var path = "option[value=" + this.option + "]"; +    var option = this.assert(element.find(path)); +    option[0].selected = !option[0].selected;  +    element.change(); +  } +}; +angular.test.THEN = { +  text:function(){ +    var element = this.element(this.at); +    if (typeof this.should_be != undefined ) { +      var should_be = this.should_be; +      if (_.isArray(this.should_be)) +        should_be = JSON.stringify(should_be); +      if (element.text() != should_be) +        throw "Expected " + should_be +  +              " but was " + element.text() + "."; +    } +  }, +  drainRequestQueue:function(){ +  } +}; diff --git a/src/test/_namespace.js b/src/test/_namespace.js new file mode 100644 index 00000000..78f430f1 --- /dev/null +++ b/src/test/_namespace.js @@ -0,0 +1,5 @@ +if (!angular)             angular = {}; +if (!angular.test)        angular.test = {}; +if (!angular.test.GIVEN)  angular.test.GIVEN = {}; +if (!angular.test.WHEN)   angular.test.WHEN = {}; +if (!angular.test.THEN)   angular.test.THEN = {}; diff --git a/test/ApiTest.js b/test/ApiTest.js new file mode 100644 index 00000000..250a27b1 --- /dev/null +++ b/test/ApiTest.js @@ -0,0 +1,252 @@ +ApiTest = TestCase("ApiTest"); + +ApiTest.prototype.testItShouldReturnTypeOf = function (){ +  assertEquals("undefined", angular.Object.typeOf(undefined)); +  assertEquals("null", angular.Object.typeOf(null)); +  assertEquals("object", angular.Collection.typeOf({})); +  assertEquals("array", angular.Array.typeOf([])); +  assertEquals("string", angular.Object.typeOf("")); +  assertEquals("date", angular.Object.typeOf(new Date())); +  assertEquals("element", angular.Object.typeOf(document.body)); +  assertEquals("function", angular.Object.typeOf(function(){})); +}; +   +ApiTest.prototype.testItShouldReturnSize = function(){ +  assertEquals(0, angular.Collection.size({})); +  assertEquals(1, angular.Collection.size({a:"b"})); +  assertEquals(0, angular.Object.size({})); +  assertEquals(1, angular.Array.size([0])); +}; + +ApiTest.prototype.testIncludeIf = function() { +  var array = []; +  var obj = {}; + +  angular.Array.includeIf(array, obj, true); +  angular.Array.includeIf(array, obj, true); +  assertTrue(_.include(array, obj)); +  assertEquals(1, array.length); + +  angular.Array.includeIf(array, obj, false); +  assertFalse(_.include(array, obj)); +  assertEquals(0, array.length); + +  angular.Array.includeIf(array, obj, 'x'); +  assertTrue(_.include(array, obj)); +  assertEquals(1, array.length); +  angular.Array.includeIf(array, obj, ''); +  assertFalse(_.include(array, obj)); +  assertEquals(0, array.length); +}; + +ApiTest.prototype.testSum = function(){ +  assertEquals(3, angular.Array.sum([{a:"1"}, {a:"2"}], 'a')); +}; + +ApiTest.prototype.testSumContainingNaN = function(){ +  assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], 'a')); +  assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], function($){return $.a;})); +}; + +ApiTest.prototype.testInclude = function(){ +  assertTrue(angular.Array.include(['a'], 'a')); +  assertTrue(angular.Array.include(['a', 'b'], 'a')); +  assertTrue(!angular.Array.include(['c'], 'a')); +  assertTrue(!angular.Array.include(['c', 'b'], 'a')); +}; + +ApiTest.prototype.testIndex = function(){ +  assertEquals(angular.Array.indexOf(['a'], 'a'), 0); +  assertEquals(angular.Array.indexOf(['a', 'b'], 'a'), 0); +  assertEquals(angular.Array.indexOf(['b', 'a'], 'a'), 1); +  assertEquals(angular.Array.indexOf(['b', 'b'],'x'), -1); +}; + +ApiTest.prototype.testRemove = function(){ +  var items = ['a', 'b', 'c']; +  assertEquals(angular.Array.remove(items, 'q'), 'q'); +  assertEquals(items.length, 3); + +  assertEquals(angular.Array.remove(items, 'b'), 'b'); +  assertEquals(items.length, 2); + +  assertEquals(angular.Array.remove(items, 'a'), 'a'); +  assertEquals(items.length, 1); + +  assertEquals(angular.Array.remove(items, 'c'), 'c'); +  assertEquals(items.length, 0); + +  assertEquals(angular.Array.remove(items, 'q'), 'q'); +  assertEquals(items.length, 0); +}; + +ApiTest.prototype.testFindById = function() { +  var items = [{$id:1}, {$id:2}, {$id:3}]; +  assertNull(angular.Array.findById(items, 0)); +  assertEquals(items[0], angular.Array.findById(items, 1)); +  assertEquals(items[1], angular.Array.findById(items, 2)); +  assertEquals(items[2], angular.Array.findById(items, 3)); +}; + +ApiTest.prototype.testFilter = function() { +  var items = ["MIsKO", {name:"shyam"}, ["adam"], 1234]; +  assertEquals(4, angular.Array.filter(items, "").length); +  assertEquals(4, angular.Array.filter(items, undefined).length); + +  assertEquals(1, angular.Array.filter(items, 'iSk').length); +  assertEquals("MIsKO", angular.Array.filter(items, 'isk')[0]); + +  assertEquals(1, angular.Array.filter(items, 'yam').length); +  assertEquals(items[1], angular.Array.filter(items, 'yam')[0]); + +  assertEquals(1, angular.Array.filter(items, 'da').length); +  assertEquals(items[2], angular.Array.filter(items, 'da')[0]); + +  assertEquals(1, angular.Array.filter(items, '34').length); +  assertEquals(1234, angular.Array.filter(items, '34')[0]); + +  assertEquals(0, angular.Array.filter(items, "I don't exist").length); +}; + +ApiTest.prototype.testShouldNotFilterOnSystemData = function() { +  assertEquals("", "".charAt(0)); // assumption +  var items = [{$name:"misko"}]; +  assertEquals(0, angular.Array.filter(items, "misko").length); +}; + +ApiTest.prototype.testFilterOnSpecificProperty = function() { +  var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}]; +  assertEquals(2, angular.Array.filter(items, {}).length); + +  assertEquals(2, angular.Array.filter(items, {name:'a'}).length); + +  assertEquals(1, angular.Array.filter(items, {name:'b'}).length); +  assertEquals("abc", angular.Array.filter(items, {name:'b'})[0].name); +}; + +ApiTest.prototype.testFilterOnFunction = function() { +  var items = [{name:"a"}, {name:"abc", done:true}]; +  assertEquals(1, angular.Array.filter(items, function(i){return i.done;}).length); +}; + +ApiTest.prototype.testFilterIsAndFunction = function() { +  var items = [{first:"misko", last:"hevery"}, +               {first:"adam", last:"abrons"}]; + +  assertEquals(2, angular.Array.filter(items, {first:'', last:''}).length); +  assertEquals(1, angular.Array.filter(items, {first:'', last:'hevery'}).length); +  assertEquals(0, angular.Array.filter(items, {first:'adam', last:'hevery'}).length); +  assertEquals(1, angular.Array.filter(items, {first:'misko', last:'hevery'}).length); +  assertEquals(items[0], angular.Array.filter(items, {first:'misko', last:'hevery'})[0]); +}; + +ApiTest.prototype.testFilterNot = function() { +  var items = ["misko", "adam"]; + +  assertEquals(1, angular.Array.filter(items, '!isk').length); +  assertEquals(items[1], angular.Array.filter(items, '!isk')[0]); +}; + +ApiTest.prototype.testAdd = function() { +  var add = angular.Array.add; +  assertJsonEquals([{}, "a"], add(add([]),"a")); +}; + +ApiTest.prototype.testCount = function() { +  var array = [{name:'a'},{name:'b'},{name:''}]; +  var obj = {}; + +  assertEquals(3, angular.Array.count(array)); +  assertEquals(2, angular.Array.count(array, 'name')); +  assertEquals(1, angular.Array.count(array, 'name=="a"')); +}; + +ApiTest.prototype.testFind = function() { +  var array = [{name:'a'},{name:'b'},{name:''}]; +  var obj = {}; + +  assertEquals(undefined, angular.Array.find(array, 'false')); +  assertEquals('default', angular.Array.find(array, 'false', 'default')); +  assertEquals('a', angular.Array.find(array, 'name == "a"').name); +  assertEquals('', angular.Array.find(array, 'name == ""').name); +}; + +ApiTest.prototype.testItShouldSortArray = function() { +  assertEquals([2,15], angular.Array.orderBy([15,2])); +  assertEquals(["a","B", "c"], angular.Array.orderBy(["c","B", "a"])); +  assertEquals([15,"2"], angular.Array.orderBy([15,"2"])); +  assertEquals(["15","2"], angular.Array.orderBy(["15","2"])); +  assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a')); +  assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a', "F")); +}; + +ApiTest.prototype.testItShouldSortArrayInReverse = function() { +  assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', true)); +  assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "T")); +  assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "reverse")); +}; + +ApiTest.prototype.testItShouldSortArrayByPredicate = function() { +  assertJsonEquals([{a:2, b:1},{a:15, b:1}],  +    angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['a', 'b'])); +  assertJsonEquals([{a:2, b:1},{a:15, b:1}],  +    angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['b', 'a'])); +  assertJsonEquals([{a:15, b:1},{a:2, b:1}],  +    angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['+b', '-a'])); +}; + +ApiTest.prototype.testQuoteString = function(){ +  assertEquals(angular.String.quote('a'), '"a"'); +  assertEquals(angular.String.quote('\\'), '"\\\\"'); +  assertEquals(angular.String.quote("'a'"), '"\'a\'"'); +  assertEquals(angular.String.quote('"a"'), '"\\"a\\""'); +  assertEquals(angular.String.quote('\n\f\r\t'), '"\\n\\f\\r\\t"'); +}; + +ApiTest.prototype.testQuoteStringBug = function(){ +  assertEquals(angular.String.quote('"7\\\\\\\"7"', "7\\\"7")); +}; + +ApiTest.prototype.testQuoteUnicode = function(){ +  assertEquals('"abc\\u00a0def"', angular.String.quoteUnicode('abc\u00A0def')); +}; + +ApiTest.prototype.testMerge = function() { +  var array = [{name:"misko"}]; +  angular.Array.merge(array, 0, {name:"", email:"email1"}); +  angular.Array.merge(array, 1, {name:"adam", email:"email2"}); +  assertJsonEquals([{"email":"email1","name":"misko"},{"email":"email2","name":"adam"}], array); +}; + +ApiTest.prototype.testOrderByToggle = function() { +  var orderByToggle = angular.Array.orderByToggle; +  var predicate = []; +  assertEquals(['+a'], orderByToggle(predicate, 'a')); +  assertEquals(['-a'], orderByToggle(predicate, 'a')); + +  assertEquals(['-a', '-b'], orderByToggle(['-b', 'a'], 'a')); +}; + +ApiTest.prototype.testOrderByToggle = function() { +  var orderByDirection = angular.Array.orderByDirection; +  assertEquals("", orderByDirection(['+a','b'], 'x')); +  assertEquals("", orderByDirection(['+a','b'], 'b')); +  assertEquals('ng-ascend', orderByDirection(['a','b'], 'a')); +  assertEquals('ng-ascend', orderByDirection(['+a','b'], 'a')); +  assertEquals('ng-descend', orderByDirection(['-a','b'], 'a')); +  assertEquals('up', orderByDirection(['+a','b'], 'a', 'up', 'down')); +  assertEquals('down', orderByDirection(['-a','b'], 'a', 'up', 'down')); +}; + +ApiTest.prototype.testDateToUTC = function(){ +  var date = new Date("Sep 10 2003 13:02:03 GMT"); +  assertEquals("date", angular.Object.typeOf(date)); +  assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date)); +}; + +ApiTest.prototype.testStringFromUTC = function(){ +  var date = angular.String.toDate("2003-09-10T13:02:03Z"); +  assertEquals("date", angular.Object.typeOf(date)); +  assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date)); +  assertEquals("str", angular.String.toDate("str")); +}; diff --git a/test/Base64Test.js b/test/Base64Test.js new file mode 100644 index 00000000..a9353186 --- /dev/null +++ b/test/Base64Test.js @@ -0,0 +1,5 @@ +Base64Test = TestCase('Base64Test'); + +Base64Test.prototype.testEncodeDecode = function(){ +  assertEquals(Base64.decode(Base64.encode('hello')), 'hello'); +}; diff --git a/test/BinderTest.js b/test/BinderTest.js new file mode 100644 index 00000000..d033996d --- /dev/null +++ b/test/BinderTest.js @@ -0,0 +1,1001 @@ +BinderTest = TestCase('BinderTest'); + +function compile(content, initialScope, config) { +  var h = html(content); +  config = config || {autoSubmit:true}; +  var scope = new nglr.Scope(initialScope, "ROOT"); +  h.data('scope', scope); +  var binder = new nglr.Binder(h[0], new nglr.WidgetFactory(), new MockUrlWatcher(), config); +  var datastore = new nglr.DataStore(); +  scope.set("$datastore", datastore); +  scope.set("$binder", binder); +  scope.set("$anchor", binder.anchor); +  binder.entity(scope); +  binder.compile(); +  return {node:h, binder:binder, scope:scope}; +} + +function compileToHtml(content) { +  return compile(content).node.sortedHtml(); +} + + +BinderTest.prototype.testParseTextWithNoBindings = function(){ +  var parts = nglr.Binder.parseBindings("a"); +  assertEquals(parts.length, 1); +  assertEquals(parts[0], "a"); +  assertTrue(!nglr.Binder.binding(parts[0])); +}; + +BinderTest.prototype.testParseEmptyText = function(){ +  var parts = nglr.Binder.parseBindings(""); +  assertEquals(parts.length, 1); +  assertEquals(parts[0], ""); +  assertTrue(!nglr.Binder.binding(parts[0])); +}; + +BinderTest.prototype.testParseInnerBinding = function(){ +  var parts = nglr.Binder.parseBindings("a{{b}}c"); +  assertEquals(parts.length, 3); +  assertEquals(parts[0], "a"); +  assertTrue(!nglr.Binder.binding(parts[0])); +  assertEquals(parts[1], "{{b}}"); +  assertEquals(nglr.Binder.binding(parts[1]), "b"); +  assertEquals(parts[2], "c"); +  assertTrue(!nglr.Binder.binding(parts[2])); +}; + +BinderTest.prototype.testParseEndingBinding = function(){ +  var parts = nglr.Binder.parseBindings("a{{b}}"); +  assertEquals(parts.length, 2); +  assertEquals(parts[0], "a"); +  assertTrue(!nglr.Binder.binding(parts[0])); +  assertEquals(parts[1], "{{b}}"); +  assertEquals(nglr.Binder.binding(parts[1]), "b"); +}; + +BinderTest.prototype.testParseBeggingBinding = function(){ +  var parts = nglr.Binder.parseBindings("{{b}}c"); +  assertEquals(parts.length, 2); +  assertEquals(parts[0], "{{b}}"); +  assertEquals(nglr.Binder.binding(parts[0]), "b"); +  assertEquals(parts[1], "c"); +  assertTrue(!nglr.Binder.binding(parts[1])); +}; + +BinderTest.prototype.testParseLoanBinding = function(){ +  var parts = nglr.Binder.parseBindings("{{b}}"); +  assertEquals(parts.length, 1); +  assertEquals(parts[0], "{{b}}"); +  assertEquals(nglr.Binder.binding(parts[0]), "b"); +}; + +BinderTest.prototype.testParseTwoBindings = function(){ +  var parts = nglr.Binder.parseBindings("{{b}}{{c}}"); +  assertEquals(parts.length, 2); +  assertEquals(parts[0], "{{b}}"); +  assertEquals(nglr.Binder.binding(parts[0]), "b"); +  assertEquals(parts[1], "{{c}}"); +  assertEquals(nglr.Binder.binding(parts[1]), "c"); +}; + +BinderTest.prototype.testParseTwoBindingsWithTextInMiddle = function(){ +  var parts = nglr.Binder.parseBindings("{{b}}x{{c}}"); +  assertEquals(parts.length, 3); +  assertEquals(parts[0], "{{b}}"); +  assertEquals(nglr.Binder.binding(parts[0]), "b"); +  assertEquals(parts[1], "x"); +  assertTrue(!nglr.Binder.binding(parts[1])); +  assertEquals(parts[2], "{{c}}"); +  assertEquals(nglr.Binder.binding(parts[2]), "c"); +}; + +BinderTest.prototype.testParseMultiline = function(){ +  var parts = nglr.Binder.parseBindings('"X\nY{{A\nB}}C\nD"'); +  assertTrue(!!nglr.Binder.binding('{{A\nB}}')); +  assertEquals(parts.length, 3); +  assertEquals(parts[0], '"X\nY'); +  assertEquals(parts[1], '{{A\nB}}'); +  assertEquals(parts[2], 'C\nD"'); +}; + +BinderTest.prototype.testHasBinding = function(){ +  assertTrue(nglr.Binder.hasBinding("{{a}}")); +  assertTrue(!nglr.Binder.hasBinding("a")); +  assertTrue(nglr.Binder.hasBinding("{{b}}x{{c}}")); +}; + + +BinderTest.prototype.tearDown = function(){ +  jQuery("*", document).die(); +  jQuery(document).unbind(); +}; + +BinderTest.prototype.testChangingTextfieldUpdatesModel = function(){ +  var state = compile('<input type="text" name="model.price" value="abc">', {model:{}}); +  state.binder.updateView(); +  assertEquals('abc', state.scope.get('model').price); +}; + +BinderTest.prototype.testChangingTextareaUpdatesModel = function(){ +  var form = html('<textarea name="model.note">abc</textarea>'); +  var scope = new nglr.Scope({model:{}}); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertEquals(scope.get('model').note, 'abc'); +}; + +BinderTest.prototype.testChangingRadioUpdatesModel = function(){ +  var form = html('<input type="radio" name="model.price" value="A" checked>' + +        '<input type="radio" name="model.price" value="B">'); +  var scope = new nglr.Scope({model:{}}); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertEquals(scope.get('model').price, 'A'); +}; + +BinderTest.prototype.testChangingCheckboxUpdatesModel = function(){ +  var form = html('<input type="checkbox" name="model.price" value="A" checked>'); +  var scope = new nglr.Scope({model:{}}); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertEquals('A', scope.get('model').price); +}; + +BinderTest.prototype.testBindUpdate = function() { +  var c = compile('<div ng-eval="a=123"/>'); +  c.binder.updateView(); +  assertEquals(123, c.scope.get('a')); +}; + +BinderTest.prototype.testChangingSelectNonSelectedUpdatesModel = function(){ +  var form = html('<select name="model.price"><option value="A">A</option><option value="B">B</option></select>'); +  var scope = new nglr.Scope({model:{}}); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertEquals('A', scope.get('model').price); +}; + +BinderTest.prototype.testChangingMultiselectUpdatesModel = function(){ +  var form = html('<select name="Invoice.options" multiple="multiple">' + +          '<option value="A" selected>Gift wrap</option>' + +          '<option value="B" selected>Extra padding</option>' + +          '<option value="C">Expedite</option>' + +          '</select>'); +  var scope = new nglr.Scope({Invoice:{}}); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertJsonEquals(["A", "B"], scope.get('Invoice').options); +}; + +BinderTest.prototype.testChangingSelectSelectedUpdatesModel = function(){ +  var form = html('<select name="model.price"><option>A</option><option selected value="b">B</option></select>'); +  var scope = new nglr.Scope({model:{}}); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertEquals(scope.get('model').price, 'b'); +}; + +BinderTest.prototype.testExecuteInitialization = function() { +  var form = html('<div ng-init="a=123">'); +  var scope = new nglr.Scope(); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0)); +  binder.executeInit(); +  assertEquals(scope.get('a'), 123); +}; + +BinderTest.prototype.testExecuteInitializationStatements = function() { +  var form = html('<div ng-init="a=123;b=345">'); +  var scope = new nglr.Scope(); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0)); +  binder.executeInit(); +  assertEquals(scope.get('a'), 123); +  assertEquals(scope.get('b'), 345); +}; + +BinderTest.prototype.testApplyTextBindings = function(){ +  var form = html('<div ng-bind="model.a">x</div>'); +  var scope = new nglr.Scope({model:{a:123}}); +  form.data('scope', scope); +  var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertEquals('123', form.text()); +}; + +BinderTest.prototype.testReplaceBindingInTextWithSpan = function() { +  assertEquals(compileToHtml("<b>a{{b}}c</b>"), '<b>a<span ng-bind="b"></span>c</b>'); +  assertEquals(compileToHtml("<b>{{b}}</b>"), '<b><span ng-bind="b"></span></b>'); +}; + +BinderTest.prototype.testReplaceBindingCreatesCorrectNumberOfWidgets = function() { +  var h = html("space{{a}}<b>{{a}}a{{a}}</b>{{a}}"); +  h.data('scope', new nglr.Scope()); +  var binder = new nglr.Binder(h.get(0), new nglr.WidgetFactory()); +  binder.compile(); + +  assertEquals(4, h.scope().widgets.length); +}; + +BinderTest.prototype.testBindingSpaceConfusesIE = function() { +  if (!nglr.msie) return; +  var span = document.createElement("span"); +  span.innerHTML = ' '; +  var nbsp = span.firstChild.nodeValue; +  assertEquals( +      '<b><span ng-bind="a"></span><span>'+nbsp+'</span><span ng-bind="b"></span></b>', +      compileToHtml("<b>{{a}} {{b}}</b>")); +  assertEquals( +      '<span ng-bind="A"></span><span>'+nbsp+'x </span><span ng-bind="B"></span><span>'+nbsp+'(</span><span ng-bind="C"></span>', +      compileToHtml("{{A}} x {{B}} ({{C}})")); +}; + +BinderTest.prototype.testBindingOfAttributes = function() { +  var form = html("<a href='http://s/a{{b}}c' foo='x'></a>"); +  form.data('scope', new nglr.Scope()); +  var binder = new nglr.Binder(form.get(0)); +  binder.compile(); +  var attrbinding = form.find("a").attr("ng-bind-attr"); +  var bindings = nglr.fromJson(attrbinding); +  assertEquals("http://s/a{{b}}c", decodeURI(bindings.href)); +  assertTrue(!bindings.foo); +}; + +BinderTest.prototype.testMarkMultipleAttributes = function() { +  var form = html("<a href='http://s/a{{b}}c' foo='{{d}}'></a>"); +  form.data('scope', new nglr.Scope()); +  var binder = new nglr.Binder(form.get(0)); +  binder.compile(); +  var attrbinding = form.find("a").attr("ng-bind-attr"); +  var bindings = nglr.fromJson(attrbinding); +  assertEquals(decodeURI(bindings.href), "http://s/a{{b}}c"); +  assertEquals(bindings.foo, "{{d}}"); +}; + +BinderTest.prototype.testAttributesNoneBound = function() { +  var form = html("<a href='abc' foo='def'></a>"); +  form.data('scope', new nglr.Scope()); +  var binder = new nglr.Binder(form.get(0)); +  binder.compile(); +  var a = form.find("a"); +  assertEquals(a.get(0).nodeName, "A"); +  assertTrue(!a.attr("ng-bind-attr")); +}; + +BinderTest.prototype.testExistingAttrbindingIsAppended = function() { +  var form = html("<a href='http://s/{{abc}}' ng-bind-attr='{\"b\":\"{{def}}\"}'></a>"); +  form.data('scope', new nglr.Scope()); +  var binder = new nglr.Binder(form.get(0)); +  binder.compile(); +  var a = form.find("a"); +  assertEquals('{"b":"{{def}}","href":"http://s/{{abc}}"}', a.attr('ng-bind-attr')); +}; + +BinderTest.prototype.testAttributesAreEvaluated = function(){ +  var form = html('<a ng-bind-attr=\'{"a":"a", "b":"a+b={{a+b}}"}\'></a>'); +  form.data('scope', new nglr.Scope({a:1, b:2})); +  var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  var a = form.find("a"); +  assertEquals(a.attr('a'), 'a'); +  assertEquals(a.attr('b'), 'a+b=3'); +}; + +BinderTest.prototype.testInputsAreUpdated = function(){ +  var form = +     html('<input type="tEXt" name="A.text"/>' + +          '<textarea name="A.textarea"/>' + +          '<input name="A.radio" type="rADio" value="r"/>' + +          '<input name="A.radioOff" type="rADio" value="r"/>' + +          '<input name="A.checkbox" type="checkbox" value="c" />' + +          '<input name="A.checkboxOff" type="checkbox" value="c" />' + +          '<select name="A.select"><option>a</option><option value="S">b</option></select>'); +  var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); +  form.data('scope', new nglr.Scope({A:{text:"t1", textarea:"t2", radio:"r", checkbox:"c", select:"S"}})); +  binder.compile(); +  binder.updateView(); +  assertEquals(form.find("input[type=text]").attr('value'), 't1'); +  assertEquals(form.find("textarea").attr('value'), 't2'); +  assertTrue(form.find("input[name=A.radio]").attr('checked')); +  assertTrue(!form.find("input[name=A.radioOff]").attr('checked')); +  assertTrue(form.find("input[name=A.checkbox]").attr('checked')); +  assertTrue(!form.find("input[name=A.checkboxOff]").attr('checked')); +  assertEquals(form.find("select").attr('value'), 'S'); +  assertEquals(form.find("option[selected]").text(), 'b'); +}; + +BinderTest.prototype.testInputTypeButtonActionExecutesInScope =  function(){ +  var savedCalled = false; +  var c = compile('<input id="apply" type="button" ng-action="person.save()" value="Apply">'); +  c.scope.set("person.save", function(){ +    savedCalled = true; +  }); +  c.node.find("#apply").click(); +  assertTrue(savedCalled); +}; + +BinderTest.prototype.testInputTypeButtonActionExecutesInScope =  function(){ +  expectAsserts(1); +  var c = compile('<input id="apply" type="image" ng-action="action()">'); +  c.scope.set("action", function(){ +    assertTrue(true); +  }); +  c.node.find("#apply").click(); +}; + +BinderTest.prototype.testButtonElementActionExecutesInScope =  function(){ +  var savedCalled = false; +  var c = compile('<button id="apply" ng-action="person.save()">Apply</button>'); +  c.scope.set("person.save", function(){ +    savedCalled = true; +  }); +  c.node.find("#apply").click(); +  assertTrue(savedCalled); +}; + +BinderTest.prototype.testParseEmptyAnchor = function(){ +  var binder = new nglr.Binder(null, null, new MockUrlWatcher()); +  var anchor = binder.anchor; +  binder.parseAnchor("a#x=1"); +  assertEquals(1, binder.anchor.x); +  binder.parseAnchor("a#"); +  assertTrue("old values did not get removed", !binder.anchor.x); +  assertTrue("anchor gor replaced", anchor === binder.anchor); +  assertEquals('undefined', typeof (anchor[""])); +}; + +BinderTest.prototype.testParseAnchor = function(){ +  var binder = new nglr.Binder(null, null, new MockUrlWatcher()); +  binder.parseAnchor("a#x=1"); +  assertEquals(binder.anchor.x, "1"); +  binder.parseAnchor("a#a=b&c=%20&d"); +  assertEquals(binder.anchor.a, 'b'); +  assertEquals(binder.anchor.c, ' '); +  assertTrue(binder.anchor.d !== null); +  assertTrue(!binder.anchor.x); +}; + +BinderTest.prototype.testWriteAnchor = function(){ +  var binder = new nglr.Binder(null, null, new MockUrlWatcher()); +  binder.urlWatcher.setUrl('a'); +  binder.anchor.a = 'b'; +  binder.anchor.c = ' '; +  binder.anchor.d = true; +  binder.updateAnchor(); +  assertEquals(binder.urlWatcher.getUrl(), "a#a=b&c=%20&d"); +}; + +BinderTest.prototype.testWriteAnchorAsPartOfTheUpdateView = function(){ +  var binder = new nglr.Binder(html("<div/>")[0], null, new MockUrlWatcher()); +  binder.urlWatcher.setUrl('a'); +  $(binder.doc).data('scope', new nglr.Scope()); +  binder.anchor.a = 'b'; +  binder.updateView(); +  assertEquals(binder.urlWatcher.getUrl(), "a#a=b"); +}; + +BinderTest.prototype.testRepeaterUpdateBindings = function(){ +  var form = html('<ul><LI ng-repeat="item in model.items" ng-bind="item.a"/></ul>'); +  var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); +  var items = [{a:"A"}, {a:"B"}]; +  form.data('scope', new nglr.Scope({model:{items:items}})); +  binder.compile(); + +  binder.updateView(); +  assertEquals('<ul>' + +        '<#comment></#comment>' + +        '<li ng-bind="item.a" ng-repeat-index="0">A</li>' + +        '<li ng-bind="item.a" ng-repeat-index="1">B</li>' + +        '</ul>', form.sortedHtml()); + +  items.unshift({a:'C'}); +  binder.updateView(); +  assertEquals('<ul>' + +        '<#comment></#comment>' + +        '<li ng-bind="item.a" ng-repeat-index="0">C</li>' + +        '<li ng-bind="item.a" ng-repeat-index="1">A</li>' + +        '<li ng-bind="item.a" ng-repeat-index="2">B</li>' + +        '</ul>', form.sortedHtml()); + +  items.shift(); +  binder.updateView(); +  assertEquals('<ul>' + +        '<#comment></#comment>' + +        '<li ng-bind="item.a" ng-repeat-index="0">A</li>' + +        '<li ng-bind="item.a" ng-repeat-index="1">B</li>' + +        '</ul>', form.sortedHtml()); +}; + +BinderTest.prototype.testRepeaterContentDoesNotBind = function(){ +  var form = html('<ul><LI ng-repeat="item in model.items"><span ng-bind="item.a"/></li></ul>'); +  form.data('scope', new nglr.Scope({model:{items:[{a:"A"}]}})); +  var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); +  binder.compile(); +  binder.updateView(); +  assertEquals('<ul>' + +        '<#comment></#comment>' + +        '<li ng-repeat-index="0"><span ng-bind="item.a">A</span></li>' + +        '</ul>', form.sortedHtml()); +}; + +BinderTest.prototype.testShouldBindActionsOnRepeaterClone = function(){ +  var c = compile('<a ng-repeat="item in items" href="#" ng-action="result.value = item">link</a>'); +  jQuery(c).die(); +  c.scope.set('result.value', false); +  c.scope.set('items', ['abc', 'xyz']); +  c.scope.updateView(); +  assertEquals(2, c.node.find("a").size()); +  c.node.find("a:last").click(); +  assertEquals('xyz', c.scope.get('result.value')); +}; + + + +BinderTest.prototype.testRepeaterInputContentDoesNotBind =  function(){ +  var form = +    html('<ul><LI repeater="item in model.items">' + +          '<input type="text" name="item.a" value="OLD"/></li></ul>'); +  var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); +  var items = [{a:"A"}]; +  form.data('scope', new nglr.Scope({model:{items:items}})); + +  assertEquals(form.find(":input").attr("value"), "OLD"); +}; + +BinderTest.prototype.testExpandEntityTag = function(){ +  assertEquals( +      '<div ng-entity="Person" ng-watch="$anchor.a:1"></div>', +      compileToHtml('<div ng-entity="Person" ng-watch="$anchor.a:1"/>')); +}; + +BinderTest.prototype.testExpandEntityTagWithDefaults = function(){ +  assertEquals( +      '<div ng-entity="Person:{a:\"a\"}" ng-watch=""></div>', +      compileToHtml('<div ng-entity=\'Person:{a:"a"}\'/>')); +}; + +BinderTest.prototype.testExpandEntityTagWithName = function(){ +  var c = compile('<div ng-entity="friend=Person"/>'); +  assertEquals( +      '<div ng-entity="friend=Person" ng-watch="$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};"></div>', +      c.node.sortedHtml()); +  assertEquals("Person", c.scope.get("friend.$entity")); +  assertEquals("friend", c.scope.get("friend.$$anchor")); +}; + +BinderTest.prototype.testExpandSubmitButtonToAction = function(){ +  var html = compileToHtml('<input type="submit" value="Save">'); +  assertTrue(html, html.indexOf('ng-action="$save()"') > 0 ); +  assertTrue(html, html.indexOf('ng-bind-attr="{"disabled":"{{$invalidWidgets}}"}"') > 0 ); +}; + +BinderTest.prototype.testDoNotOverwriteCustomAction = function(){ +  var html = compileToHtml('<input type="submit" value="Save" action="foo();">'); +  assertTrue(html.indexOf('action="foo();"') > 0 ); +}; + +BinderTest.prototype.testReplaceFileUploadWithSwf = function(){ +  expectAsserts(1); +  var form = jQuery("body").append('<div id="testTag"><input type="file"></div>'); +  form.data('scope', new nglr.Scope()); +  var factory = {}; +  var binder = new nglr.Binder(form.get(0), factory, new MockUrlWatcher()); +  factory.createController = function(node){ +    assertEquals(node.attr('type'), 'file'); +    return {updateModel:function(){}}; +  }; +  binder.compile(); +  jQuery("#testTag").remove(); +}; + +BinderTest.prototype.testRepeaterAdd = function(){ +  var doc = $('<div><input type="text" name="item.x" ng-repeat="item in items"></div>'); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  doc.data('scope', new nglr.Scope({items:[{x:'a'}, {x:'b'}], $binder:binder})); +  binder.compile(); +  binder.updateView(); +  assertEquals('a', doc.find(':input')[0].value); +  assertEquals('b', doc.find(':input')[1].value); + +  var first = doc.find('[ng-repeat-index="0"]'); +  first[0].value = 'ABC'; +  first.trigger('keyup'); +  assertEquals(doc.scope().get('items')[0].x, 'ABC'); +}; + +BinderTest.prototype.testIfTextBindingThrowsErrorDecorateTheSpan = function(){ +  var doc = $('<div>{{error.throw()}}</div>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('error.throw', function(){throw "ErrorMsg1";}); +  binder.updateView(); +  var span = doc.find('span'); +  assertTrue(span.hasClass('ng-exception')); +  assertEquals('ErrorMsg1', nglr.fromJson(span.text())); +  assertEquals('"ErrorMsg1"', span.attr('ng-error')); + +  scope.set('error.throw', function(){throw "MyError";}); +  binder.updateView(); +  span = doc.find('span'); +  assertTrue(span.hasClass('ng-exception')); +  assertTrue(span.text(), span.text().match('MyError') !== null); +  assertEquals('"MyError"', span.attr('ng-error')); + +  scope.set('error.throw', function(){return "ok";}); +  binder.updateView(); +  assertFalse(span.hasClass('ng-exception')); +  assertEquals('ok', span.text()); +  assertEquals(null, span.attr('ng-error')); +}; + +BinderTest.prototype.testIfAttrBindingThrowsErrorDecorateTheSpan = function(){ +  var doc = $('<div attr="before {{error.throw()}} after"/>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('error.throw', function(){throw "ErrorMsg";}); +  binder.updateView(); +  assertTrue('ng-exception', doc.hasClass('ng-exception')); +  assertEquals('before ["ErrorMsg"] after', doc.attr('attr')); +  assertEquals('"ErrorMsg"', doc.attr('ng-error')); + +  scope.set('error.throw', function(){ return 'X';}); +  binder.updateView(); +  assertFalse('!ng-exception', doc.hasClass('ng-exception')); +  assertEquals('before X after', doc.attr('attr')); +  assertEquals(null, doc.attr('ng-error')); +}; + +BinderTest.prototype.testNestedRepeater = function() { +  var doc = html('<div ng-repeat="m in model" name="{{m.name}}">' + +                   '<ul name="{{i}}" ng-repeat="i in m.item"></ul>' + +                 '</div>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('model', [{name:'a', item:['a1', 'a2']}, {name:'b', item:['b1', 'b2']}]); +  binder.updateView(); + +  assertEquals( +      //'<#comment></#comment>'+ +      '<div name="a" ng-bind-attr="{"name":"{{m.name}}"}" ng-repeat-index="0">'+ +        '<#comment></#comment>'+ +        '<ul name="a1" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="0"></ul>'+ +        '<ul name="a2" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="1"></ul>'+ +      '</div>'+ +      '<div name="b" ng-bind-attr="{"name":"{{m.name}}"}" ng-repeat-index="1">'+ +        '<#comment></#comment>'+ +        '<ul name="b1" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="0"></ul>'+ +        '<ul name="b2" ng-bind-attr="{"name":"{{i}}"}" ng-repeat-index="1"></ul>'+ +      '</div>', doc.sortedHtml()); +}; + +BinderTest.prototype.testRadioButtonGetsPrefixed = function () { +  var doc = html('<input ng-repeat="m in model" type="radio" name="m.a" value="on"/>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('model', ['a1', 'a2']); +  binder.updateView(); + +  assertEquals( +      //'<#comment></#comment>'+ +      '<input name="0:m.a" ng-repeat-index="0" type="radio" value="on"></input>'+ +      '<input name="1:m.a" ng-repeat-index="1" type="radio" value="on"></input>', +      doc.sortedHtml()); +}; + +BinderTest.prototype.testHideBindingExpression = function() { +  var doc = html('<div ng-hide="hidden == 3"/>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('hidden', 3); +  binder.updateView(); + +  assertHidden(doc.children()); + +  scope.set('hidden', 2); +  binder.updateView(); + +  assertVisible(doc.children()); +}; + +BinderTest.prototype.testHideBinding = function() { +  var doc = html('<div ng-hide="hidden"/>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('hidden', 'true'); +  binder.updateView(); + +  assertHidden(doc.children()); + +  scope.set('hidden', 'false'); +  binder.updateView(); + +  assertVisible(doc.children()); + +  scope.set('hidden', ''); +  binder.updateView(); + +  assertVisible(doc.children()); +}; + +BinderTest.prototype.testShowBinding = function() { +  var doc = html('<div ng-show="show"/>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('show', 'true'); +  binder.updateView(); + +  assertVisible(doc.children()); + +  scope.set('show', 'false'); +  binder.updateView(); + +  assertHidden(doc.children()); + +  scope.set('show', ''); +  binder.updateView(); + +  assertHidden(doc.children()); +}; + +BinderTest.prototype.testBindClassUndefined = function() { +  var doc = compile('<div ng-class="undefined"/>'); +  doc.binder.updateView(); + +  assertEquals( +      '<div ng-class="undefined"></div>', +      doc.node.sortedHtml()); +}; + +BinderTest.prototype.testBindClass = function() { +  var doc = html('<div ng-class="class"/>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.set('class', 'testClass'); +  binder.updateView(); + +  assertEquals(doc.sortedHtml(), +      '<div class="testClass" ng-class="class"></div>'); + +  scope.set('class', ['a', 'b']); +  binder.updateView(); + +  assertEquals(doc.sortedHtml(), +      '<div class="a,b" ng-class="class"></div>'); +}; + +BinderTest.prototype.testBindClassEvenOdd = function() { +  var x = compile('<div ng-repeat="i in [0,1]" ng-class-even="\'e\'" ng-class-odd="\'o\'"/>'); +  x.binder.updateView(); +  assertEquals( +      '<div class="o" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat-index="0"></div>' + +      '<div class="e" ng-class-even="\'e\'" ng-class-odd="\'o\'" ng-repeat-index="1"></div>', +      x.node.sortedHtml()); +}; + +BinderTest.prototype.testBindStyle = function() { +  var doc = html('<div ng-style="style"/>'); +  var scope = new nglr.Scope(); +  doc.data('scope', scope); +  var binder = new nglr.Binder(doc[0], new nglr.WidgetFactory(), new MockUrlWatcher()); +  binder.compile(); + +  scope.eval('style={color:"red"}'); +  binder.updateView(); + +  assertEquals("red", doc.find('div').css('color')); + +  scope.eval('style={}'); +  binder.updateView(); + +  assertEquals(doc.sortedHtml(), '<div ng-style="style"></div>'); +}; + +BinderTest.prototype.testActionOnAHrefThrowsError = function(){ +  var model = {books:[]}; +  var state = compile('<a ng-action="throw {a:\'abc\', b:2};">Add Phone</a>', model); +  var input = state.node.find('a'); +  input.click(); +  assertEquals('abc', nglr.fromJson(input.attr('ng-error')).a); +  assertNotNull(input.data('qtip')); +  assertTrue("should have an error class", input.hasClass('ng-exception')); + +  input.attr('ng-action', '0'); +  input.click(); +  assertFalse('error class should be cleared', input.hasClass('ng-exception')); +}; + +BinderTest.prototype.testShoulIgnoreVbNonBindable = function(){ +  var c = compile("{{a}}" + +      "<div ng-non-bindable>{{a}}</div>" + +      "<div ng-non-bindable=''>{{b}}</div>" + +      "<div ng-non-bindable='true'>{{c}}</div>"); +  c.scope.set('a', 123); +  c.scope.updateView(); +  assertEquals('123{{a}}{{b}}{{c}}', c.node.text()); +}; + +BinderTest.prototype.testOptionShouldUpdateParentToGetProperBinding = function() { +  var c = compile('<select name="s"><option ng-repeat="i in [0,1]" value="{{i}}" ng-bind="i"></option></select>'); +  c.scope.set('s', 1); +  c.binder.updateView(); +  assertEquals(1, c.node.find('select')[0].selectedIndex); +}; + +BinderTest.prototype.testRepeaterShouldBindInputsDefaults = function () { +  var c = compile('<input value="123" name="item.name" ng-repeat="item in items">'); +  c.scope.set('items', [{}, {name:'misko'}]); +  c.binder.updateView(); + +  assertEquals("123", c.scope.eval('items[0].name')); +  assertEquals("misko", c.scope.eval('items[1].name')); +}; + +BinderTest.prototype.testRepeaterShouldCreateArray = function () { +  var c = compile('<input value="123" name="item.name" ng-repeat="item in items">'); +  c.binder.updateView(); + +  assertEquals(0, c.scope.get('items').length); +}; + +BinderTest.prototype.testShouldTemplateBindPreElements = function () { +  var c = compile('<pre>Hello {{name}}!</pre>'); +  c.scope.set("name", "World"); +  c.binder.updateView(); + +  assertEquals('<pre ng-bind-template="Hello {{name}}!">Hello World!</pre>', c.node.sortedHtml()); +}; + +BinderTest.prototype.testDissableAutoSubmit = function() { +  var c = compile('<input type="submit" value="S"/>', null, {autoSubmit:true}); +  assertEquals( +      '<input ng-action="$save()" ng-bind-attr="{"disabled":"{{$invalidWidgets}}"}" type="submit" value="S"></input>', +      c.node.sortedHtml()); + +  c = compile('<input type="submit" value="S"/>', null, {autoSubmit:false}); +  assertEquals( +      '<input type="submit" value="S"></input>', +      c.node.sortedHtml()); +}; + +BinderTest.prototype.testSettingAnchorToNullOrUndefinedRemovesTheAnchorFromURL = function() { +  var c = compile(''); +  c.binder.urlWatcher.setUrl("http://server/#a=1&b=2"); +  c.binder.parseAnchor(); +  assertEquals('1', c.binder.anchor.a); +  assertEquals('2', c.binder.anchor.b); + +  c.binder.anchor.a = null; +  c.binder.anchor.b = null; +  c.binder.updateAnchor(); +  assertEquals('http://server/#', c.binder.urlWatcher.getUrl()); +}; + +BinderTest.prototype.testFillInOptionValueWhenMissing = function() { +  var c = compile('<select><option selected="true">A</option><option value="">B</option></select>'); +  assertEquals( +      '<select><option selected="true" value="A">A</option><option>B</option></select>', +      c.node.sortedHtml()); +}; + +BinderTest.prototype.testValidateForm = function() { +  var c = compile('<input name="name" ng-required>' + +      '<div ng-repeat="item in items"><input name="item.name" ng-required/></div>'); +  var items = [{}, {}]; +  c.scope.set("items", items); +  c.binder.updateView(); +  assertEquals(3, c.scope.get("$invalidWidgets.length")); + +  c.scope.set('name', 'abc'); +  c.binder.updateView(); +  assertEquals(2, c.scope.get("$invalidWidgets.length")); + +  items[0].name = 'abc'; +  c.binder.updateView(); +  assertEquals(1, c.scope.get("$invalidWidgets.length")); + +  items[1].name = 'abc'; +  c.binder.updateView(); +  assertEquals(0, c.scope.get("$invalidWidgets.length")); +}; + +BinderTest.prototype.testDeleteAttributeIfEvaluatesFalse = function() { +  var c = compile( +      '<input name="a0" ng-bind-attr="{disabled:\'{{true}}\'}"><input name="a1" ng-bind-attr="{disabled:\'{{false}}\'}">' + +      '<input name="b0" ng-bind-attr="{disabled:\'{{1}}\'}"><input name="b1" ng-bind-attr="{disabled:\'{{0}}\'}">' + +      '<input name="c0" ng-bind-attr="{disabled:\'{{[0]}}\'}"><input name="c1" ng-bind-attr="{disabled:\'{{[]}}\'}">'); +  c.binder.updateView(); +  var html = c.node.html(); +  assertEquals(html + 0, 1, c.node.find("input[name='a0']:disabled").size()); +  assertEquals(html + 1, 1, c.node.find("input[name='b0']:disabled").size()); +  assertEquals(html + 2, 1, c.node.find("input[name='c0']:disabled").size()); + +  assertEquals(html + 3, 0, c.node.find("input[name='a1']:disabled").size()); +  assertEquals(html + 4, 0, c.node.find("input[name='b1']:disabled").size()); +  assertEquals(html + 5, 0, c.node.find("input[name='c1']:disabled").size()); +}; + +BinderTest.prototype.testRepeaterErrorShouldBePlacedOnInstanceNotOnTemplateComment = function () { +  var c = compile( +    '<input name="person.{{name}}" ng-repeat="name in [\'a\', \'b\']" />'); +  c.binder.updateView(); +  assertTrue(c.node.find("input").hasClass("ng-exception")); +}; + +BinderTest.prototype.XtestItShouldApplyAttirbutesBeforeTheWidgetsAreMaterialized = function() { +  var c = compile( +      '<input name="person.{{name}}" ng-repeat="name in [\'a\', \'b\']" />'); +  c.scope.set('person', {a:'misko', b:'adam'}); +  c.binder.updateView(); +  assertEquals("", c.node.html()); +}; + +BinderTest.prototype.testItShouldCallListenersWhenAnchorChanges = function() { +  var log = ""; +  var c = compile('<div ng-watch="$anchor.counter:count = count+1">'); +  c.scope.set("count", 0); +  c.scope.addWatchListener("$anchor.counter", function(newValue, oldValue){ +    log += oldValue + "->" + newValue + ";"; +  }); +  assertEquals(0, c.scope.get("count")); +  c.binder.onUrlChange("#counter=1"); +  assertEquals(1, c.scope.get("count")); +  c.binder.onUrlChange("#counter=1"); +  assertEquals(1, c.scope.get("count")); +  c.binder.onUrlChange("#counter=2"); +  assertEquals(2, c.scope.get("count")); +  c.binder.onUrlChange("#counter=2"); +  assertEquals(2, c.scope.get("count")); +  c.binder.onUrlChange("#"); +  assertEquals("undefined->1;1->2;2->undefined;", log); +  assertEquals(3, c.scope.get("count")); +}; + +BinderTest.prototype.testParseQueryString = function(){ +  var binder = new nglr.Binder(); +  assertJsonEquals({"a":"1"}, binder.parseQueryString("a=1")); +  assertJsonEquals({"a":"1", "b":"two"}, binder.parseQueryString("a=1&b=two")); +  assertJsonEquals({}, binder.parseQueryString("")); + +  assertJsonEquals({"a":"1", "b":""}, binder.parseQueryString("a=1&b=")); +  assertJsonEquals({"a":"1", "b":""}, binder.parseQueryString("a=1&b")); +  assertJsonEquals({"a":"1", "b":" 2 "}, binder.parseQueryString("a=1&b=%202%20")); +  assertJsonEquals({"a a":"1", "b":"2"}, binder.parseQueryString("a%20a=1&b=2")); + +}; + +BinderTest.prototype.testSetBinderAnchorTriggersListeners = function(){ +  expectAsserts(2); +  var doc = html("<div/>")[0]; +  var binder = new nglr.Binder(doc, null, new MockUrlWatcher()); +  var scope = new nglr.Scope({$binder:binder, $anchor:binder.anchor}); +  jQuery(doc).data('scope', scope); + +  scope.addWatchListener("$anchor.name", function(newVal, oldVal) { +    assertEquals("new", newVal); +    assertEquals(undefined, oldVal); +  }); + +  binder.anchor.name = "new"; +  binder.onUrlChange("http://base#name=new"); +}; + +BinderTest.prototype.testItShouldDisplayErrorWhenActionIsSyntacticlyIncorect = function(){ +  var c = compile( +      '<input type="button" ng-action="greeting=\'ABC\'"/>' + +      '<input type="button" ng-action=":garbage:"/>'); +  c.node.find("input").click(); +  assertEquals("ABC", c.scope.get('greeting')); +  assertTrue(c.node.find(":input:last").hasClass("ng-exception")); +}; + +BinderTest.prototype.testItShouldSelectTheCorrectRadioBox = function() { +  var c = compile( +      '<input type="radio" name="sex" value="female"/>' + +      '<input type="radio" name="sex" value="male"/>'); + +  c.node.find("input[value=female]").click(); +  assertEquals("female", c.scope.get("sex")); +  assertEquals(1, c.node.find("input:checked").size()); +  assertEquals("female", c.node.find("input:checked").attr("value")); + +  c.node.find("input[value=male]").click(); +  assertEquals("male", c.scope.get("sex")); +  assertEquals(1, c.node.find("input:checked").size()); +  assertEquals("male", c.node.find("input:checked").attr("value")); +}; + +BinderTest.prototype.testItShouldListenOnRightScope = function() { +  var c = compile( +      '<div ng-init="counter=0; gCounter=0" ng-watch="w:counter=counter+1">' + +      '<div ng-repeat="n in [1,2,4]" ng-watch="w:counter=counter+1;w:$root.gCounter=$root.gCounter+n"/>'); +  c.binder.executeInit(); +  c.binder.updateView(); +  assertEquals(0, c.scope.get("counter")); +  assertEquals(0, c.scope.get("gCounter")); + +  c.scope.set("w", "something"); +  c.binder.updateView(); +  assertEquals(1, c.scope.get("counter")); +  assertEquals(7, c.scope.get("gCounter")); +}; + +BinderTest.prototype.testItShouldRepeatOnHashes = function() { +  var x = compile('<div ng-repeat="(k,v) in {a:0,b:1}" ng-bind=\"k + v\"></div>'); +  x.binder.updateView(); +  assertEquals( +      '<div ng-bind=\"k + v\" ng-repeat-index="0">a0</div>' + +      '<div ng-bind=\"k + v\" ng-repeat-index="1">b1</div>', +      x.node.sortedHtml()); +}; + +BinderTest.prototype.testItShouldFireChangeListenersBeforeUpdate = function(){ +  var x = compile('<div ng-bind="name"></div>'); +  x.scope.set("name", ""); +  x.scope.set("watched", "change"); +  x.scope.watch("watched:name=123"); +  x.scope.updateView(); +  assertEquals(123, x.scope.get("name")); +  assertEquals( +      '<div ng-bind="name">123</div>', +      x.node.sortedHtml()); +}; + +BinderTest.prototype.testItShouldHandleMultilineBindings = function(){ +  var x = compile('<div>{{\n 1 \n + \n 2 \n}}</div>'); +  x.scope.updateView(); +  assertEquals("3", x.node.text()); +}; + +BinderTest.prototype.testItBindHiddenInputFields = function(){ +  var x = compile('<input type="hidden" name="myName" value="abc" />'); +  x.scope.updateView(); +  assertEquals("abc", x.scope.get("myName")); +}; + +BinderTest.prototype.testItShouldRenderMultiRootHtmlInBinding = function() { +  var x = compile('<div>before {{a|html}}after</div>'); +  x.scope.set("a", "a<b>c</b>d"); +  x.binder.updateView(); +  assertEquals( +      '<div>before <span ng-bind="a|html">a<b>c</b>d</span>after</div>',  +      x.node.sortedHtml()); +}; diff --git a/test/ConsoleTest.js b/test/ConsoleTest.js new file mode 100644 index 00000000..56e223bd --- /dev/null +++ b/test/ConsoleTest.js @@ -0,0 +1,13 @@ +ConsoleTest = TestCase('ConsoleTest'); + +ConsoleTest.prototype.testConsoleWrite = function(){ +  var consoleNode = $("<div></div>")[0]; +  nglr.consoleNode = consoleNode; +  nglr.consoleLog("error", ["Hello", "world"]); +  assertEquals($(consoleNode)[0].nodeName, 'DIV'); +  assertEquals($(consoleNode).text(), 'Hello world'); +  assertEquals($('div', consoleNode)[0].className, 'error'); +  nglr.consoleLog("error",["Bye"]); +  assertEquals($(consoleNode).text(), 'Hello worldBye'); +  nglr.consoleNode = null; +};
\ No newline at end of file diff --git a/test/ControlBarTest.js b/test/ControlBarTest.js new file mode 100644 index 00000000..c914c8ff --- /dev/null +++ b/test/ControlBarTest.js @@ -0,0 +1,2 @@ +ControlBarTest = TestCase("ControlBarTest"); + diff --git a/test/DataStoreTest.js b/test/DataStoreTest.js new file mode 100644 index 00000000..9fe6c3df --- /dev/null +++ b/test/DataStoreTest.js @@ -0,0 +1,617 @@ +DataStoreTest = TestCase('DataStoreTest'); + +DataStoreTest.prototype.testSavePostsToServer = function(){ +  expectAsserts(10); +  var response; +  var post = function(data, callback){ +    var method = data[0][0]; +    var posted = data[0][2]; +    assertEquals("POST", method); +    assertEquals("abc", posted.$entity); +    assertEquals("123", posted.$id); +    assertEquals("1", posted.$version); +    assertFalse('function' == typeof posted.save); +    response = nglr.fromJson(nglr.toJson(posted)); +    response.$entity = "abc"; +    response.$id = "123"; +    response.$version = "2"; +    callback(200, [response]); +  }; +  var model; +  var datastore = new nglr.DataStore(post); +  model = datastore.entity('abc', {name: "value"})(); +  model.$id = "123"; +  model.$version = "1"; + +  datastore.save(model, function(obj){ +    assertTrue(obj === model); +    assertEquals(obj.$entity, "abc"); +    assertEquals(obj.$id, "123"); +    assertEquals(obj.$version, "2"); +    assertEquals(obj.name, "value"); +    obj.after = true; +  }); +  datastore.flush(); +}; + +DataStoreTest.prototype.testLoadGetsFromServer = function(){ +  expectAsserts(12); +  var post = function(data, callback){ +      var method = data[0][0]; +      var path = data[0][1]; +      assertEquals("GET", method); +      assertEquals("abc/1", path); +      response = [{$entity:'abc', $id:'1', $version:'2', key:"value"}]; +      callback(200, response); +    }; +  var datastore = new nglr.DataStore(post); + +  var model = datastore.entity("abc", {merge:true})(); +  assertEquals(datastore.load(model, '1', function(obj){ +    assertEquals(obj.$entity, "abc"); +    assertEquals(obj.$id, "1"); +    assertEquals(obj.$version, "2"); +    assertEquals(obj.key, "value"); +  }), model); +  datastore.flush(); +  assertEquals(model.$entity, "abc"); +  assertEquals(model.$id, "1"); +  assertEquals(model.$version, "2"); +  assertEquals(model.key, "value"); +  assertEquals(model.merge, true); +}; + +DataStoreTest.prototype.testRemove = function(){ +  expectAsserts(8); +  var response; +  var post = function(data, callback){ +    var method = data[0][0]; +    var posted = data[0][2]; +    assertEquals("DELETE", method); +    assertEquals("abc", posted.$entity); +    assertEquals("123", posted.$id); +    assertEquals("1", posted.$version); +    assertFalse('function' == typeof posted.save); +    response = nglr.fromJson(nglr.toJson(posted)); +    response.$entity = "abc"; +    response.$id = "123"; +    response.$version = "2"; +    callback(200, [response]); +  }; +  var model; +  var datastore = new nglr.DataStore(post); +  model = datastore.entity('abc', {name: "value"})(); +  model.$id = "123"; +  model.$version = "1"; + +  datastore.remove(model, function(obj){ +    assertEquals(obj.$id, "123"); +    assertEquals(obj.$version, "2"); +    assertEquals(obj.name, "value"); +    obj.after = true; +  }); +  datastore.flush(); + +}; + + +DataStoreTest.prototype.test401ResponseDoesNotCallCallback = function(){ +  expectAsserts(1); +  var post = function(data, callback) { +    callback(200, {$status_code: 401}); +  }; + +  var datastore = new nglr.DataStore(post, {login:function(){ +    assertTrue(true); +  }}); + +  var onLoadAll = function(){ +    assertTrue(false, "onLoadAll should not be called when response is status 401"); +  }; +  datastore.bulkRequest.push({}); +  datastore.flush(); +  datastore.loadAll({type: "A"}, onLoadAll); +}; + +DataStoreTest.prototype.test403ResponseDoesNotCallCallback = function(){ +  expectAsserts(1); +  var post = function(data, callback) { +    callback(200, [{$status_code: 403}]); +  }; + +  var datastore = new nglr.DataStore(post, {notAuthorized:function(){ +    assertTrue(true); +  }}); + +  var onLoadAll = function(){ +    assertTrue(false, "onLoadAll should not be called when response is status 403"); +  }; +  datastore.bulkRequest.push({}); +  datastore.flush(); +  datastore.loadAll({type: "A"}, onLoadAll); +}; + +DataStoreTest.prototype.testLoadCalledWithoutIdShouldBeNoop = function(){ +  expectAsserts(2); +  var post = function(url, callback){ +    assertTrue(false); +  }; +  var datastore = new nglr.DataStore(post); +  var model = datastore.entity("abc")(); +  assertEquals(datastore.load(model, undefined), model); +  assertEquals(model.$entity, "abc"); +}; + +DataStoreTest.prototype.testEntityFactory = function(){ +  var ds = new nglr.DataStore(); +  var Recipe = ds.entity("Recipe", {a:1, b:2}); +  assertEquals(Recipe.title, "Recipe"); +  assertEquals(Recipe.defaults.a, 1); +  assertEquals(Recipe.defaults.b, 2); + +  var recipe = Recipe(); +  assertEquals(recipe.$entity, "Recipe"); +  assertEquals(recipe.a, 1); +  assertEquals(recipe.b, 2); + +  recipe = new Recipe(); +  assertEquals(recipe.$entity, "Recipe"); +  assertEquals(recipe.a, 1); +  assertEquals(recipe.b, 2); +}; + +DataStoreTest.prototype.testEntityFactoryNoDefaults = function(){ +  var ds = new nglr.DataStore(); +  var Recipe = ds.entity("Recipe"); +  assertEquals(Recipe.title, "Recipe"); + +  recipe = new Recipe(); +  assertEquals(recipe.$entity, "Recipe"); +}; + +DataStoreTest.prototype.testEntityFactoryWithInitialValues = function(){ +  var ds = new nglr.DataStore(); +  var Recipe = ds.entity("Recipe"); + +  var recipe = Recipe({name: "name"}); +  assertEquals("name", recipe.name); +}; + +DataStoreTest.prototype.testEntityLoad = function(){ +  var ds = new nglr.DataStore(); +  var Recipe = ds.entity("Recipe", {a:1, b:2}); +  ds.load = function(instance, id, callback){ +    callback.apply(instance); +    return instance; +  }; +  var instance = null; +  var recipe2 = Recipe.load("ID", function(){ +    instance = this; +  }); +  assertTrue(recipe2 === instance); +}; + +DataStoreTest.prototype.testSaveScope = function(){ +  var ds = new nglr.DataStore(); +  var log = ""; +  var Person = ds.entity("Person"); +  var person1 = Person({name:"A", $entity:"Person", $id:"1", $version:"1"}, ds); +  person1.$$anchor = "A"; +  var person2 = Person({name:"B", $entity:"Person", $id:"2", $version:"2"}, ds); +  person2.$$anchor = "B"; +  var anchor = {}; +  ds.anchor = anchor; +  ds._jsonRequest = function(request, callback){ +    log += "save(" + request[2].$id + ");"; +    callback({$id:request[2].$id}); +  }; +  ds.saveScope({person1:person1, person2:person2, +        ignoreMe:{name: "ignore", save:function(callback){callback();}}}, function(){ +    log += "done();"; +  }); +  assertEquals("save(1);save(2);done();", log); +  assertEquals(1, anchor.A); +  assertEquals(2, anchor.B); +}; + +DataStoreTest.prototype.testEntityLoadAllRows = function(){ +  var ds = new nglr.DataStore(); +  var Recipe = ds.entity("Recipe"); +  var list = []; +  ds.loadAll = function(entity, callback){ +    assertTrue(Recipe === entity); +    callback.apply(list); +    return list; +  }; +  var items = Recipe.all(function(){ +    assertTrue(list === this); +  }); +  assertTrue(items === list); +}; + +DataStoreTest.prototype.testLoadAll = function(){ +  expectAsserts(8); +  var post = function(data, callback){ +    assertEquals("GET", data[0][0]); +    assertEquals("A", data[0][1]); +    callback(200, [[{$entity:'A', $id:'1'},{$entity:'A', $id:'2'}]]); +  }; +  var datastore = new nglr.DataStore(post); +  var list = datastore.entity("A").all(function(){ +    assertTrue(true); +  }); +  datastore.flush(); +  assertEquals(list.length, 2); +  assertEquals(list[0].$entity, "A"); +  assertEquals(list[0].$id, "1"); +  assertEquals(list[1].$entity, "A"); +  assertEquals(list[1].$id, "2"); +}; + +DataStoreTest.prototype.testQuery = function(){ +  expectAsserts(5); +  var post = function(data, callback) { +    assertEquals("GET", data[0][0]); +    assertEquals("Employee/managerId=123abc", data[0][1]); +    callback(200, [[{$entity:"Employee", $id: "456", managerId: "123ABC"}]]); + +  }; +  var datastore = new nglr.DataStore(post); +  var Employee = datastore.entity("Employee"); +  var list = Employee.query('managerId', "123abc", function(){ +    assertTrue(true); +  }); +  datastore.flush(); +  assertJsonEquals([[{$entity:"Employee", $id: "456", managerId: "123ABC"}]], datastore._cache.$collections); +  assertEquals(list[0].$id, "456"); +}; + +DataStoreTest.prototype.testLoadingDocumentRefreshesExistingArrays = function() { +  expectAsserts(12); +  var post; +  var datastore = new nglr.DataStore(function(r, c){post(r,c);}); +  var Book = datastore.entity('Book'); +  post = function(req, callback) { +    callback(200, [[{$id:1, $entity:"Book", name:"Moby"}, +                    {$id:2, $entity:"Book", name:"Dick"}]]); +  }; +  var allBooks = Book.all(); +  datastore.flush(); +  var queryBooks = Book.query("a", "b"); +  datastore.flush(); +  assertEquals("Moby", allBooks[0].name); +  assertEquals("Dick", allBooks[1].name); +  assertEquals("Moby", queryBooks[0].name); +  assertEquals("Dick", queryBooks[1].name); + +  post = function(req, callback) { +    assertEquals('[["GET","Book/1"]]', nglr.toJson(req)); +    callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]); +  }; +  var book = Book.load(1); +  datastore.flush(); +  assertEquals("Moby Dick", book.name); +  assertEquals("Moby Dick", allBooks[0].name); +  assertEquals("Moby Dick", queryBooks[0].name); + +  post = function(req, callback) { +    assertEquals('POST', req[0][0]); +    callback(200, [{$id:1, $entity:"Book", name:"The Big Fish"}]); +  }; +  book.$save(); +  datastore.flush(); +  assertEquals("The Big Fish", book.name); +  assertEquals("The Big Fish", allBooks[0].name); +  assertEquals("The Big Fish", queryBooks[0].name); +}; + +DataStoreTest.prototype.testEntityProperties = function() { +  expectAsserts(2); +  var datastore = new nglr.DataStore(); +  var callback = {}; + +  datastore._jsonRequest = function(request, callbackFn) { +    assertJsonEquals(["GET", "Cheese/$properties"], request); +    assertEquals(callback, callbackFn); +  }; + +  var Cheese = datastore.entity("Cheese"); +  Cheese.properties(callback); + +}; + +DataStoreTest.prototype.testLoadInstanceIsNotFromCache = function() { +  var post; +  var datastore = new nglr.DataStore(function(r, c){post(r,c);}); +  var Book = datastore.entity('Book'); + +  post = function(req, callback) { +    assertEquals('[["GET","Book/1"]]', nglr.toJson(req)); +    callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]); +  }; +  var book = Book.load(1); +  datastore.flush(); +  assertEquals("Moby Dick", book.name); +  assertFalse(book === datastore._cache['Book/1']); +}; + +DataStoreTest.prototype.testLoadStarsIsNewDocument = function() { +  var datastore = new nglr.DataStore(); +  var Book = datastore.entity('Book'); +  var book = Book.load('*'); +  assertEquals('Book', book.$entity); +}; + +DataStoreTest.prototype.testUndefinedEntityReturnsNullValueObject = function() { +  var datastore = new nglr.DataStore(); +  var Entity = datastore.entity(undefined); +  var all = Entity.all(); +  assertEquals(0, all.length); +}; + +DataStoreTest.prototype.testFetchEntities = function(){ +  expectAsserts(6); +  var post = function(data, callback){ +    assertJsonEquals(["GET", "$entities"], data[0]); +    callback(200, [{A:0, B:0}]); +  }; +  var datastore = new nglr.DataStore(post); +  var entities = datastore.entities(function(){ +    assertTrue(true); +  }); +  datastore.flush(); +  assertJsonEquals([], datastore.bulkRequest); +  assertEquals(2, entities.length); +  assertEquals("A", entities[0].title); +  assertEquals("B", entities[1].title); +}; + +DataStoreTest.prototype.testItShouldMigrateSchema = function() { +  var datastore = new nglr.DataStore(); +  var Entity = datastore.entity("Entity", {a:[], user:{name:"Misko", email:""}}); +  var doc = Entity().$loadFrom({b:'abc', user:{email:"misko@hevery.com"}}); +  assertFalse( +      nglr.toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}) == +      nglr.toJson(doc)); +  doc.$migrate(); +  assertEquals( +      nglr.toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}), +      nglr.toJson(doc)); +}; + +DataStoreTest.prototype.testItShouldCollectRequestsForBulk = function() { +  var ds = new nglr.DataStore(); +  var Book = ds.entity("Book"); +  var Library = ds.entity("Library"); +  Book.all(); +  Library.load("123"); +  assertEquals(2, ds.bulkRequest.length); +  assertJsonEquals(["GET", "Book"], ds.bulkRequest[0]); +  assertJsonEquals(["GET", "Library/123"], ds.bulkRequest[1]); +}; + +DataStoreTest.prototype.testEmptyFlushShouldDoNothing = function () { +  var ds = new nglr.DataStore(function(){ +    fail("expecting noop"); +  }); +  ds.flush(); +}; + +DataStoreTest.prototype.testFlushShouldCallAllCallbacks = function() { +  var log = ""; +  function post(request, callback){ +    log += 'BulkRequest:' + nglr.toJson(request) + ';'; +    callback(200, [[{$id:'ABC'}], {$id:'XYZ'}]); +  } +  var ds = new nglr.DataStore(post); +  var Book = ds.entity("Book"); +  var Library = ds.entity("Library"); +  Book.all(function(instance){ +    log += nglr.toJson(instance) + ';'; +  }); +  Library.load("123", function(instance){ +    log += nglr.toJson(instance) + ';'; +  }); +  assertEquals("", log); +  ds.flush(); +  assertJsonEquals([], ds.bulkRequest); +  assertEquals('BulkRequest:[["GET","Book"],["GET","Library/123"]];[{"$id":"ABC"}];{"$id":"XYZ"};', log); +}; + +DataStoreTest.prototype.testSaveOnNotLoggedInRetriesAfterLoggin = function(){ +  var log = ""; +  var book; +  var ds = new nglr.DataStore(null, {login:function(c){c();}}); +  ds.post = function (request, callback){ +    assertJsonEquals([["POST", "", book]], request); +    ds.post = function(request, callback){ +      assertJsonEquals([["POST", "", book]], request); +      ds.post = function(){fail("too much recursion");}; +      callback(200, [{saved:"ok"}]); +    }; +    callback(200, {$status_code:401}); +  }; +  book = ds.entity("Book")({name:"misko"}); +  book.$save(); +  ds.flush(); +  assertJsonEquals({saved:"ok"}, book); +}; + +DataStoreTest.prototype.testItShouldRemoveItemFromCollectionWhenDeleted = function() { +  expectAsserts(6); +  var ds = new nglr.DataStore(); +  ds.post = function(request, callback){ +    assertJsonEquals([["GET", "Book"]], request); +    callback(200, [[{name:"Moby Dick", $id:123, $entity:'Book'}]]); +  }; +  var Book = ds.entity("Book"); +  var books = Book.all(); +  ds.flush(); +  assertJsonEquals([[{name:"Moby Dick", $id:123, $entity:'Book'}]], ds._cache.$collections); +  assertDefined(ds._cache['Book/123']); +  var book = Book({$id:123}); +  ds.post = function(request, callback){ +    assertJsonEquals([["DELETE", "", book]], request); +    callback(200, [book]); +  }; +  ds.remove(book); +  ds.flush(); +  assertUndefined(ds._cache['Book/123']); +  assertJsonEquals([[]],ds._cache.$collections); +}; + +DataStoreTest.prototype.testItShouldAddToAll = function() { +  expectAsserts(8); +  var ds = new nglr.DataStore(); +  ds.post = function(request, callback){ +    assertJsonEquals([["GET", "Book"]], request); +    callback(200, [[]]); +  }; +  var Book = ds.entity("Book"); +  var books = Book.all(); +  assertEquals(0, books.length); +  ds.flush(); +  var moby = Book({name:'moby'}); +  moby.$save(); +  ds.post = function(request, callback){ +    assertJsonEquals([["POST", "", moby]], request); +    moby.$id = '123'; +    callback(200, [moby]); +  }; +  ds.flush(); +  assertEquals(1, books.length); +  assertEquals(moby, books[0]); +   +  moby.$save(); +  ds.flush(); +  assertEquals(1, books.length); +  assertEquals(moby, books[0]); +}; + +DataStoreTest.prototype.testItShouldReturnCreatedDocumentCountByUser = function(){ +  expectAsserts(2); +  var datastore = new nglr.DataStore( +      function(request, callback){ +        assertJsonEquals([["GET", "$users"]], request); +        callback(200, [{misko:1, adam:1}]); +      }); +  var users = datastore.documentCountsByUser(); +  assertJsonEquals({misko:1, adam:1}, users); +}; + + +DataStoreTest.prototype.testItShouldReturnDocumentIdsForUeserByEntity = function(){ +  expectAsserts(2); +  var datastore = new nglr.DataStore( +      function(request, callback){ +        assertJsonEquals([["GET", "$users/misko@hevery.com"]], request); +        callback(200, [{Book:["1"], Library:["2"]}]); +      }); +  var users = datastore.userDocumentIdsByEntity("misko@hevery.com"); +  assertJsonEquals({Book:["1"], Library:["2"]}, users); +}; + +DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){ +  expectAsserts(7); +  var log = ""; +  var datastore = new nglr.DataStore( +      function(request, callback){ +        assertJsonEquals([["GET", "User/misko"]], request); +        callback(200, [{$status_code:404}]); +      }); +  var User = datastore.entity("User", {admin:false}); +  var user = User.loadOrCreate('misko', function(i){log+="cb "+i.$id+";";}); +  datastore.flush(); +  assertEquals("misko", user.$id); +  assertEquals("User", user.$entity); +  assertEquals(false, user.admin); +  assertEquals("undefined", typeof user.$secret); +  assertEquals("undefined", typeof user.$version); +  assertEquals("cb misko;", log); +}; + +DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){ +  var log = ""; +  var datastore = new nglr.DataStore( +      function(request, callback){ +        assertJsonEquals([["GET", "User/misko"],["GET", "User/adam"]], request); +        callback(200, [{$id:'misko'},{$id:'adam'}]); +      }); +  var User = datastore.entity("User"); +  var users = User.loadMany(['misko', 'adam'], function(i){log+="cb "+nglr.toJson(i)+";";}); +  datastore.flush(); +  assertEquals("misko", users[0].$id); +  assertEquals("adam", users[1].$id); +  assertEquals('cb [{"$id":"misko"},{"$id":"adam"}];', log); +}; + +DataStoreTest.prototype.testItShouldCreateJoinAndQuery = function() { +  var datastore = new nglr.DataStore(); +  var Invoice = datastore.entity("Invoice"); +  var Customer = datastore.entity("Customer"); +  var InvoiceWithCustomer = datastore.join({ +    invoice:{join:Invoice}, +    customer:{join:Customer, on:"invoice.customer"} +  }); +  var invoiceWithCustomer = InvoiceWithCustomer.query("invoice.month", 1); +  assertEquals([], invoiceWithCustomer); +  assertJsonEquals([["GET", "Invoice/month=1"]], datastore.bulkRequest); +  var request = datastore.bulkRequest.shift(); +  request.$$callback([{$id:1, customer:1},{$id:2, customer:1},{$id:3, customer:3}]); +  assertJsonEquals([["GET","Customer/1"],["GET","Customer/3"]], datastore.bulkRequest); +  datastore.bulkRequest.shift().$$callback({$id:1}); +  datastore.bulkRequest.shift().$$callback({$id:3}); +  assertJsonEquals([ +    {invoice:{$id:1,customer:1},customer:{$id:1}}, +    {invoice:{$id:2,customer:1},customer:{$id:1}}, +    {invoice:{$id:3,customer:3},customer:{$id:3}}], invoiceWithCustomer); +}; + +DataStoreTest.prototype.testItShouldThrowIfMoreThanOneEntityIsPrimary = function() { +  var datastore = new nglr.DataStore(); +  var Invoice = datastore.entity("Invoice"); +  var Customer = datastore.entity("Customer"); +  assertThrows("Exactly one entity needs to be primary.", function(){ +    datastore.join({ +      invoice:{join:Invoice}, +      customer:{join:Customer} +    }); +  });   +}; + +DataStoreTest.prototype.testItShouldThrowIfLoopInReferences = function() { +  var datastore = new nglr.DataStore(); +  var Invoice = datastore.entity("Invoice"); +  var Customer = datastore.entity("Customer"); +  assertThrows("Infinite loop in join: invoice -> customer", function(){ +    datastore.join({ +      invoice:{join:Invoice, on:"customer.invoice"}, +      customer:{join:Customer, on:"invoice.customer"} +    }); +  }); +}; + +DataStoreTest.prototype.testItShouldThrowIfReferenceToNonExistantJoin = function() { +  var datastore = new nglr.DataStore(); +  var Invoice = datastore.entity("Invoice"); +  var Customer = datastore.entity("Customer"); +  assertThrows("Named entity 'x' is undefined.", function(){ +    datastore.join({ +      invoice:{join:Invoice, on:"x.invoice"}, +      customer:{join:Customer, on:"invoice.customer"} +    }); +  });   +}; + +DataStoreTest.prototype.testItShouldThrowIfQueryOnNonPrimary = function() { +  var datastore = new nglr.DataStore(); +  var Invoice = datastore.entity("Invoice"); +  var Customer = datastore.entity("Customer"); +  var InvoiceWithCustomer = datastore.join({ +    invoice:{join:Invoice}, +    customer:{join:Customer, on:"invoice.customer"} +  }); +  assertThrows("Named entity 'customer' is not a primary entity.", function(){ +    InvoiceWithCustomer.query("customer.month", 1); +  });   +}; diff --git a/test/EntityDeclarationTest.js b/test/EntityDeclarationTest.js new file mode 100644 index 00000000..5cab90f4 --- /dev/null +++ b/test/EntityDeclarationTest.js @@ -0,0 +1,46 @@ +EntityDeclarationTest = TestCase('EntityDeclarationTest'); + +EntityDeclarationTest.prototype.testEntityTypeOnly = function(){ +  expectAsserts(2); +  var scope = new nglr.Scope({$datastore:{entity:function(name){ +    assertEquals("Person", name); +  }}}); +  var init = scope.entity("Person"); +  assertEquals("", init); +}; + +EntityDeclarationTest.prototype.testWithDefaults = function(){ +  expectAsserts(4); +  var scope = new nglr.Scope({$datastore:{entity:function(name, init){ +    assertEquals("Person", name); +    assertEquals("=a:", init.a); +    assertEquals(0, init.b.length); +  }}}); +  var init = scope.entity('Person:{a:"=a:", b:[]}'); +  assertEquals("", init); +}; + +EntityDeclarationTest.prototype.testWithName = function(){ +  expectAsserts(2); +  var scope = new nglr.Scope({$datastore:{entity:function(name, init){ +    assertEquals("Person", name); +    return function (){ return {}; }; +  }}}); +  var init = scope.entity('friend=Person'); +  assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};", init); +}; + +EntityDeclarationTest.prototype.testMultipleEntities = function(){ +  expectAsserts(3); +  var expect = ['Person', 'Book']; +  var i=0; +  var scope = new nglr.Scope({$datastore:{entity:function(name, init){ +    assertEquals(expect[i], name); +    i++; +    return function (){ return {}; }; +  }}}); +  var init = scope.entity('friend=Person;book=Book;'); +  assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};" + +               "$anchor.book:{book=Book.load($anchor.book);book.$$anchor=\"book\";};", +               init); +}; diff --git a/test/FileControllerTest.js b/test/FileControllerTest.js new file mode 100644 index 00000000..ca5925e4 --- /dev/null +++ b/test/FileControllerTest.js @@ -0,0 +1,98 @@ +FileControllerTest = TestCase('FileControllerTest'); + +FileControllerTest.prototype.testOnSelectUpdateView = function(){ +  var view = jQuery('<span><a/><span/></span>'); +  var swf = {}; +  var controller = new nglr.FileController(view, null, swf); +  swf.uploadFile = function(path){}; +  controller._on_select('A', 9, '9 bytes'); +  assertEquals(view.find('a').text(), "A"); +  assertEquals(view.find('span').text(), "9 bytes"); +}; + +FileControllerTest.prototype.testUpdateModelView = function(){ +  var view = nglr.FileController.template(''); +  var input = $('<input name="value.input">'); +  var controller; +  var scope = new nglr.Scope({value:{}, $binder:{updateView:function(){ +      controller.updateView(scope); +    }}}); +  view.data('scope', scope); +  controller = new nglr.FileController(view, 'value.input', null, "http://server_base"); +  var value = '{"text":"A", "size":123, "id":"890"}'; +  controller._on_uploadCompleteData(value); +  controller.updateView(scope); +  assertEquals(scope.get('value.input.text'), 'A'); +  assertEquals(scope.get('value.input.size'), 123); +  assertEquals(scope.get('value.input.id'), '890'); +  assertEquals(scope.get('value.input.url'), 'http://server_base/_attachments/890/A'); +  assertEquals(view.find('a').text(), "A"); +  assertEquals(view.find('a').attr('href'), "http://server_base/_attachments/890/A"); +  assertEquals(view.find('span').text(), "123 bytes"); +}; + +FileControllerTest.prototype.testFileUpload = function(){ +  expectAsserts(1); +  var swf = {}; +  var controller = new nglr.FileController(null, null, swf, "http://server_base"); +  swf.uploadFile = function(path){ +    assertEquals("http://server_base/_attachments", path); +  }; +  controller.name = "Name"; +  controller.upload(); +}; + +FileControllerTest.prototype.testFileUploadNoFileIsNoop = function(){ +  expectAsserts(0); +  var swf = {uploadFile:function(path){ +    fail(); +  }}; +  var controller = new nglr.FileController(null, swf); +  controller.upload("basePath", null); +}; + +FileControllerTest.prototype.testRemoveAttachment = function(){ +  var doc = nglr.FileController.template(); +  var input = $('<input name="file">'); +  var scope = new nglr.Scope(); +  input.data('scope', scope); +  var controller = new nglr.FileController(doc, 'file', null, null); +  controller.updateView(scope); +  assertEquals(false, doc.find('input').attr('checked')); + +  scope.set('file', {url:'url', size:123}); +  controller.updateView(scope); +  assertEquals(true, doc.find('input').attr('checked')); + +  doc.find('input').attr('checked', false); +  controller.updateModel(scope); +  assertNull(scope.get('file')); + +  doc.find('input').attr('checked', true); +  controller.updateModel(scope); +  assertEquals('url', scope.get('file.url')); +  assertEquals(123, scope.get('file.size')); +}; + +FileControllerTest.prototype.testShouldEmptyOutOnUndefined = function () { +  var view = nglr.FileController.template('hello'); +  var controller = new nglr.FileController(view, 'abc', null, null); + +  var scope = new nglr.Scope(); +  scope.set('abc', {text: 'myname', url: 'myurl', size: 1234}); + +  controller.updateView(scope); +  assertEquals("myurl", view.find('a').attr('href')); +  assertEquals("myname", view.find('a').text()); +  assertEquals(true, view.find('input').is(':checked')); +  assertEquals("1.2 KB", view.find('span').text()); + +  scope.set('abc', undefined); +  controller.updateView(scope); +  assertEquals("myurl", view.find('a').attr('href')); +  assertEquals("myname", view.find('a').text()); +  assertEquals(false, view.find('input').is(':checked')); +  assertEquals("1.2 KB", view.find('span').text()); +}; + + diff --git a/test/FiltersTest.js b/test/FiltersTest.js new file mode 100644 index 00000000..8943fdd4 --- /dev/null +++ b/test/FiltersTest.js @@ -0,0 +1,153 @@ +FiltersTest = TestCase('FiltersTest'); + +FiltersTest.prototype.testCurrency = function(){ +  var html = $('<span/>'); +  var context = {element:html[0]}; +  var currency = nglr.bind(context, angular.filter.currency); + +  assertEquals(currency(0), '$0.00'); +  assertEquals(html.hasClass('ng-format-negative'), false); +  assertEquals(currency(-999), '$-999.00'); +  assertEquals(html.hasClass('ng-format-negative'), true); +  assertEquals(currency(1234.5678), '$1,234.57'); +  assertEquals(html.hasClass('ng-format-negative'), false); +}; + +FiltersTest.prototype.testFilterThisIsContext = function(){ +  expectAsserts(2); +  var scope = new nglr.Scope(); +  nglr.Scope.expressionCache = {}; +  var context = {element:123}; +  angular.filter.testFn = function () { +    assertEquals('Context not equal', this, context); +    assertEquals('scope not equal', this.scope, scope); +  }; +  scope.eval("0|testFn", context); +  delete angular.filter['testFn']; +}; + +FiltersTest.prototype.testNumberFormat = function(){ +  var context = {jqElement:$('<span/>')}; +  var number = nglr.bind(context, angular.filter.number); + +  assertEquals('0', number(0, 0)); +  assertEquals('0.00', number(0)); +  assertEquals('-999.00', number(-999)); +  assertEquals('1,234.57', number(1234.5678)); +  assertEquals('', number(Number.NaN)); +  assertEquals('1,234.57', number("1234.5678")); +  assertEquals("", number(1/0)); +}; + +FiltersTest.prototype.testJson = function () { +  assertEquals(nglr.toJson({a:"b"}, true), angular.filter.json({a:"b"})); +}; + +FiltersTest.prototype.testPackageTracking = function () { +  var assert = function(title, trackingNo) { +    var val = angular.filter.trackPackage(trackingNo, title); +    assertNotNull("Did Not Match: " + trackingNo, val); +    assertEquals(angular.filter.Meta.TAG, val.TAG); +    assertEquals(title + ": " + nglr.trim(trackingNo), val.text); +    assertNotNull(val.url); +    assertEquals(nglr.trim(trackingNo), val.trackingNo); +    assertEquals('<a href="' + val.url + '">' + val.text + '</a>', val.html); +  }; +  assert('UPS', ' 1Z 999 999 99 9999 999 9 '); +  assert('UPS', '1ZW5w5220379084747'); + +  assert('FedEx', '418822131061812'); +  assert('FedEx', '9612019 5935 3267 2473 738'); +  assert('FedEx', '9612019593532672473738'); +  assert('FedEx', '235354667129449'); +  assert('FedEx', '915368880571'); +  assert('FedEx', '901712142390'); +  assert('FedEx', '297391510063413'); + +  assert('USPS', '9101 8052 1390 7402 4335 49'); +  assert('USPS', '9101010521297963339560'); +  assert('USPS', '9102901001301038667029'); +  assert('USPS', '910 27974 4490 3000 8916 56'); +  assert('USPS', '9102801438635051633253'); +}; + +FiltersTest.prototype.testLink = function() { +  var assert = function(text, url, obj){ +    var val = angular.filter.link(obj); +    assertEquals(angular.filter.Meta.TAG, val.TAG); +    assertEquals('<a href="' + url + '">' + text + '</a>', val.html); +  }; +  assert("url", "url", "url"); +  assert("hello", "url", {text:"hello", url:"url"}); +  assert("a@b.com", "mailto:a@b.com", "a@b.com"); +}; + +FiltersTest.prototype.testBytes = function(){ +  var controller = new nglr.FileController(); +  assertEquals(angular.filter.bytes(123), '123 bytes'); +  assertEquals(angular.filter.bytes(1234), '1.2 KB'); +  assertEquals(angular.filter.bytes(1234567), '1.1 MB'); +}; + +FiltersTest.prototype.testImage = function(){ +  assertEquals(null, angular.filter.image()); +  assertEquals(null, angular.filter.image({})); +  assertEquals(null, angular.filter.image("")); +  assertEquals('<img src="abc"/>', angular.filter.image({url:"abc"}).html); +  assertEquals( +      '<img src="abc" style="max-width: 10px; max-height: 10px;"/>', +      angular.filter.image({url:"abc"}, 10).html); +  assertEquals( +      '<img src="abc" style="max-width: 10px; max-height: 20px;"/>', +      angular.filter.image({url:"abc"}, 10, 20).html); +}; + +FiltersTest.prototype.testQRcode = function() { +  assertEquals( +      '<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello%20world&chs=200x200&cht=qr"/>', +      angular.filter.qrcode('Hello world').html); +  assertEquals( +      '<img width="100" height="100" src="http://chart.apis.google.com/chart?chl=http%3A%2F%2Fserver%3Fa%26b%3Dc&chs=100x100&cht=qr"/>', +      angular.filter.qrcode('http://server?a&b=c', 100).html); +}; + +FiltersTest.prototype.testLowercase = function() { +  assertEquals('abc', angular.filter.lowercase('AbC')); +  assertEquals(null, angular.filter.lowercase(null)); +}; + +FiltersTest.prototype.testUppercase = function() { +  assertEquals('ABC', angular.filter.uppercase('AbC')); +  assertEquals(null, angular.filter.uppercase(null)); +}; + +FiltersTest.prototype.testLineCount = function() { +  assertEquals(1, angular.filter.linecount(null)); +  assertEquals(1, angular.filter.linecount('')); +  assertEquals(1, angular.filter.linecount('a')); +  assertEquals(2, angular.filter.linecount('a\nb')); +  assertEquals(3, angular.filter.linecount('a\nb\nc')); +}; + +FiltersTest.prototype.testIf = function() { +  assertEquals('A', angular.filter['if']('A', true)); +  assertEquals(undefined, angular.filter['if']('A', false)); +}; + +FiltersTest.prototype.testUnless = function() { +  assertEquals('A', angular.filter.unless('A', false)); +  assertEquals(undefined, angular.filter.unless('A', true)); +}; + +FiltersTest.prototype.testGoogleChartApiEncode = function() { +  assertEquals( +      '<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello world&chs=200x200&cht=qr"/>', +      angular.filter.googleChartApi.encode({cht:"qr", chl:"Hello world"}).html); +}; + +FiltersTest.prototype.testHtml = function() { +  assertEquals( +      "a<b>c</b>d", +      angular.filter.html("a<b>c</b>d").html); +  assertTrue(angular.filter.html("a<b>c</b>d") instanceof angular.filter.Meta); +}; diff --git a/test/JsonTest.js b/test/JsonTest.js new file mode 100644 index 00000000..5c3644f5 --- /dev/null +++ b/test/JsonTest.js @@ -0,0 +1,69 @@ +JsonTest = TestCase("JsonTest"); + +JsonTest.prototype.testPrimitives = function () { +  assertEquals("null", nglr.toJson(0/0)); +  assertEquals("null", nglr.toJson(null)); +  assertEquals("true", nglr.toJson(true)); +  assertEquals("false", nglr.toJson(false)); +  assertEquals("123.45", nglr.toJson(123.45)); +  assertEquals('"abc"', nglr.toJson("abc")); +  assertEquals('"a \\t \\n \\r b \\\\"', nglr.toJson("a \t \n \r b \\")); +}; + +JsonTest.prototype.testEscaping = function () { +  assertEquals("\"7\\\\\\\"7\"", nglr.toJson("7\\\"7")); +}; + +JsonTest.prototype.testObjects = function () { +  assertEquals('{"a":1,"b":2}', nglr.toJson({a:1,b:2})); +  assertEquals('{"a":{"b":2}}', nglr.toJson({a:{b:2}})); +  assertEquals('{"a":{"b":{"c":0}}}', nglr.toJson({a:{b:{c:0}}})); +  assertEquals('{"a":{"b":null}}', nglr.toJson({a:{b:0/0}})); +}; + +JsonTest.prototype.testObjectPretty = function () { +  assertEquals('{\n  "a":1,\n  "b":2}', nglr.toJson({a:1,b:2}, true)); +  assertEquals('{\n  "a":{\n    "b":2}}', nglr.toJson({a:{b:2}}, true)); +}; + +JsonTest.prototype.testArray = function () { +  assertEquals('[]', nglr.toJson([])); +  assertEquals('[1,"b"]', nglr.toJson([1,"b"])); +}; + +JsonTest.prototype.testIgnoreFunctions = function () { +  assertEquals('[null,1]', nglr.toJson([function(){},1])); +  assertEquals('{}', nglr.toJson({a:function(){}})); +}; + +JsonTest.prototype.testParseNull = function () { +  assertNull(nglr.fromJson("null")); +}; + +JsonTest.prototype.testParseBoolean = function () { +  assertTrue(nglr.fromJson("true")); +  assertFalse(nglr.fromJson("false")); +}; + +JsonTest.prototype.test$$isIgnored = function () { +  assertEquals("{}", nglr.toJson({$$:0})); +}; + +JsonTest.prototype.testArrayWithEmptyItems = function () { +  var a = []; +  a[1] = "X"; +  assertEquals('[null,"X"]', nglr.toJson(a)); +}; + +JsonTest.prototype.testItShouldEscapeUnicode = function () { +  assertEquals(1, "\u00a0".length); +  assertEquals(8, nglr.toJson("\u00a0").length); +  assertEquals(1, nglr.fromJson(nglr.toJson("\u00a0")).length); +}; + +JsonTest.prototype.testItShouldUTCDates = function() { +  var date = angular.String.toDate("2009-10-09T01:02:03Z"); +  assertEquals('"2009-10-09T01:02:03Z"', nglr.toJson(date));   +  assertEquals(date.getTime(),  +      nglr.fromJson('"2009-10-09T01:02:03Z"').getTime());   +}; diff --git a/test/LoaderTest.js b/test/LoaderTest.js new file mode 100644 index 00000000..91a804a5 --- /dev/null +++ b/test/LoaderTest.js @@ -0,0 +1,70 @@ +LoaderTest = TestCase('LoaderTest'); + +LoaderTest.prototype.testLoadCss = function(){ +  if ($.browser.safari) return; +  var head = jQuery('<head/>')[0]; +  var loader = new nglr.Loader(document, head, {}); +  var log = ''; +  loader.config.server = 'http://'; +  loader.loadCss('x'); +  assertEquals($(head).find('link').attr('href'), 'http://x'); +}; + +LoaderTest.prototype.testDefaultDatabasePathFromSubdomain = function() { +  var loader = new nglr.Loader(null, null, {server:"http://account.getangular.com", database:"database"}); +  loader.computeConfiguration(); +  assertEquals("database", loader.config.database); + +  loader = new nglr.Loader(null, null, {server:"http://account.getangular.com"}); +  loader.computeConfiguration(); +  assertEquals("account", loader.config.database); + +  loader = new nglr.Loader(null, null, {server:"https://account.getangular.com"}); +  loader.computeConfiguration(); +  assertEquals("account", loader.config.database); +}; + + + +UrlWatcherTest = TestCase('UrlWatcherTest'); + +UrlWatcherTest.prototype.testUrlWatcher = function () { +  expectAsserts(2); +  var location = {href:"http://server", hash:""}; +  var watcher = new nglr.UrlWatcher(location); +  watcher.delay = 1; +  watcher.listener = function(url){ +    assertEquals('http://getangular.test', url); +  }; +  watcher.setTimeout = function(fn, delay){ +    assertEquals(1, delay); +    location.href = "http://getangular.test"; +    watcher.setTimeout = function(fn, delay) { +    }; +    fn(); +  }; +  watcher.watch(); +}; + +UrlWatcherTest.prototype.testItShouldFireOnUpdateEventWhenSpecialURLSet = function(){ +  expectAsserts(2); +  var location = {href:"http://server", hash:"#$iframe_notify=1234"}; +  var watcher = new nglr.UrlWatcher(location); +  nglr._iframe_notify_1234 = function () { +    assertEquals("undefined", typeof nglr._iframe_notify_1234); +    assertEquals("http://server2#", location.href); +  }; +  watcher.delay = 1; +  watcher.expectedUrl = "http://server2"; +  watcher.setTimeout = function(fn, delay){ +    watcher.setTimeout = function(fn, delay) {}; +    fn(); +  }; +  watcher.watch(); +}; + +FunctionTest = TestCase("FunctionTest"); + +FunctionTest.prototype.testEscapeHtml = function () { +  assertEquals("<div>&amp;</div>", nglr.escapeHtml('<div>&</div>')); +};
\ No newline at end of file diff --git a/test/ModelTest.js b/test/ModelTest.js new file mode 100644 index 00000000..5d9119a1 --- /dev/null +++ b/test/ModelTest.js @@ -0,0 +1,84 @@ +ModelTest = TestCase('ModelTest'); + +ModelTest.prototype.testLoadSaveOperations = function(){ +  var m1 = new nglr.DataStore().entity('A')(); +  m1.a = 1; + +  var m2 =  {b:1}; + +  m1.$loadFrom(m2); + +  assertTrue(!m1.a); +  assertEquals(m1.b, 1); +}; + +ModelTest.prototype.testLoadfromDoesNotClobberFunctions = function(){ +  var m1 = new nglr.DataStore().entity('A')(); +  m1.id = function(){return 'OK';}; +  m1.$loadFrom({id:null}); +  assertEquals(m1.id(), 'OK'); + +  m1.b = 'OK'; +  m1.$loadFrom({b:function(){}}); +  assertEquals(m1.b, 'OK'); +}; + +ModelTest.prototype.testDataStoreDoesNotGetClobbered = function(){ +  var ds = new nglr.DataStore(); +  var m = ds.entity('A')(); +  assertTrue(m.$$entity.datastore === ds); +  m.$loadFrom({}); +  assertTrue(m.$$entity.datastore === ds); +}; + +ModelTest.prototype.testManagedModelDelegatesMethodsToDataStore = function(){ +  expectAsserts(7); +  var datastore = new nglr.DataStore(); +  var model = datastore.entity("A", {a:1})(); +  var fn = {}; +  datastore.save = function(instance, callback) { +    assertTrue(model === instance); +    assertTrue(callback === fn); +  }; +  datastore.remove = function(instance, callback) { +    assertTrue(model === instance); +    assertTrue(callback === fn); +  }; +  datastore.load = function(instance, id, callback) { +    assertTrue(model === instance); +    assertTrue(id === "123"); +    assertTrue(callback === fn); +  }; +  model.$save(fn); +  model.$delete(fn); +  model.$loadById("123", fn); +}; + +ModelTest.prototype.testManagedModelCanBeForcedToFlush = function(){ +  expectAsserts(6); +  var datastore = new nglr.DataStore(); +  var model = datastore.entity("A", {a:1})(); + +  datastore.save = function(instance, callback) { +    assertTrue(model === instance); +    assertTrue(callback === undefined); +  }; +  datastore.remove = function(instance, callback) { +    assertTrue(model === instance); +    assertTrue(callback === undefined); +  }; +  datastore.flush = function(){ +    assertTrue(true); +  }; +  model.$save(true); +  model.$delete(true); +}; + + +ModelTest.prototype.testItShouldMakeDeepCopyOfInitialValues = function (){ +  var initial = {a:[]}; +  var entity = new nglr.DataStore().entity("A", initial); +  var model = entity(); +  model.a.push(1); +  assertEquals(0, entity().a.length); +}; diff --git a/test/ParserTest.js b/test/ParserTest.js new file mode 100644 index 00000000..7fe8e6a4 --- /dev/null +++ b/test/ParserTest.js @@ -0,0 +1,462 @@ +LexerTest = TestCase('LexerTest'); + +LexerTest.prototype.testTokenizeAString = function(){ +  var lexer = new nglr.Lexer("a.bc[22]+1.3|f:'a\\\'c':\"d\\\"e\""); +  var tokens = lexer.parse(); +  var i = 0; +  assertEquals(tokens[i].index, 0); +  assertEquals(tokens[i].text, 'a.bc'); + +  i++; +  assertEquals(tokens[i].index, 4); +  assertEquals(tokens[i].text, '['); + +  i++; +  assertEquals(tokens[i].index, 5); +  assertEquals(tokens[i].text, 22); + +  i++; +  assertEquals(tokens[i].index, 7); +  assertEquals(tokens[i].text, ']'); + +  i++; +  assertEquals(tokens[i].index, 8); +  assertEquals(tokens[i].text, '+'); + +  i++; +  assertEquals(tokens[i].index, 9); +  assertEquals(tokens[i].text, 1.3); + +  i++; +  assertEquals(tokens[i].index, 12); +  assertEquals(tokens[i].text, '|'); + +  i++; +  assertEquals(tokens[i].index, 13); +  assertEquals(tokens[i].text, 'f'); + +  i++; +  assertEquals(tokens[i].index, 14); +  assertEquals(tokens[i].text, ':'); + +  i++; +  assertEquals(tokens[i].index, 15); +  assertEquals(tokens[i].text, "a'c"); + +  i++; +  assertEquals(tokens[i].index, 21); +  assertEquals(tokens[i].text, ':'); + +  i++; +  assertEquals(tokens[i].index, 22); +  assertEquals(tokens[i].text, 'd"e'); +}; + + +LexerTest.prototype.testTokenizeRegExp = function(){ +  var lexer = new nglr.Lexer("/r 1/"); +  var tokens = lexer.parse(); +  var i = 0; +  assertEquals(tokens[i].index, 0); +  assertEquals(tokens[i].text, 'r 1'); +  assertEquals("r 1".match(tokens[i].fn())[0], 'r 1'); +}; + +LexerTest.prototype.testQuotedString = function(){ +  var str = "['\\'', \"\\\"\"]"; +  var lexer = new nglr.Lexer(str); +  var tokens = lexer.parse(); + +  assertEquals(1, tokens[1].index); +  assertEquals("'", tokens[1].text); + +  assertEquals(7, tokens[3].index); +  assertEquals('"', tokens[3].text); + +}; + +LexerTest.prototype.testQuotedStringEscape = function(){ +  var str = '"\\"\\n\\f\\r\\t\\v\\u00A0"'; +  var lexer = new nglr.Lexer(str); +  var tokens = lexer.parse(); + +  assertEquals('"\n\f\r\t\v\u00A0', tokens[0].text); +}; + +LexerTest.prototype.testTokenizeUnicode = function(){ +  var lexer = new nglr.Lexer('"\\u00A0"'); +  var tokens = lexer.parse(); +  assertEquals(1, tokens.length); +  assertEquals('\u00a0', tokens[0].text); +}; + +LexerTest.prototype.testTokenizeRegExpWithOptions = function(){ +  var lexer = new nglr.Lexer("/r/g"); +  var tokens = lexer.parse(); +  var i = 0; +  assertEquals(tokens[i].index, 0); +  assertEquals(tokens[i].text, 'r'); +  assertEquals(tokens[i].flags, 'g'); +  assertEquals("rr".match(tokens[i].fn()).length, 2); +}; + +LexerTest.prototype.testTokenizeRegExpWithEscape = function(){ +  var lexer = new nglr.Lexer("/\\/\\d/"); +  var tokens = lexer.parse(); +  var i = 0; +  assertEquals(tokens[i].index, 0); +  assertEquals(tokens[i].text, '\\/\\d'); +  assertEquals("/1".match(tokens[i].fn())[0], '/1'); +}; + +LexerTest.prototype.testIgnoreWhitespace = function(){ +  var lexer = new nglr.Lexer("a \t \n \r b"); +  var tokens = lexer.parse(); +  assertEquals(tokens[0].text, 'a'); +  assertEquals(tokens[1].text, 'b'); +}; + +LexerTest.prototype.testRelation = function(){ +  var lexer = new nglr.Lexer("! == != < > <= >="); +  var tokens = lexer.parse(); +  assertEquals(tokens[0].text, '!'); +  assertEquals(tokens[1].text, '=='); +  assertEquals(tokens[2].text, '!='); +  assertEquals(tokens[3].text, '<'); +  assertEquals(tokens[4].text, '>'); +  assertEquals(tokens[5].text, '<='); +  assertEquals(tokens[6].text, '>='); +}; + +LexerTest.prototype.testStatements = function(){ +  var lexer = new nglr.Lexer("a;b;"); +  var tokens = lexer.parse(); +  assertEquals(tokens[0].text, 'a'); +  assertEquals(tokens[1].text, ';'); +  assertEquals(tokens[2].text, 'b'); +  assertEquals(tokens[3].text, ';'); +}; + +ParserTest = TestCase('ParserTest'); + +ParserTest.prototype.testExpressions = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("-1"), -1); +  assertEquals(scope.eval("1 + 2.5"), 3.5); +  assertEquals(scope.eval("1 + -2.5"), -1.5); +  assertEquals(scope.eval("1+2*3/4"), 1+2*3/4); +  assertEquals(scope.eval("0--1+1.5"), 0- -1 + 1.5); +  assertEquals(scope.eval("-0--1++2*-3/-4"), -0- -1+ +2*-3/-4); +  assertEquals(scope.eval("1/2*3"), 1/2*3); +}; + +ParserTest.prototype.testComparison = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("false"), false); +  assertEquals(scope.eval("!true"), false); +  assertEquals(scope.eval("1==1"), true); +  assertEquals(scope.eval("1!=2"), true); +  assertEquals(scope.eval("1<2"), true); +  assertEquals(scope.eval("1<=1"), true); +  assertEquals(scope.eval("1>2"), 1>2); +  assertEquals(scope.eval("2>=1"), 2>=1); +}; + +ParserTest.prototype.testLogical = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("0&&2"), 0&&2); +  assertEquals(scope.eval("0||2"), 0||2); +  assertEquals(scope.eval("0||1&&2"), 0||1&&2); +}; + +ParserTest.prototype.testString = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("'a' + 'b c'"), "ab c"); +}; + +ParserTest.prototype.testFilters = function(){ +  angular.filter.substring = function(input, start, end) { +    return input.substring(start, end); +  }; + +  angular.filter.upper = {_case:function(input) { +    return input.toUpperCase(); +  }}; +  var scope = new nglr.Scope(); +  try { +    scope.eval("1|nonExistant"); +    fail(); +  } catch (e) { +    assertEquals(e, "Function 'nonExistant' at column '3' in '1|nonExistant' is not defined."); +  } +  scope.set('offset', 3); +  assertEquals(scope.eval("'abcd'|upper._case"), "ABCD"); +  assertEquals(scope.eval("'abcd'|substring:1:offset"), "bc"); +  assertEquals(scope.eval("'abcd'|substring:1:3|upper._case"), "BC"); +}; + +ParserTest.prototype.testScopeAccess = function(){ +  var scope = new nglr.Scope(); +  scope.set('a', 123); +  scope.set('b.c', 456); +  assertEquals(scope.eval("a", scope), 123); +  assertEquals(scope.eval("b.c", scope), 456); +  assertEquals(scope.eval("x.y.z", scope), undefined); +}; + +ParserTest.prototype.testGrouping = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("(1+2)*3"), (1+2)*3); +}; + +ParserTest.prototype.testAssignments = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("a=12"), 12); +  assertEquals(scope.get("a"), 12); + +  scope = new nglr.Scope(); +  assertEquals(scope.eval("x.y.z=123;"), 123); +  assertEquals(scope.get("x.y.z"), 123); + +  assertEquals(234, scope.eval("a=123; b=234")); +  assertEquals(123, scope.get("a")); +  assertEquals(234, scope.get("b")); +}; + +ParserTest.prototype.testFunctionCallsNoArgs = function(){ +  var scope = new nglr.Scope(); +  scope.set('const', function(a,b){return 123;}); +  assertEquals(scope.eval("const()"), 123); +}; + +ParserTest.prototype.testFunctionCalls = function(){ +  var scope = new nglr.Scope(); +  scope.set('add', function(a,b){ +    return a+b; +  }); +  assertEquals(3, scope.eval("add(1,2)")); +}; + +ParserTest.prototype.testCalculationBug = function(){ +  var scope = new nglr.Scope(); +  scope.set('taxRate', 8); +  scope.set('subTotal', 100); +  assertEquals(scope.eval("taxRate / 100 * subTotal"), 8); +  assertEquals(scope.eval("subTotal * taxRate / 100"), 8); +}; + +ParserTest.prototype.testArray = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("[]").length, 0); +  assertEquals(scope.eval("[1, 2]").length, 2); +  assertEquals(scope.eval("[1, 2]")[0], 1); +  assertEquals(scope.eval("[1, 2]")[1], 2); +}; + +ParserTest.prototype.testArrayAccess = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("[1][0]"), 1); +  assertEquals(scope.eval("[[1]][0][0]"), 1); +  assertEquals(scope.eval("[].length"), 0); +  assertEquals(scope.eval("[1, 2].length"), 2); +}; + +ParserTest.prototype.testObject = function(){ +  var scope = new nglr.Scope(); +  assertEquals(nglr.toJson(scope.eval("{}")), "{}"); +  assertEquals(nglr.toJson(scope.eval("{a:'b'}")), '{"a":"b"}'); +  assertEquals(nglr.toJson(scope.eval("{'a':'b'}")), '{"a":"b"}'); +  assertEquals(nglr.toJson(scope.eval("{\"a\":'b'}")), '{"a":"b"}'); +}; + +ParserTest.prototype.testObjectAccess = function(){ +  var scope = new nglr.Scope(); +  assertEquals("WC", scope.eval("{false:'WC', true:'CC'}[false]")); +}; + +ParserTest.prototype.testJSON = function(){ +  var scope = new nglr.Scope(); +  assertEquals(nglr.toJson(scope.eval("[{}]")), "[{}]"); +  assertEquals(nglr.toJson(scope.eval("[{a:[]}, {b:1}]")), '[{"a":[]},{"b":1}]'); +}; + +ParserTest.prototype.testMultippleStatements = function(){ +  var scope = new nglr.Scope(); +  assertEquals(scope.eval("a=1;b=3;a+b"), 4); +  assertEquals(scope.eval(";;1;;"), 1); +}; + +ParserTest.prototype.testParseThrow = function(){ +  expectAsserts(1); +  var scope = new nglr.Scope(); +  scope.set('e', 'abc'); +  try { +    scope.eval("throw e"); +  } catch(e) { +    assertEquals(e, 'abc'); +  } +}; + +ParserTest.prototype.testMethodsGetDispatchedWithCorrectThis = function(){ +  var scope = new nglr.Scope(); +  var C = function (){ +    this.a=123; +  }; +  C.prototype.getA = function(){ +    return this.a; +  }; + +  scope.set("obj", new C()); +  assertEquals(123, scope.eval("obj.getA()")); +}; +ParserTest.prototype.testMethodsArgumentsGetCorrectThis = function(){ +  var scope = new nglr.Scope(); +  var C = function (){ +    this.a=123; +  }; +  C.prototype.sum = function(value){ +    return this.a + value; +  }; +  C.prototype.getA = function(){ +    return this.a; +  }; + +  scope.set("obj", new C()); +  assertEquals(246, scope.eval("obj.sum(obj.getA())")); +}; + +ParserTest.prototype.testObjectPointsToScopeValue = function(){ +  var scope = new nglr.Scope(); +  scope.set('a', "abc"); +  assertEquals("abc", scope.eval("{a:a}").a); +}; + +ParserTest.prototype.testFieldAccess = function(){ +  var scope = new nglr.Scope(); +  var fn = function(){ +      return {name:'misko'}; +    }; +  scope.set('a', fn); +  assertEquals("misko", scope.eval("a().name")); +}; + +ParserTest.prototype.testArrayIndexBug = function () { +  var scope = new nglr.Scope(); +  scope.set('items', [{}, {name:'misko'}]); + +  assertEquals("misko", scope.eval('items[1].name')); +}; + +ParserTest.prototype.testArrayAssignment = function () { +  var scope = new nglr.Scope(); +  scope.set('items', []); + +  assertEquals("abc", scope.eval('items[1] = "abc"')); +  assertEquals("abc", scope.eval('items[1]')); +//  Dont know how to make this work.... +//  assertEquals("moby", scope.eval('books[1] = "moby"')); +//  assertEquals("moby", scope.eval('books[1]')); +}; + +ParserTest.prototype.testFiltersCanBeGrouped = function () { +  var scope = new nglr.Scope({name:'MISKO'}); +  assertEquals('misko', scope.eval('n = (name|lowercase)')); +  assertEquals('misko', scope.eval('n')); +}; + +ParserTest.prototype.testFiltersCanBeGrouped = function () { +  var scope = new nglr.Scope({name:'MISKO'}); +  assertEquals('misko', scope.eval('n = (name|lowercase)')); +  assertEquals('misko', scope.eval('n')); +}; + +ParserTest.prototype.testRemainder = function () { +  var scope = new nglr.Scope(); +  assertEquals(1, scope.eval('1%2')); +}; + +ParserTest.prototype.testSumOfUndefinedIsNotUndefined = function () { +  var scope = new nglr.Scope(); +  assertEquals(1, scope.eval('1+undefined')); +  assertEquals(1, scope.eval('undefined+1')); +}; + +ParserTest.prototype.testMissingThrowsError = function() { +  var scope = new nglr.Scope(); +  try { +    scope.eval('[].count('); +    fail(); +  } catch (e) { +    assertEquals('Unexpected end of expression: [].count(', e); +  } +}; + +ParserTest.prototype.testItShouldParseOnChangeIntoHashSet = function () { +  var scope = new nglr.Scope({count:0}); +  scope.watch("$anchor.a:count=count+1;$anchor.a:count=count+20;b:count=count+300"); + +  scope.watchListeners["$anchor.a"].listeners[0](); +  assertEquals(1, scope.get("count")); +  scope.watchListeners["$anchor.a"].listeners[1](); +  assertEquals(21, scope.get("count")); +  scope.watchListeners["b"].listeners[0]({scope:scope}); +  assertEquals(321, scope.get("count")); +}; +ParserTest.prototype.testItShouldParseOnChangeBlockIntoHashSet = function () { +  var scope = new nglr.Scope({count:0}); +  var listeners = {a:[], b:[]}; +  scope.watch("a:{count=count+1;count=count+20;};b:count=count+300",  +      function(n, fn){listeners[n].push(fn);}); + +  assertEquals(1, scope.watchListeners.a.listeners.length); +  assertEquals(1, scope.watchListeners.b.listeners.length); +  scope.watchListeners["a"].listeners[0](); +  assertEquals(21, scope.get("count")); +  scope.watchListeners["b"].listeners[0](); +  assertEquals(321, scope.get("count")); +}; + +ParserTest.prototype.testItShouldParseEmptyOnChangeAsNoop = function () { +  var scope = new nglr.Scope(); +  scope.watch("", function(){fail();}); +}; + +ParserTest.prototype.testItShouldCreateClosureFunctionWithNoArguments = function () { +  var scope = new nglr.Scope(); +  var fn = scope.eval("{:value}"); +  scope.set("value", 1); +  assertEquals(1, fn()); +  scope.set("value", 2); +  assertEquals(2, fn()); +  fn = scope.eval("{():value}"); +  assertEquals(2, fn()); +}; + +ParserTest.prototype.testItShouldCreateClosureFunctionWithArguments = function () { +  var scope = new nglr.Scope(); +  var fn = scope.eval("{(a):value+a}"); +  scope.set("value", 1); +  assertEquals(11, fn(10)); +  scope.set("value", 2); +  assertEquals(12, fn(10)); +  fn = scope.eval("{(a,b):value+a+b}"); +  assertEquals(112, fn(10, 100)); +}; + +ParserTest.prototype.testItShouldHaveDefaultArugument = function(){ +  var scope = new nglr.Scope(); +  var fn = scope.eval("{:$*2}"); +  assertEquals(4, fn(2)); +}; + +ParserTest.prototype.testReturnFunctionsAreNotBound = function(){ +  var scope = new nglr.Scope(); +  scope.set("$datastore", new nglr.DataStore()); +  scope.entity("Group"); +  var Group = scope.get("Group"); +  assertEquals("eval Group", "function", typeof scope.eval("Group")); +  assertEquals("direct Group", "function", typeof Group); +  assertEquals("eval Group.all", "function", typeof scope.eval("Group.query")); +  assertEquals("direct Group.all", "function", typeof Group.query); +}; + diff --git a/test/ScopeTest.js b/test/ScopeTest.js new file mode 100644 index 00000000..c66a2329 --- /dev/null +++ b/test/ScopeTest.js @@ -0,0 +1,144 @@ +ScopeTest = TestCase('ScopeTest'); + +ScopeTest.prototype.testGetScopeRetrieval = function(){ +  var scope = {}; +  var form = jQuery("<a><b><c></c></b></a>"); +  form.data('scope', scope); +  var c = form.find('c'); +  assertTrue(scope === c.scope()); +}; + +ScopeTest.prototype.testGetScopeRetrievalIntermediateNode = function(){ +  var scope = {}; +  var form = jQuery("<a><b><c></c></b></a>"); +  form.find("b").data('scope', scope); +  var b = form.find('b'); +  assertTrue(scope === b.scope()); +}; + +ScopeTest.prototype.testNoScopeDoesNotCauseInfiniteRecursion = function(){ +  var form = jQuery("<a><b><c></c></b></a>"); +  var c = form.find('c'); +  assertTrue(!c.scope()); +}; + +ScopeTest.prototype.testScopeEval = function(){ +  var scope = new nglr.Scope({b:345}); +  assertEquals(scope.eval('b = 123'), 123); +  assertEquals(scope.get('b'), 123); +}; + +ScopeTest.prototype.testScopeFromPrototype = function(){ +  var scope = new nglr.Scope({b:123}); +  scope.eval('a = b'); +  scope.eval('b = 456'); +  assertEquals(scope.get('a'), 123); +  assertEquals(scope.get('b'), 456); +}; + +ScopeTest.prototype.testSetScopeGet = function(){ +  var scope = new nglr.Scope(); +  scope.set('a', 987); +  assertEquals(scope.get('a'), 987); +  assertEquals(scope.eval('a'), 987); +}; + +ScopeTest.prototype.testGetChain = function(){ +  var scope = new nglr.Scope({a:{b:987}}); +  assertEquals(scope.get('a.b'), 987); +  assertEquals(scope.eval('a.b'), 987); +}; + +ScopeTest.prototype.testGetUndefinedChain = function(){ +  var scope = new nglr.Scope(); +  assertEquals(typeof scope.get('a.b'),  'undefined'); +}; + +ScopeTest.prototype.testSetChain = function(){ +  var scope = new nglr.Scope({a:{}}); +  scope.set('a.b', 987); +  assertEquals(scope.get('a.b'), 987); +  assertEquals(scope.eval('a.b'), 987); +}; + +ScopeTest.prototype.testSetGetOnChain = function(){ +  var scope = new nglr.Scope(); +  scope.set('a.b', 987); +  assertEquals(scope.get('a.b'), 987); +  assertEquals(scope.eval('a.b'), 987); +}; + +ScopeTest.prototype.testGlobalFunctionAccess =function(){ +  window['scopeAddTest'] = function (a, b) {return a+b;}; +  var scope = new nglr.Scope({window:window}); +  assertEquals(scope.eval('window.scopeAddTest(1,2)'), 3); + +  scope.set('add', function (a, b) {return a+b;}); +  assertEquals(scope.eval('add(1,2)'), 3); + +  scope.set('math.add', function (a, b) {return a+b;}); +  assertEquals(scope.eval('math.add(1,2)'), 3); +}; + +ScopeTest.prototype.testValidationEval = function(){ +  expectAsserts(4); +  var scope = new nglr.Scope(); +  angular.validator.testValidator = function(value, expect){ +    assertEquals(scope, this.scope); +    return value == expect ? null : "Error text"; +  }; + +  assertEquals("Error text", scope.validate("testValidator:'abc'", 'x')); +  assertEquals(null, scope.validate("testValidator:'abc'", 'abc')); + +  delete angular.validator['testValidator']; +}; + +ScopeTest.prototype.testCallingNonExistantMethodShouldProduceFriendlyException = function() { +  expectAsserts(1); +  var scope = new nglr.Scope({obj:{}}); +  try { +    scope.eval("obj.iDontExist()"); +    fail(); +  } catch (e) { +    assertEquals("Expression 'obj.iDontExist' is not a function.", e); +  } +}; + +ScopeTest.prototype.testAccessingWithInvalidPathShouldThrowError = function() { +  var scope = new nglr.Scope(); +  try { +    scope.get('a.{{b}}'); +    fail(); +  } catch (e) { +    assertEquals("Expression 'a.{{b}}' is not a valid expression for accesing variables.", e); +  } +}; + +ScopeTest.prototype.testItShouldHave$parent = function() { +  var parent = new nglr.Scope({}, "ROOT"); +  var child = new nglr.Scope(parent.state); +  assertSame("parent", child.state.$parent, parent.state); +  assertSame("root", child.state.$root, parent.state); +}; + +ScopeTest.prototype.testItShouldHave$root = function() { +  var scope = new nglr.Scope({}, "ROOT"); +  assertSame(scope.state.$root, scope.state); +}; + +ScopeTest.prototype.testItShouldBuildPathOnUndefined = function(){ +  var scope = new nglr.Scope({}, "ROOT"); +  scope.setEval("a.$b.c", 1); +  assertJsonEquals({$b:{c:1}}, scope.get("a")); +}; + +ScopeTest.prototype.testItShouldMapUnderscoreFunctions = function(){ +  var scope = new nglr.Scope({}, "ROOT"); +  scope.set("a", [1,2,3]); +  assertEquals('function', typeof scope.get("a.$size")); +  scope.eval("a.$includeIf(4,true)"); +  assertEquals(4, scope.get("a.$size")()); +  assertEquals(4, scope.eval("a.$size()")); +  assertEquals('undefined', typeof scope.get("a.dontExist")); +}; diff --git a/test/ServerTest.js b/test/ServerTest.js new file mode 100644 index 00000000..d1f662f9 --- /dev/null +++ b/test/ServerTest.js @@ -0,0 +1,42 @@ +ServerTest = TestCase("ServerTest"); +ServerTest.prototype.testBreakLargeRequestIntoPackets = function() { +  var log = ""; +  var server = new nglr.Server("http://server", function(url){ +    log += "|" + url; +  }); +  server.maxSize = 30; +  server.uuid = "uuid"; +  server.request("POST", "/data/database", {}, function(code, r){ +    assertEquals(200, code); +    assertEquals("response", r); +  }); +  nglr.uuid0("response"); +  assertEquals( +      "|http://server/$/uuid0/2/1?h=eyJtIjoiUE9TVCIsInAiOnt9LCJ1Ij" + +      "|http://server/$/uuid0/2/2?h=oiL2RhdGEvZGF0YWJhc2UifQ==", +      log); +}; + +ServerTest.prototype.testItShouldEncodeUsingUrlRules = function() { +  var server = new nglr.Server("http://server"); +  assertEquals("fn5-fn5-", server.base64url("~~~~~~")); +  assertEquals("fn5_fn5_", server.base64url("~~\u007f~~\u007f")); +}; + +FrameServerTest = TestCase("FrameServerTest"); + +FrameServerTest.prototype = { +  testRead:function(){ +    var window = {name:'$DATASET:"MyData"'}; +    var server = new nglr.FrameServer(window); +    server.read(); +    assertEquals("MyData", server.data); +  }, +  testWrite:function(){ +    var window = {}; +    var server = new nglr.FrameServer(window); +    server.data = "TestData" +    server.write(); +    assertEquals('$DATASET:"TestData"', window.name); +  } +}; diff --git a/test/UsersTest.js b/test/UsersTest.js new file mode 100644 index 00000000..c808885c --- /dev/null +++ b/test/UsersTest.js @@ -0,0 +1,26 @@ +// Copyright (C) 2008,2009 BRAT Tech LLC + +UsersTest = TestCase("UsersTest"); + +UsersTest.prototype = { +  setUp:function(){}, +   +  tearDown:function(){}, +   +  testItShouldFetchCurrentUser:function(){ +    expectAsserts(5); +    var user; +    var users = new nglr.Users({request:function(method, url, request, callback){ +      assertEquals("GET", method); +      assertEquals("/account.json", url); +      assertEquals("{}", nglr.toJson(request)); +      callback(200, {$status_code:200, user:{name:'misko'}}); +    }}); +    users.fetchCurrentUser(function(u){ +      user = u; +      assertEquals("misko", u.name); +      assertEquals("misko", users.current.name); +    }); +  } +   +}; diff --git a/test/ValidatorsTest.js b/test/ValidatorsTest.js new file mode 100644 index 00000000..22c7f390 --- /dev/null +++ b/test/ValidatorsTest.js @@ -0,0 +1,65 @@ +ValidatorTest = TestCase('ValidatorTest'); + +ValidatorTest.prototype.testRegexp = function() { +  assertEquals(angular.validator.regexp("abc", /x/, "E1"), "E1"); +  assertEquals(angular.validator.regexp("abc", '/x/'), +      "Value does not match expected format /x/."); +  assertEquals(angular.validator.regexp("ab", '^ab$'), null); +  assertEquals(angular.validator.regexp("ab", '^axb$', "E3"), "E3"); +}; + +ValidatorTest.prototype.testNumber = function() { +  assertEquals(angular.validator.number("ab"), "Value is not a number."); +  assertEquals(angular.validator.number("-0.1",0), "Value can not be less than 0."); +  assertEquals(angular.validator.number("10.1",0,10), "Value can not be greater than 10."); +  assertEquals(angular.validator.number("1.2"), null); +  assertEquals(angular.validator.number(" 1 ", 1, 1), null); +}; + +ValidatorTest.prototype.testInteger = function() { +  assertEquals(angular.validator.integer("ab"), "Value is not a number."); +  assertEquals(angular.validator.integer("1.1"), "Value is not a whole number."); +  assertEquals(angular.validator.integer("-1",0), "Value can not be less than 0."); +  assertEquals(angular.validator.integer("11",0,10), "Value can not be greater than 10."); +  assertEquals(angular.validator.integer("1"), null); +  assertEquals(angular.validator.integer(" 1 ", 1, 1), null); +}; + +ValidatorTest.prototype.testDate = function() { +  var error = "Value is not a date. (Expecting format: 12/31/2009)."; +  assertEquals(angular.validator.date("ab"), error); +  assertEquals(angular.validator.date("12/31/2009"), null); +}; + +ValidatorTest.prototype.testPhone = function() { +  var error = "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."; +  assertEquals(angular.validator.phone("ab"), error); +  assertEquals(null, angular.validator.phone("1(408)757-3023")); +  assertEquals(null, angular.validator.phone("+421 (0905) 933 297")); +  assertEquals(null, angular.validator.phone("+421 0905 933 297")); +}; + +ValidatorTest.prototype.testSSN = function() { +  var error = "SSN needs to be in 999-99-9999 format."; +  assertEquals(angular.validator.ssn("ab"), error); +  assertEquals(angular.validator.ssn("123-45-6789"), null); +}; + +ValidatorTest.prototype.testURL = function() { +  var error = "URL needs to be in http://server[:port]/path format."; +  assertEquals(angular.validator.url("ab"), error); +  assertEquals(angular.validator.url("http://server:123/path"), null); +}; + +ValidatorTest.prototype.testEmail = function() { +  var error = "Email needs to be in username@host.com format."; +  assertEquals(error, angular.validator.email("ab")); +  assertEquals(null, angular.validator.email("misko@hevery.com")); +}; + +ValidatorTest.prototype.testJson = function() { +  assertNotNull(angular.validator.json("'")); +  assertNotNull(angular.validator.json("''X")); +  assertNull(angular.validator.json("{}")); +}; + diff --git a/test/WidgetsTest.js b/test/WidgetsTest.js new file mode 100644 index 00000000..a245abda --- /dev/null +++ b/test/WidgetsTest.js @@ -0,0 +1,269 @@ +WidgetTest = TestCase('WidgetTest'); + +WidgetTest.prototype.testRequired = function () { +  var view = $('<input name="a" ng-required>'); +  var scope = new nglr.Scope({$invalidWidgets:[]}); +  var cntl = new nglr.TextController(view[0], 'a'); +  cntl.updateView(scope); +  assertTrue(view.hasClass('ng-validation-error')); +  assertEquals("Required Value", view.attr('ng-error')); +  scope.set('a', 'A'); +  cntl.updateView(scope); +  assertFalse(view.hasClass('ng-validation-error')); +  assertEquals("undefined", typeof view.attr('ng-error')); +}; + +WidgetTest.prototype.testValidator = function () { +  var view = $('<input name="a" ng-validate="testValidator:\'ABC\'">'); +  var scope = new nglr.Scope({$invalidWidgets:[]}); +  var cntl = new nglr.TextController(view[0], 'a'); +  angular.validator.testValidator = function(value, expect){ +    return value == expect ? null : "Error text"; +  }; + +  scope.set('a', ''); +  cntl.updateView(scope); +  assertEquals(view.hasClass('ng-validation-error'), false); +  assertEquals(null, view.attr('ng-error')); + +  scope.set('a', 'X'); +  cntl.updateView(scope); +  assertEquals(view.hasClass('ng-validation-error'), true); +  assertEquals(view.attr('ng-error'), "Error text"); +  assertEquals("Error text", view.attr('ng-error')); + +  scope.set('a', 'ABC'); +  cntl.updateView(scope); +  assertEquals(view.hasClass('ng-validation-error'), false); +  assertEquals(view.attr('ng-error'), null); +  assertEquals(null, view.attr('ng-error')); + +  delete angular.validator['testValidator']; +}; + +WidgetTest.prototype.testRequiredValidator = function () { +  var view = $('<input name="a" ng-required ng-validate="testValidator:\'ABC\'">'); +  var scope = new nglr.Scope({$invalidWidgets:[]}); +  var cntl = new nglr.TextController(view[0], 'a'); +  angular.validator.testValidator = function(value, expect){ +    return value == expect ? null : "Error text"; +  }; + +  scope.set('a', ''); +  cntl.updateView(scope); +  assertEquals(view.hasClass('ng-validation-error'), true); +  assertEquals("Required Value", view.attr('ng-error')); + +  scope.set('a', 'X'); +  cntl.updateView(scope); +  assertEquals(view.hasClass('ng-validation-error'), true); +  assertEquals("Error text", view.attr('ng-error')); + +  scope.set('a', 'ABC'); +  cntl.updateView(scope); +  assertEquals(view.hasClass('ng-validation-error'), false); +  assertEquals(null, view.attr('ng-error')); + +  delete angular.validator['testValidator']; +}; + +TextController = TestCase("TextController"); + +TextController.prototype.testDatePicker = function() { +  var input = $('<input type="text" ng-widget="datepicker">'); +  input.data('scope', new nglr.Scope()); +  var body = $(document.body); +  body.append(input); +  var binder = new nglr.Binder(input[0], new nglr.WidgetFactory()); +  assertTrue('before', input.data('datepicker') === undefined); +  binder.compile(); +  assertTrue('after', input.data('datepicker') !== null); +  assertTrue(body.html(), input.hasClass('hasDatepicker')); +}; + +RepeaterUpdater = TestCase("RepeaterUpdater"); + +RepeaterUpdater.prototype.testRemoveThenAdd = function() { +  var view = $("<div><span/></div>"); +  var template = function () { +    return $("<li/>"); +  }; +  var repeater = new nglr.RepeaterUpdater(view.find("span"), "a in b", template, ""); +  var scope = new nglr.Scope(); +  scope.set('b', [1,2]); + +  repeater.updateView(scope); + +  scope.set('b', []); +  repeater.updateView(scope); + +  scope.set('b', [1]); +  repeater.updateView(scope); +  assertEquals(1, view.find("li").size()); +}; + +RepeaterUpdater.prototype.testShouldBindWidgetOnRepeaterClone = function(){ +  //fail(); +}; + +RepeaterUpdater.prototype.testShouldThrowInformativeSyntaxError= function(){ +  expectAsserts(1); +  try { +    var repeater = new nglr.RepeaterUpdater(null, "a=b"); +  } catch (e) { +    assertEquals("Expected ng-repeat in form of 'item in collection' but got 'a=b'.", e); +  } +}; + +SelectControllerTest = TestCase("SelectControllerTest"); + +SelectControllerTest.prototype.testShouldUpdateModelNullOnNothingSelected = function(){ +  var scope = new nglr.Scope(); +  var view = {selectedIndex:-1, options:[]}; +  var cntl = new nglr.SelectController(view, 'abc'); +  cntl.updateModel(scope); +  assertNull(scope.get('abc')); +}; + +SelectControllerTest.prototype.testShouldUpdateModelWhenNothingSelected = function(){ +  var scope = new nglr.Scope(); +  var view = {value:'123'}; +  var cntl = new nglr.SelectController(view, 'abc'); +  cntl.updateView(scope); +  assertEquals("123", scope.get('abc')); +}; + +BindUpdaterTest = TestCase("BindUpdaterTest"); + +BindUpdaterTest.prototype.testShouldDisplayNothingForUndefined = function () { +  var view = $('<span />'); +  var controller = new nglr.BindUpdater(view[0], "{{a}}"); +  var scope = new nglr.Scope(); + +  scope.set('a', undefined); +  controller.updateView(scope); +  assertEquals("", view.text()); + +  scope.set('a', null); +  controller.updateView(scope); +  assertEquals("", view.text()); +}; + +BindUpdaterTest.prototype.testShouldDisplayJsonForNonStrings = function () { +  var view = $('<span />'); +  var controller = new nglr.BindUpdater(view[0], "{{obj}}"); + +  controller.updateView(new nglr.Scope({obj:[]})); +  assertEquals("[]", view.text()); + +  controller.updateView(new nglr.Scope({obj:{text:'abc'}})); +  assertEquals('abc', nglr.fromJson(view.text()).text); +}; + + +BindUpdaterTest.prototype.testShouldInsertHtmlNode = function () { +  var view = $('<span />'); +  var controller = new nglr.BindUpdater(view[0], "<fake>&{{obj}}</fake>"); +  var scope = new nglr.Scope(); + +  scope.set("obj", $('<div>myDiv</div>')[0]); +  controller.updateView(scope); +  assertEquals("<fake>&myDiv</fake>", view.text()); +}; + + +BindUpdaterTest.prototype.testShouldDisplayTextMethod = function () { +  var view = $('<div />'); +  var controller = new nglr.BindUpdater(view[0], "{{obj}}"); +  var scope = new nglr.Scope(); + +  scope.set("obj", new angular.filter.Meta({text:function(){return "abc";}})); +  controller.updateView(scope); +  assertEquals("abc", view.text()); + +  scope.set("obj", new angular.filter.Meta({text:"123"})); +  controller.updateView(scope); +  assertEquals("123", view.text()); + +  scope.set("obj", {text:"123"}); +  controller.updateView(scope); +  assertEquals("123", nglr.fromJson(view.text()).text); +}; + +BindUpdaterTest.prototype.testShouldDisplayHtmlMethod = function () { +  var view = $('<div />'); +  var controller = new nglr.BindUpdater(view[0], "{{obj}}"); +  var scope = new nglr.Scope(); + +  scope.set("obj", new angular.filter.Meta({html:function(){return "a<div>b</div>c";}})); +  controller.updateView(scope); +  assertEquals("abc", view.text()); + +  scope.set("obj", new angular.filter.Meta({html:"1<div>2</div>3"})); +  controller.updateView(scope); +  assertEquals("123", view.text()); + +  scope.set("obj", {html:"123"}); +  controller.updateView(scope); +  assertEquals("123", nglr.fromJson(view.text()).html); +}; + +BindUpdaterTest.prototype.testUdateBoolean = function() { +  var view = $('<div />'); +  var controller = new nglr.BindUpdater(view[0], "{{true}}, {{false}}"); +  controller.updateView(new nglr.Scope()); +  assertEquals('true, false', view.text()); +}; + +BindAttrUpdaterTest = TestCase("BindAttrUpdaterTest"); + +BindAttrUpdaterTest.prototype.testShouldLoadBlankImageWhenBindingIsUndefined = function () { +  var view = $('<img />'); +  var controller = new nglr.BindAttrUpdater(view[0], {src: '{{imageUrl}}'}); + +  var scope = new nglr.Scope(); +  scope.set('imageUrl', undefined); +  scope.set('config.server', 'http://server'); + +  controller.updateView(scope); +  assertEquals("http://server/images/blank.gif", view.attr('src')); +}; + +RepeaterUpdaterTest = TestCase("RepeaterUpdaterTest"); +RepeaterUpdaterTest.prototype.testShouldNotDieWhenRepeatExpressionIsNull = function() { +  var rep = new nglr.RepeaterUpdater(null, "$item in items", null, null); +  var scope = new nglr.Scope(); +  scope.set('items', undefined); +  rep.updateView(scope); +}; + +RepeaterUpdaterTest.prototype.testShouldIterateOverKeys = function() { +  var rep = new nglr.RepeaterUpdater(null, "($k,_v) in items", null, null); +  assertEquals("items", rep.iteratorExp); +  assertEquals("_v", rep.valueExp); +  assertEquals("$k", rep.keyExp); +}; + +EvalUpdaterTest = TestCase("EvalUpdaterTest"); +EvalUpdaterTest.prototype.testEvalThrowsException = function(){ +  var view = $('<div/>'); +  var eval = new nglr.EvalUpdater(view[0], 'undefined()'); + +  eval.updateView(new nglr.Scope()); +  assertTrue(!!view.attr('ng-error')); +  assertTrue(view.hasClass('ng-exception')); + +  eval.exp = "1"; +  eval.updateView(new nglr.Scope()); +  assertFalse(!!view.attr('ng-error')); +  assertFalse(view.hasClass('ng-exception')); +}; + +RadioControllerTest = TestCase("RadioController"); +RadioControllerTest.prototype.testItShouldTreatTrueStringAsBoolean = function () { +  var view = $('<input type="radio" name="select" value="true"/>'); +  var radio = new nglr.RadioController(view[0], 'select'); +  var scope = new nglr.Scope({select:true}); +  radio.updateView(scope); +  assertTrue(view[0].checked); +}; diff --git a/test/XSitePostTest.js b/test/XSitePostTest.js new file mode 100644 index 00000000..8a3e4d6f --- /dev/null +++ b/test/XSitePostTest.js @@ -0,0 +1,47 @@ +XSitePost = TestCase("XSitePost"); + +var e = function(text){ return Base64.encode(text); }; + +XSitePost.prototype.testMessageReceived = function () { +  expectAsserts(4); +  var xPost = new nglr.XSitePost(); +  xPost.baseUrl = "http://getangular.test"; +  xPost.post = function(url, request, callback){ +    assertEquals('http://getangular.test/url', url); +    assertEquals('abc', request.a); +    assertEquals('xyz', request.x); +  }; +  xPost.incomingFragment('#id;0;1;'+e('/url')+':a:'+e('abc')+':x:'+e('xyz')); +  assertEquals('{}', nglr.toJson(xPost.inQueue)); +}; + +XSitePost.prototype.testMessageReceivedInParts = function () { +  expectAsserts(5); +  var xPost = new nglr.XSitePost(); +  xPost.baseUrl = "http://getangular.test"; +  xPost.post = function(url, request, callback){ +    assertEquals('http://getangular.test/url', url); +    assertEquals('abc', request.a); +    assertEquals('xyz', request.x); +  }; +  xPost.incomingFragment('#id;1;2;:x:'+e('xyz')); +  assertNotSame('{}', nglr.toJson(xPost.inQueue)); +  xPost.incomingFragment('#id;0;2;'+e('/url')+':a:'+e('abc')); +  assertEquals('{}', nglr.toJson(xPost.inQueue)); +}; + +XSitePost.prototype.testPostResponsIsEnqueued = function () { +  var xPost = new nglr.XSitePost(); +  xPost.maxMsgSize = 11; +  xPost.response("id", "response", "status"); + +  assertEquals('["id:0:2:cmVzcG9uc2U","id:1:2:="]', +      nglr.toJson(xPost.outQueue)); +}; + +XSitePost.prototype.testPush = function () { +  var window = {}; +  var xPost = new nglr.XSitePost(window); +  xPost.response("id", "response", "status"); +  assertEquals('id:0:1:cmVzcG9uc2U=', xPost.outQueue[0]); +}; diff --git a/test/formsTest.js b/test/formsTest.js new file mode 100644 index 00000000..e834e938 --- /dev/null +++ b/test/formsTest.js @@ -0,0 +1,22 @@ +nglrTest = TestCase('nglrTest'); + +nglrTest.prototype.testShiftBind = function(){ +  expectAsserts(3); +  nglr.shiftBind('this', function(target, arg) { +    assertEquals(this, 'this'); +    assertEquals(target, 'target'); +    assertEquals(arg, 'arg'); +  }).apply('target', ['arg']); +}; + +nglrTest.prototype.testBind = function(){ +  expectAsserts(2); +  nglr.bind('this', function(arg) { +    assertEquals(this, 'this'); +    assertEquals(arg, 'arg'); +  }).apply('XXX', ['arg']); +}; + + + + diff --git a/test/test/StepsTest.js b/test/test/StepsTest.js new file mode 100644 index 00000000..9d64d0a9 --- /dev/null +++ b/test/test/StepsTest.js @@ -0,0 +1,7 @@ +StepsTest = TestCase("StepsTest"); + +StepsTest.prototype.testGivenDataset=function(){ +  var self = {frame:{}, dataset:[]}; +  angular.test.GIVEN.dataset.call(self); +  assertEquals('$DATASET:{"dataset":[]}', self.frame.name); +}; diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js new file mode 100644 index 00000000..5fca3524 --- /dev/null +++ b/test/testabilityPatch.js @@ -0,0 +1,129 @@ +TestCase = function(name) { return jstestdriver.testCaseManager.TestCase(name); }; + +HIDDEN = jQuery.browser.msie ? +    '' : +    jQuery.browser.safari ? +         ' style="display: none; "' : +         ' style="display: none;"'; + +nglr.msie = jQuery.browser.msie; +nglr.alert = function(msg) {jstestdriver.console.log("ALERT: " + msg);}; + +function noop(){} + +jstd = jstestdriver; + +function html(content) { +  return jQuery("<div></div>").html(content); +} + +function report(reportTest){ +  $("#tests").children().each(function(i){ +    var success = this.className == "pass"; +    var strong = this.firstChild; +    var msg = strong.firstChild.nodeValue; +    var parts = msg.split(" module: "); +    var module = parts[0]; +    var name = parts[1].replace(/ *$/, ""); +    reportTest(success, module, name, this.nodeValue); +  }); +} + +MockUrlWatcher = function() { +  this.url = "http://server"; +}; +MockUrlWatcher.prototype.getUrl = function(){ +  return this.url; +}; +MockUrlWatcher.prototype.setUrl = function(url){ +  this.url = url; +}; + +jQuery.fn.sortedHtml = function() { +  var html = ""; +  var toString = function(index, node) { +    node = node || this; +    if (node.nodeName == "#text") { +      html += nglr.escapeHtml(node.nodeValue); +    } else { +      html += '<' + node.nodeName.toLowerCase(); +      var attributes = node.attributes || []; +      var attrs = []; +      for(var i=0; i<attributes.length; i++) { +        var attr = attributes[i]; +        if(attr.name.match(/^ng-/) || +            attr.value && +            attr.value !='null' && +            attr.value !='auto' && +            attr.value !='false' && +            attr.value !='inherit' && +            attr.value !='0' && +            attr.name !='loop' && +            attr.name !='maxLength' && +            attr.name !='size' && +            attr.name !='start' && +            attr.name !='tabIndex' && +            attr.name.substr(0, 6) != 'jQuery') { +          // in IE we need to check for all of these. +          attrs.push(' ' + attr.name + '="' + attr.value + '"'); +        } +      } +      attrs.sort(); +      html += attrs.join(''); +      html += '>'; +      var children = node.childNodes; +      for(var j=0; j<children.length; j++) { +        toString(j, children[j]); +      } +      html += '</' + node.nodeName.toLowerCase() + '>'; +    } +  }; +  this.children().each(toString); +  return html; +}; + +function encode64(obj){ +  return Base64.encode(nglr.toJson(obj)); +} + +function decode64(base64){ +  return nglr.fromJson(Base64.decode(base64)); +} + +nglr.Loader.prototype.configureJQueryPlugins(); + +function assertHidden(node) { +  var display = node.css('display'); +  assertEquals("Node should be hidden but vas visible: " + node.sortedHtml(), 'none', display); +} + +function assertVisible(node) { +  var display = node.css('display'); +  if (display == 'block') display = ""; +  assertEquals("Node should be visible but vas hidden: " + node.sortedHtml(), '', display); +} + +function assertJsonEquals(expected, actual) { +  assertEquals(nglr.toJson(expected), nglr.toJson(actual)); +} + +function assertUndefined(value) { +  assertEquals('undefined', typeof value); +} + +function assertDefined(value) { +  assertTrue(nglr.toJson(value), !!value); +} + +function assertThrows(error, fn){ +  var exception = null; +  try { +    fn(); +  } catch(e) { +    exception = e; +  } +  if (!exception) { +    fail("Expecting exception, none thrown"); +  } +  assertEquals(error, exception); +}
\ No newline at end of file | 
