diff options
| author | Misko Hevery | 2010-01-11 17:32:33 -0800 |
|---|---|---|
| committer | Misko Hevery | 2010-01-11 17:32:33 -0800 |
| commit | 6d5471c9bea9671dec7900a7bc548aea8ddd5a3e (patch) | |
| tree | 23a0affaa4b977b0eb776f515bd27eda89431049 | |
| parent | 1a42a3fab99ca02af0476f5a87175c53104aa2e3 (diff) | |
| download | angular.js-6d5471c9bea9671dec7900a7bc548aea8ddd5a3e.tar.bz2 | |
all files converted to prototype= {}
| -rw-r--r-- | angular-minified.js | 198 | ||||
| -rw-r--r-- | angular.js | 4140 | ||||
| -rw-r--r-- | src/Binder.js | 595 | ||||
| -rw-r--r-- | src/ControlBar.js | 106 | ||||
| -rw-r--r-- | src/DataStore.js | 604 | ||||
| -rw-r--r-- | src/Filters.js | 2 | ||||
| -rw-r--r-- | src/JSON.js | 10 | ||||
| -rw-r--r-- | src/Loader.js | 4 | ||||
| -rw-r--r-- | src/Model.js | 82 | ||||
| -rw-r--r-- | src/Parser.js | 1292 | ||||
| -rw-r--r-- | src/Scope.js | 295 | ||||
| -rw-r--r-- | src/Server.js | 44 | ||||
| -rw-r--r-- | src/Users.js | 11 | ||||
| -rw-r--r-- | src/Validators.js | 2 | ||||
| -rw-r--r-- | src/Widgets.js | 972 | ||||
| -rw-r--r-- | src/angular-bootstrap.js | 25 | ||||
| -rw-r--r-- | src/angular.prefix | 23 | ||||
| -rw-r--r-- | test/DataStoreTest.js | 3 |
18 files changed, 4261 insertions, 4147 deletions
diff --git a/angular-minified.js b/angular-minified.js index 9b4eec6e..2c702b48 100644 --- a/angular-minified.js +++ b/angular-minified.js @@ -1,99 +1,99 @@ -function p(){return function(){}}function A(n){return function(r){this[n]=r}}function E(n){return function(){return n}} -(function(n,r){function x(){}function M(a,b){var c=r.createElement("div");c.className=a;for(var d=a="",e=0;e<b.length;e++){var f=b[e];a+=d+(typeof f=="string"?f:toJson(f));d=" "}c.appendChild(r.createTextNode(a));F.appendChild(c)}function N(a){switch(a.nodeName){case "OPTION":case "PRE":case "TITLE":return true;default:return false}}function O(a,b){if(N(a))if(G)a.innerText=b;else a.textContent=b;else a.innerHTML=b}function C(a){if(!a||!a.replace)return a;return a.replace(/&/g,"&").replace(/</g, -"<").replace(/>/g,">")}function X(a){if(!a||!a.replace)return a;return a.replace(/</g,"<").replace(/>/g,">").replace(/\"/g,""")}function P(a,b){if(!a)throw"Missing this";if(!_.isFunction(b))throw"Missing function";return function(){return b.apply(a,arguments)}}function Y(a,b){return function(){for(var c=[this],d=0;d<arguments.length;d++)c.push(arguments[d]);return b.apply(a,c)}}function Q(a){var b=r.createElement("div");b.appendChild(a);var c=b.innerHTML;b.removeChild(a);return c} -function R(a){var b=(""+a).toLowerCase();if(b=="f"||b=="0"||b=="false"||b=="no")a=false;return!!a}function S(a,b){for(var c in a){var d=b[c],e=typeof d;if(e=="undefined")b[c]=fromJson(toJson(a[c]));else e=="object"&&d.constructor!=array&&c.substring(0,1)!="$"&&S(a[c],d)}}function T(a,b,c){this.document=i(a);this.head=i(b);this.i=c;this.location=n.location}function U(a){this.location=a;this.delay=25;this.setTimeout=function(b,c){n.setTimeout(b,c)};this.Ua=function(b){return b};this.N=a.href}var H= -{u:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",Tb:function(a){var b="",c,d,e,f,g,h,j=0;for(a=H.yb(a);j<a.length;){c=a.charCodeAt(j++);d=a.charCodeAt(j++);e=a.charCodeAt(j++);f=c>>2;c=(c&3)<<4|d>>4;g=(d&15)<<2|e>>6;h=e&63;if(isNaN(d))g=h=64;else if(isNaN(e))h=64;b=b+this.u.charAt(f)+this.u.charAt(c)+this.u.charAt(g)+this.u.charAt(h)}return b},sd:function(a){var b="",c,d,e,f,g,h=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");h<a.length;){c=this.u.indexOf(a.charAt(h++));d=this.u.indexOf(a.charAt(h++)); -f=this.u.indexOf(a.charAt(h++));g=this.u.indexOf(a.charAt(h++));c=c<<2|d>>4;d=(d&15)<<4|f>>2;e=(f&3)<<6|g;b+=String.fromCharCode(c);if(f!=64)b+=String.fromCharCode(d);if(g!=64)b+=String.fromCharCode(e)}return b=H.xb(b)},yb:function(a){a=a.replace(/\r\n/g,"\n");for(var b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);if(d<128)b+=String.fromCharCode(d);else{if(d>127&&d<2048)b+=String.fromCharCode(d>>6|192);else{b+=String.fromCharCode(d>>12|224);b+=String.fromCharCode(d>>6&63|128)}b+=String.fromCharCode(d& -63|128)}}return b},xb:function(a){for(var b="",c=0,d=c1=c2=0;c<a.length;){d=a.charCodeAt(c);if(d<128){b+=String.fromCharCode(d);c++}else if(d>191&&d<224){c2=a.charCodeAt(c+1);b+=String.fromCharCode((d&31)<<6|c2&63);c+=2}else{c2=a.charCodeAt(c+1);c3=a.charCodeAt(c+2);b+=String.fromCharCode((d&15)<<12|(c2&63)<<6|c3&63);c+=3}}return b}};if(typeof r.getAttribute=="undefined")r.getAttribute=p();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};var F,o=_.each,V=_.extend,I=n.console||{log:x,error:x},i=n.jQuery,G=i.browser.msie;function q(){I.log.apply(this,arguments)}function J(){I.error.apply(this,arguments)}var m=n.angular||(n.angular={}),W=m.validator||(m.validator={}),s=m.filter||(m.filter={}),K=m.callbacks||(m.callbacks={});m.alert||(m.alert=function(){q(arguments);n.alert.apply(n,arguments)}); -T.prototype={load:function(){this.Lb();this.Wa("/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css");this.Wa("/stylesheets/css");q("Server: "+this.i.z);this.Kb();this.Jb();this.Gb()},Kb:function(){q("Loader.configureJQueryPlugins()");i.fn.scope=function(){for(var a=this;a&&a.get(0);){var b=a.data("scope");if(b)return b;a=a.parent()}return null};i.fn.controller=function(){return this.data("controller")||NullController.cc}},Kc:function(){return""+(new Date).getTime()},Jb:function(){var a=this.i; -if(!a.G){var b=a.z.match(/https?:\/\/([\w]*)/);a.G=b?b[1]:"$MEMORY"}},Gb:function(){function a(){g.Ma(function(l){!l&&c.find("[ng-auth=eager]").length&&g.ba()})}q("Loader.bindHtml()");var b=new U(this.location),c=this.document,d=new WidgetFactory(this.i.z,this.i.G),e=new Binder(c[0],d,b,this.i);d.wc=Y(e,e.e);d=new ControlBar(c.find("body"),this.i.z);var f=this.i.G=="$MEMORY"?new FrameServer(this.window):new Server(this.i.z,i.getScript);f=new VisualServer(f,new Status(i(c.body)),function(){e.c()}); -var g=new Users(f,d),h="/data/"+this.i.G,j=new DataStore(function(l,y){f.P("POST",h,l,y)},g,e.anchor);e.lb.push(function(){j.Oa()});var k=new Scope({$anchor:e.anchor,$binder:e,$config:this.i,$console:n.console,$datastore:j,$save:function(l){j.Ec(k.s,l,e.anchor)},$window:n,$uid:this.Kc,$users:g},"ROOT");c.data("scope",k);q("$binder.entity()");e.M(k);q("$binder.compile()");e.compile();q("ControlBar.bind()");d.bind();q("$users.fetchCurrentUser()");a();q("PopUp.bind()");(new PopUp(c)).bind();q("$binder.parseAnchor()"); -e.db();q("$binder.executeInit()");e.Xb();q("$binder.updateView()");e.c();b.Ua=P(e,e.yc,b);b.zd=function(){alert("update")};b.watch();c.find("body").show();q("ready()")},Lb:function(){var a=n.location.href+"#";a=a.split("#")[1];var b={Qb:null};a=a.split("&");for(var c=0;c<a.length;c++){var d=(a[c]+"=").split("=");b[d[0]]=d[1]}if(b.Qb=="console"){F=r.createElement("div");F.id="ng-console";r.getElementsByTagName("body")[0].appendChild(F);q=function(){M("ng-console-info",arguments)};I.error=function(){M("ng-console-error", -arguments)}}},Wa:function(a){var b=r.createElement("link");b.rel="stylesheet";b.type="text/css";a.match(/^http:/)||(a=this.i.z+a);b.href=a;this.head[0].appendChild(b)}};U.prototype={watch:function(){var a=this;function b(){if(a.N!==a.location.href){var c=a.location.hash.match(/^#\$iframe_notify=(.*)$/);if(c){a.N.match(/#/)||(a.N+="#");a.location.href=a.N;c="_iframe_notify_"+c[1];var d=K[c];delete K[c];try{(d||x)()}catch(e){alert(e)}}else{a.Ua(a.location.href);a.N=a.location.href}}a.setTimeout(b,a.delay)} -b()},Hc:function(a){var b=n.location.href;b.match(/#/)||(b+="#");if(b!=a)n.location.href=a;this.ud=a},Qa:function(){return n.location.href}};m.compile=function(a,b){b=b||{};(new T(a,i("head"),_({z:""}).extend(b))).load();var c=i(a).scope();return{updateView:function(){return c.c.apply(c,arguments)},set:function(){return c.j.apply(c,arguments)},get:function(){return c.get.apply(c,arguments)}}};var D={typeOf:function(a){if(a===null)return"null";var b=typeof a;if(b=="object"){if(a instanceof Array)return"array"; -if(a instanceof Date)return"date";if(a.nodeType==1)return"element"}return b}},L={};(function(){function a(b,c,d){o(c,function(e){V(m[b],e)});o(d,function(e){m[b][e]=_[e]})}a("Global",[D],["extend","clone","isEqual","isElement","isArray","isFunction","isUndefined"]);a("Collection",[D,L],["each","map","reduce","reduceRight","detect","select","reject","all","any","include","invoke","pluck","max","min","sortBy","sortedIndex","toArray","size"]);a("Array",[D,L],["first","last","compact","flatten","without", -"uniq","intersect","zip","indexOf","lastIndexOf"]);a("Object",[D,L],["keys","values"]);a("String",[D]);a("Function",[D],["bind","bindAll","delay","defer","wrap","compose"])})();Binder=function(a,b,c,d){this.B=a;this.va=c;this.anchor={};this.Rc=b;this.i=d||{};this.lb=[]};Binder.da=function(a){for(var b=[],c=0,d;(d=a.indexOf("{{",c))>-1;){c<d&&b.push(a.substr(c,d-c));c=d;d=a.indexOf("}}",d);d=d<0?a.length:d+2;b.push(a.substr(c,d-c));c=d}c!=a.length&&b.push(a.substr(c,a.length-c));return b.length=== -0?[a]:b};Binder.bc=function(a){a=Binder.da(a);return a.length>1||Binder.L(a[0])!==null};Binder.L=function(a){return(a=a.replace(/\n/gm," ").match(/^\{\{(.*)\}\}$/))?a[1]:null};Binder.prototype.zc=function(a){var b={};a.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,function(c,d,e){if(d)b[decodeURIComponent(d)]=decodeURIComponent(e)});return b};Binder.prototype.db=function(a){var b=this;a=a||this.va.Qa();var c=a.indexOf("#");if(!(c<0)){a=this.zc(a.substring(c+1));o(b.anchor,function(d,e){delete b.anchor[e]}); -o(a,function(d,e){b.anchor[e]=d})}};Binder.prototype.yc=function(a){q("URL change detected",a);this.db(a);this.c()};Binder.prototype.Lc=function(){var a=this.va.Qa(),b=a.indexOf("#");if(b>-1)a=a.substring(0,b);a+="#";b="";for(var c in this.anchor){var d=this.anchor[c];if(typeof d==="undefined"||d===null)delete this.anchor[c];else{a+=b+encodeURIComponent(c);if(d!==true)a+="="+encodeURIComponent(d);b="&"}}this.va.Hc(a);return a};Binder.prototype.c=function(){(new Date).getTime();var a=i(this.B).scope(); -a.j("$invalidWidgets",[]);a.c();(new Date).getTime();this.Lc();_.each(this.lb,function(b){b()})};Binder.prototype.W=function(a){var b=i(this.B),c=b.find(a);if(b.is(a))c=c.andSelf();return c};Binder.prototype.Xb=function(){this.W("[ng-init]").each(function(){var a=i(this),b=a.scope();try{b.eval(a.attr("ng-init"))}catch(c){alert("EVAL ERROR:\n"+a.attr("ng-init")+"\n"+toJson(c,true))}})};Binder.prototype.M=function(a){this.W("[ng-entity]").attr("ng-watch",function(){try{var b=i(this);return a.M(b.attr("ng-entity"))+ -(b.attr("ng-watch")||"")}catch(c){alert(c)}})};Binder.prototype.compile=function(){var a=i(this.B),b=this;if(this.i.$c){var c=this.W(":submit").not("[ng-action]");c.attr("ng-action","$save()");c.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr",'{disabled:"{{$invalidWidgets}}"}')}this.eb(this.B)(this.B,a.scope(),"");this.W("a[ng-action]").live("click",function(){var d=i(this);try{d.scope().eval(d.attr("ng-action"));d.removeAttr("ng-error");d.removeClass("ng-exception")}catch(e){d.addClass("ng-exception"); -d.attr("ng-error",toJson(e,true))}b.c();return false})};Binder.prototype.Jc=function(a,b,c){b=b.concat();var d=b.pop(),e=Binder.da(a.nodeValue);if(e.length>1||Binder.L(e[0])){var f=a.parentNode;if(N(f)){f.setAttribute("ng-bind-template",a.nodeValue);c.push({path:b,b:function(l){return new BindUpdater(l,l.getAttribute("ng-bind-template"))}})}else for(var g=0;g<e.length;g++){var h=e[g],j=Binder.L(h),k;if(j){k=r.createElement("span");i(k).attr("ng-bind",j);g===0&&c.push({path:b.concat(d+g),b:Binder.prototype.cb})}else if(G&& -h.charAt(0)==" "){k=r.createElement("span");k.innerHTML=" "+h.substring(1)}else k=r.createTextNode(h);f.insertBefore(k,a)}f.removeChild(a)}};Binder.prototype.eb=function(a){var b=[];this.fb(a,[],b);return function(c,d,e){for(var f=b.length,g=0;g<f;g++){for(var h=b[g],j=c,k=h.path,l=0;l<k.length;l++)j=j.childNodes[k[l]];try{d.Bb(h.b(j,d,e))}catch(y){alert(y)}}}};Binder.prototype.fb=function(a,b,c){var d=a.nodeType;if(d==Node.TEXT_NODE)this.Jc(a,b,c);else if(!(d!=Node.ELEMENT_NODE&&d!=Node.DOCUMENT_NODE))if(a.getAttribute){d= -a.getAttribute("ng-non-bindable");if(!(d||d==="")){if(d=a.attributes){var e=a.getAttribute("ng-bind-attr");a.removeAttribute("ng-bind-attr");e=e?fromJson(e):{};for(var f=d.length,g=0;g<f;g++){var h=d[g],j=h.name;h=G&&j=="href"?decodeURI(a.getAttribute(j,2)):h.value;if(Binder.bc(h))e[j]=h}d=toJson(e);d.length>2&&a.setAttribute("ng-bind-attr",d)}a.getAttribute||q(a);var k=a.getAttribute("ng-repeat");if(k){a.removeAttribute("ng-repeat");var l=this.eb(a);d=r.createComment("ng-repeat: "+k);e=a.parentNode; -e.insertBefore(d,a);e.removeChild(a);var y=function(t,u,w){var B=i(a).clone();B.css("display","");B.attr("ng-repeat-index",""+w);B.data("scope",t);l(B[0],t,u+w+":");return B};c.push({path:b,b:function(t,u,w){return new RepeaterUpdater(i(t),k,y,w)}})}else{a.getAttribute("ng-eval")&&c.push({path:b,b:this.qc});a.getAttribute("ng-bind")&&c.push({path:b,b:this.cb});a.getAttribute("ng-bind-attr")&&c.push({path:b,b:this.mc});a.getAttribute("ng-hide")&&c.push({path:b,b:this.rc});a.getAttribute("ng-show")&& -c.push({path:b,b:this.sc});a.getAttribute("ng-class")&&c.push({path:b,b:this.nc});a.getAttribute("ng-class-odd")&&c.push({path:b,b:this.pc});a.getAttribute("ng-class-even")&&c.push({path:b,b:this.oc});a.getAttribute("ng-style")&&c.push({path:b,b:this.tc});a.getAttribute("ng-watch")&&c.push({path:b,b:this.uc});d=a.nodeName;if(d=="INPUT"||d=="TEXTAREA"||d=="SELECT"||d=="BUTTON"){var z=this;c.push({path:b,b:function(t,u,w){t.name=w+t.name.split(":").pop();return z.Rc.Mb(i(t),u)}})}if(d=="OPTION")if(!i("<select/>").append(i(a).clone()).html().match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi))a.value= -a.text;d=a.childNodes;for(e=0;e<d.length;e++)this.fb(d[e],b.concat(e),c)}}}};Binder.prototype.qc=function(a){return new EvalUpdater(a,a.getAttribute("ng-eval"))};Binder.prototype.cb=function(a){return new BindUpdater(a,"{{"+a.getAttribute("ng-bind")+"}}")};Binder.prototype.mc=function(a){return new BindAttrUpdater(a,fromJson(a.getAttribute("ng-bind-attr")))};Binder.prototype.rc=function(a){return new HideUpdater(a,a.getAttribute("ng-hide"))};Binder.prototype.sc=function(a){return new ShowUpdater(a, -a.getAttribute("ng-show"))};Binder.prototype.nc=function(a){return new ClassUpdater(a,a.getAttribute("ng-class"))};Binder.prototype.oc=function(a){return new ClassEvenUpdater(a,a.getAttribute("ng-class-even"))};Binder.prototype.pc=function(a){return new ClassOddUpdater(a,a.getAttribute("ng-class-odd"))};Binder.prototype.tc=function(a){return new StyleUpdater(a,a.getAttribute("ng-style"))};Binder.prototype.uc=function(a,b){b.watch(a.getAttribute("ng-watch"))};ControlBar=function(a,b){this.document= -a;this.ha=b;this.window=n;this.F=[]};ControlBar.prototype.bind=p();ControlBar.Wc='<div><div class="ui-widget-overlay"></div><div id="ng-login" ng-non-bindable="true"><div class="ng-login-container"></div></div></div>';ControlBar.prototype.ba=function(a){this.F.push(a);this.F.length==1&&this.Ka("/user_session/new.mini?return_url="+encodeURIComponent(this.mb()))};ControlBar.prototype.ab=function(a){this.F.push(a);this.F.length==1&&this.Ka("/user_session/do_destroy.mini")};ControlBar.prototype.mb=function(){return this.window.location.href.split("#")[0]}; -ControlBar.prototype.Ka=function(a){var b=this,c=(new Date).getTime(),d=this.mb();d+="#$iframe_notify="+c;var e=i('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+d+'" src="'+this.ha+a+'" width="500" height="330"/></div>');this.document.append(e);e.Ja({height:363,width:500,Fd:false,jc:true,title:'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>'});callbacks["_iframe_notify_"+c]=function(){e.Ja("destroy");e.remove();o(b.F,function(f){f()});b.F=[]}}; -ControlBar.ub='<div ng-non-bindable="true" title="Permission Error:">Sorry, you do not have permission for this!</div>';ControlBar.prototype.ra=function(){if(!this.Pa){this.Pa=i(ControlBar.ub);this.Pa.Ja({ed:true,height:70,jc:true})}};DataStore=function(a,b,c){this.post=a;this.nb=b;this.A={S:[]};this.anchor=c;this.U=[]};DataStore.prototype.V=function(a){if(a.constructor!=Model)throw"Parameter must be an instance of Entity! "+toJson(a);var b=a.T+"/"+a.C,c=this.A[b];if(c)Model.la(a,c);else c=this.A[b]= -a;return c};DataStore.prototype.load=function(a,b,c,d){if(b&&b!=="*"){var e=this;this.D(["GET",a.T+"/"+b],function(f){a.I(f);a.za();f=a.R(a);e.V(f);(c||x)(a)},d)}return a};DataStore.prototype.oa=function(a,b,c){var d=this,e=[],f=0;o(b,function(g){e.push(d.load(a(),g,function(){f++;if(f==b.length)(c||x)(e)}))});return e};DataStore.prototype.Xa=function(a,b,c){return this.load(a,b,c,function(d){if(d.ja==404){a.C=b;(c||x)(a)}else throw d;})};DataStore.prototype.hc=function(a,b){var c=this,d=[];d.xa= -function(e){return e.T==a.title};this.A.S.push(d);this.D(["GET",a.title],function(e){for(var f=0;f<e.length;f++){var g=a();g.I(e[f]);d.push(c.V(g))}(b||x)(d)});return d};DataStore.prototype.save=function(a,b){var c=this,d={};a.Aa(d);this.D(["POST","",d],function(e){a.I(e);var f=c.V(a);_.each(c.A.S,function(g){g.xa(a)&&m.Array.includeIf(g,f,true)});if(a.ya)c.anchor[a.ya]=a.C;b&&b(a)})};DataStore.prototype.remove=function(a,b){var c=this,d={};a.Aa(d);this.D(["DELETE","",d],function(e){delete c.A[a.T+ -"/"+a.C];_.each(c.A.S,function(f){for(var g=0;g<f.length;g++)f[g].C==a.C&&f.splice(g,1)});(b||x)(e)})};DataStore.prototype.D=function(a,b,c){a.pb=b;a.rb=c||function(d){throw d;};this.U.push(a)};DataStore.prototype.Oa=function(){function a(d,e){q("RESPONSE["+d+"]: ",e);if(e.ja==401)b.nb.ba(function(){b.post(c,a)});else if(e.ja)alert(toJson(e));else for(var f=0;f<e.length;f++){var g=e[f],h=c[f];if(d=g.ja)d==403?b.nb.ra():h.rb(g);else h.pb(g)}}if(this.U.length!==0){var b=this,c=this.U;this.U=[];q("REQUEST:", -c);this.post(c,a)}};DataStore.prototype.Ec=function(a,b){function c(){d--;d===0&&b&&b()}var d=1;for(var e in a){var f=a[e];if(f&&f.ia==Model.prototype.ia){d++;f.ia(c)}}c()};DataStore.prototype.O=function(a,b,c,d){var e=this,f=[];f.xa=E(false);this.A.S.push(f);this.D(["GET",a.title+"/"+b+"="+c],function(g){for(var h=0;h<g.length;h++){var j=(new a).I(g[h]);f.push(e.V(j))}d&&d(f)});return f};DataStore.J=p();DataStore.J.all=function(){return[]};DataStore.J.O=function(){return[]};DataStore.J.load=function(){return{}}; -DataStore.J.title=undefined;DataStore.prototype.M=function(a,b){if(!a)return DataStore.J;var c=this;function d(e){return new Model(d,e)}d.title=a;d.qb=true;d.Ia=this;d.Rb=b||{};d.load=function(e,f){return c.load(d(),e,f)};d.oa=function(e,f){return c.oa(d,e,f)};d.Xa=function(e,f){return c.Xa(d(),e,f)};d.all=function(e){return c.hc(d,e)};d.O=function(e,f,g){return c.O(d,e,f,g)};d.Ed=function(e){c.D(["GET",a+"/$properties"],e)};return d};DataStore.prototype.join=function(a){function b(){throw"Joined entities can not be instantiated into a document."; -}var c=_(a).fd().map(function(d,e){return e}).sortBy(function(d){var e=[];do{if(_(e).include(d))throw"Infinite loop in join: "+e.join(" -> ");e.push(d);if(!a[d])throw _("Named entity '<%=name%>' is undefined.").template({name:d});d=a[d].ca?a[d].ca.substring(0,a[d].ca.indexOf(".")):undefined}while(d);return e.length}).value();if(_(c).select(function(d){return a[d].ca}).length!=c.length-1)throw"Exactly one entity needs to be primary.";b.O=function(d,e){var f=[],g=d?d.substring(0,d.indexOf(".")):undefined; -if(g!=c[0])throw _("Named entity '<%=name%>' is not a primary entity.").template({name:g});var h=1;a[g].join.O(d.substring(d.indexOf(".")+1),e,function(j){var k=c[h++],l=a[k],y=l.ca,z={};_(j).each(function(t){var u={};f.push(u);u[g]=t;t=Scope.Y(u,y);z[t]=t});l.join.oa(_.toArray(z),function(t){var u={};_(t).each(function(w){u[w.C]=w});_(f).each(function(w){var B=Scope.Y(w,y);w[k]=u[B]})})});return f};return b};s.h=function(a){if(a)for(var b in a)this[b]=a[b]};s.h.get=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}};var v;o({currency:function(a){i(this.element).toggleClass("ng-format-negative",a<0);return"$"+s.number.apply(this,[a,2])},number:function(a,b){if(isNaN(a)||!isFinite(a))return"";b=typeof b=="undefined"?2:b;var c=a<0;a=Math.abs(a);var d=Math.pow(10,b);a=""+Math.round(a*d);var e=a.substring(0,a.length-b);e=e||"0";d=a.substring(a.length-b);a=c?"-":"";for(c=0;c<e.length;c++){if((e.length-c)%3===0&&c!== -0)a+=",";a+=e.charAt(c)}if(b>0){for(c=d.length;c<b;c++)d+="0";a+="."+d.substring(0,b)}return a},date:p(),json:function(a){i(this.element).addClass("ng-monospace");return toJson(a,true)},trackPackage:function(){var a=[{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=",ta:[/^1Z[0-9A-Z]{16}$/i]},{name:"FedEx",url:"http://www.fedex.com/Tracking?tracknumbers=",ta:[/^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=",ta:[/^(91\d{20})$/i,/^(91\d{18})$/i]}];return function(b,c){b=b.replace(/^ */,"").replace(/ *$/,"");var d=b.replace(/ /g,""),e;o(a,function(f){o(f.ta,function(g){if(g.test(d)){g=f.name+": "+b;var h=f.url+b;e=new s.h({text:g,url:h,html:'<a href="'+X(h)+'">'+g+"</a>",Jd:b});_.breakLoop()}});e&&_.breakLoop()});return e?e:b?c||new s.h({text:b+" is not recognized"}): -null}}(),link:function(a,b){b=b||s.h.get(a);var c=s.h.get(a,"url")||s.h.get(a);if(c){if(m.Q.td(c)===null)c="mailto:"+c;a='<a href="'+C(c)+'">'+b+"</a>";return new s.h({text:b,url:c,html:a})}return a},bytes:function(){var a=["bytes","KB","MB","GB","TB","PB"];return function(b){if(b===null)return"";for(var c=0;b>1E3;){b/=1024;c++}b=""+b;var d=b.indexOf(".");if(d>-1&&d+2<b.length)b=b.substring(0,d+2);return b+" "+a[c]}}(),image:function(a,b,c){if(a&&a.url){var d="";if(b)d=' style="max-width: '+b+"px; max-height: "+ -(c||b)+'px;"';return new s.h({url:a.url,text:a.url,html:'<img src="'+a.url+'"'+d+"/>"})}return null},lowercase:function(a){return(a=s.h.get(a))?(""+a).toLowerCase():a},uppercase:function(a){return(a=s.h.get(a))?(""+a).toUpperCase():a},linecount:function(a){a=s.h.get(a);if(a===""||!a)return 1;return a.split(/\n|\f/).length},"if":function(a,b){return b?a:undefined},unless:function(a,b){return b?undefined:a},googleChartApi:V(function(a,b,c,d){b=b||{};a={Hb:a,gd:v.Ib(b,"color"),nd:v.title(b),jd:v.Ib(b, -"label"),hd:v.values(b),kd:"bg,s,FFFFFF00"};if(_.isArray(b.Sc)){a.pd="x";a.od="0:|"+b.Sc.join("|")}return v.encode(a,c,d)},{values:function(a){var b=[];o(a.Gc||[],function(c){var d=[];o(c.values||[],function(e){d.push(e)});b.push(d.join(","))});a=b.join("|");return a===""?null:"t:"+a},title:function(a){var b=[];a=a.title||[];o(_.isArray(a)?a:[a],function(c){b.push(encodeURIComponent(c))});return b.join("|")},collect:function(a,b){var c=[],d=0;o(a.Gc||[],function(e){var f=[];e=e[b]||[];o(_.isArray(e)? -e:[e],function(g){f.push(encodeURIComponent(g));d++});c.push(f.join("|"))});return d?c.join(","):null},encode:function(a,b,c){b=b||200;c=c||b;var d="http://chart.apis.google.com/chart?",e=[];a.md=b+"x"+c;o(a,function(f,g){f&&e.push(g+"="+f)});e.sort();d+=e.join("&");return new s.h({url:d,html:'<img width="'+b+'" height="'+c+'" src="'+d+'"/>'})}}),qrcode:function(a,b,c){return v.encode({Hb:"qr",ld:encodeURIComponent(a)},b,c)},chart:{Bd:function(a,b,c){return v("p",a,b,c)},Cd:function(a,b,c){return v("p3", -a,b,c)},Dd:function(a,b,c){return v("pc",a,b,c)},bd:function(a,b,c){return v("bhs",a,b,c)},ad:function(a,b,c){return v("bhg",a,b,c)},dd:function(a,b,c){return v("bvs",a,b,c)},cd:function(a,b,c){return v("bvg",a,b,c)},xd:function(a,b,c){return v("lc",a,b,c)},Hd:function(a,b,c){return v("ls",a,b,c)},Gd:function(a,b,c){return v("s",a,b,c)}},html:function(a){return new s.h({html:a})}},function(a,b){s[b]=a});v=s.googleChartApi;array=[].constructor;toJson=function(a,b){var c=[];toJsonArray(c,a,b?"\n ": -null);return c.join("")};toPrettyJson=function(a){return toJson(a,true)};fromJson=function(a){try{var b=new Parser(a,true),c=b.fa();b.K();return c()}catch(d){J("fromJson error: ",a,d);throw d;}};toJsonArray=function(a,b,c){var d=typeof b;if(b===null)a.push("null");else if(d!=="function")if(d==="boolean")a.push(""+b);else if(d==="number")isNaN(b)?a.push("null"):a.push(""+b);else if(d==="string")return a.push(m.String.quoteUnicode(b));else if(d==="object")if(b instanceof Array){a.push("[");var e=b.length; -d=false;for(var f=0;f<e;f++){var g=b[f];d&&a.push(",");typeof g=="function"||typeof g=="undefined"?a.push("null"):toJsonArray(a,g,c);d=true}a.push("]")}else if(b instanceof Date)a.push(m.String.quoteUnicode(m.Date.toString(b)));else{a.push("{");c&&a.push(c);d=false;f=c?c+" ":false;g=[];for(var h in b)h.indexOf("$$")!==0&&g.push(h);g.sort();for(h=0;h<g.length;h++){var j=g[h];try{e=b[j];if(typeof e!="function"){if(d){a.push(",");c&&a.push(c)}a.push(m.String.quote(j));a.push(":");toJsonArray(a,e,f); -d=true}}catch(k){}}a.push("}")}};Model=function(a,b){this.R=a;this.I(b||{});this.T=a.title;this.za()};Model.la=function(a,b){if(!(a===b||!a||!b)){var c=function(e,f,g){return g.substring(0,2)!=="$$"&&typeof e[g]!=="function"&&typeof f[g]!=="function"};for(var d in b)c(a,b,d)&&delete b[d];for(d in a)if(c(a,b,d))b[d]=a[d]}};Model.prototype.za=function(){S(this.R.Rb,this);return this};Model.prototype.ia=function(a){this.R.Ia.save(this,a===true?undefined:a);a===true&&this.R.Ia.Oa();return this};Model.prototype.I= -function(a){Model.la(a,this);return this};Model.prototype.Aa=function(a){Model.la(this,a);return this};Lexer=function(a,b){this.text=a;this.Pb=b?20:-1;this.g=[];this.index=0};Lexer.Ba={"null":E(null),"true":E(true),"false":E(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 a.scope.j(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}};Lexer.prototype.H=function(){return this.index+1<this.text.length?this.text.charAt(this.index+1):false};Lexer.prototype.parse=function(){for(var a=this.g, -b=Lexer.Ba,c=true;this.index<this.text.length;){var d=this.text.charAt(this.index);if(d=='"'||d=="'"){this.Cc(d);c=true}else if(d=="("||d=="["){a.push({index:this.index,text:d});this.index++}else if(d=="{"){c=this.H();if(c==":"||c=="("){a.push({index:this.index,text:d+c});this.index++}else a.push({index:this.index,text:d});this.index++;c=true}else if(d==")"||d=="]"||d=="}"){a.push({index:this.index,text:d});this.index++;c=false}else if(d==":"||d=="."||d==","||d==";"){a.push({index:this.index,text:d}); -this.index++;c=true}else if(c&&d=="/"){this.Bc();c=false}else if(this.ma(d)){this.Ac();c=false}else if(this.$(d)){this.gb();c=false}else if(this.ec(d))this.index++;else{c=d+this.H();var e=b[d],f=b[c];if(f){a.push({index:this.index,text:c,b:f});this.index+=2}else if(e){a.push({index:this.index,text:d,b:e});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};Lexer.prototype.ma= -function(a){return"0"<=a&&a<="9"};Lexer.prototype.ec=function(a){return a==" "||a=="\r"||a=="\t"||a=="\n"||a=="\u000b"};Lexer.prototype.$=function(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"};Lexer.prototype.Ac=function(){for(var a="",b=this.index;this.index<this.text.length;){var c=this.text.charAt(this.index);if(c=="."||this.ma(c))a+=c;else break;this.index++}a=1*a;this.g.push({index:b,text:a,b:function(){return a}})};Lexer.prototype.gb=function(){for(var a="",b=this.index;this.index< -this.text.length;){var c=this.text.charAt(this.index);if(c=="."||this.$(c)||this.ma(c))a+=c;else break;this.index++}c=Lexer.Ba[a];if(!c){c=function(d){return d.scope.get(a)};c.Z=a}this.g.push({index:b,text:a,b:c})};Lexer.tb={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'};Lexer.prototype.Cc=function(a){var b=this.index,c=this.Pb;this.index++;for(var d="",e=false;this.index<this.text.length;){var f=this.text.charAt(this.index);if(e){if(f=="u"){f=this.text.substring(this.index+1,this.index+ -5);this.index+=4;d+=String.fromCharCode(parseInt(f,16))}else{e=Lexer.tb[f];d+=e?e:f}e=false}else if(f=="\\")e=true;else if(f==a){this.index++;this.g.push({index:b,text:d,b:function(){return d.length==c?m.String.toDate(d):d}});return}else d+=f;this.index++}throw"Lexer Error: Unterminated quote ["+this.text.substring(b)+"] starting at column '"+(b+1)+"' in expression '"+this.text+"'.";};Lexer.prototype.Bc=function(){var a=this.index;this.index++;for(var b="",c=false;this.index<this.text.length;){var d= -this.text.charAt(this.index);if(c){b+=d;c=false}else if(d==="\\"){b+=d;c=true}else if(d==="/"){this.index++;c="";if(this.$(this.text.charAt(this.index))){this.gb();c=this.g.pop().text}var e=new RegExp(b,c);this.g.push({index:a,text:b,vd:c,b:function(){return e}});return}else b+=d;this.index++}throw"Lexer Error: Unterminated RegExp ["+this.text.substring(a)+"] starting at column '"+(a+1)+"' in expression '"+this.text+"'.";};Parser=function(a,b){this.text=a;this.g=(new Lexer(a,b)).parse();this.index= -0};Parser.vb=E(0);Parser.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)+"'.";};Parser.prototype.ea=function(){if(this.g.length===0)throw"Unexpected end of expression: "+this.text;return this.g[0]};Parser.prototype.H=function(a,b,c,d){var e=this.g;if(e.length>0){e=e[0];var f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return false};Parser.prototype.a=function(a,b,c,d){if(a= -this.H(a,b,c,d)){this.g.shift();return this.qd=a}return false};Parser.prototype.k=function(a){if(!this.a(a)){var b=this.H();throw"Expecting '"+a+"' at column '"+(b.index+1)+"' in '"+this.text+"' got '"+this.text.substring(b.index)+"'.";}};Parser.prototype.wb=function(a,b){var c=b.apply(this);return function(d){return a(d,c(d))}};Parser.prototype.q=function(a,b,c){var d=c.apply(this);return function(e){return b(e,a(e),d(e))}};Parser.prototype.Ra=function(){return this.g.length>0};Parser.prototype.K= -function(){if(this.g.length!==0)throw"Did not understand '"+this.text.substring(this.g[0].index)+"' while evaluating '"+this.text+"'.";};Parser.prototype.ua=function(){for(var a=[];;){this.g.length>0&&!this.H("}",")",";","]")&&a.push(this.Na());if(!this.a(";"))return function(b){for(var c,d=0;d<a.length;d++){var e=a[d];if(e)c=e(b)}return c}}};Parser.prototype.Na=function(){for(var a=this.o(),b;;)if(b=this.a("|"))a=this.q(a,b.b,this.filter);else return a};Parser.prototype.filter=function(){return this.Da(m.filter)}; -Parser.prototype.Q=function(){return this.Da(m.validator)};Parser.prototype.Da=function(a){for(var b=this.ac(a),c=[];;)if(this.a(":"))c.push(this.o());else{var d=function(e,f){f=[f];for(var g=0;g<c.length;g++)f.push(c[g](e));return b.apply(e,f)};return function(){return d}}};Parser.prototype.o=function(){return this.Ic()};Parser.prototype.Ic=function(){if(this.a("throw")){var a=this.Fa();return function(b){throw a(b);}}else return this.Fa()};Parser.prototype.Fa=function(){var a=this.$a(),b;if(b=this.a("=")){if(!a.Z)throw"Left hand side '"+ -this.text.substring(0,b.index)+"' of assignment '"+this.text.substring(b.index)+"' is not assignable.";return this.q(function(){return a.Z},b.b,this.$a)}else return a};Parser.prototype.$a=function(){for(var a=this.Za(),b;;)if(b=this.a("||"))a=this.q(a,b.b,this.Za);else return a};Parser.prototype.Za=function(){for(var a=this.bb(),b;;)if(b=this.a("&&"))a=this.q(a,b.b,this.bb);else return a};Parser.prototype.bb=function(){var a;return(a=this.a("!"))?this.wb(a.b,this.La):this.La()};Parser.prototype.La= -function(){for(var a=this.hb(),b;;)if(b=this.a("==","!="))a=this.q(a,b.b,this.hb);else return a};Parser.prototype.hb=function(){for(var a=this.Ea(),b;;)if(b=this.a("<",">","<=",">="))a=this.q(a,b.b,this.Ea);else return a};Parser.prototype.Ea=function(){for(var a=this.qa(),b;b=this.a("+","-");)a=this.q(a,b.b,this.qa);return a};Parser.prototype.qa=function(){for(var a=this.kb(),b;b=this.a("*","/","%");)a=this.q(a,b.b,this.kb);return a};Parser.prototype.kb=function(){var a;return this.a("+")?this.fa(): -(a=this.a("-"))?this.q(Parser.vb,a.b,this.qa):this.fa()};Parser.prototype.ac=function(a){var b=this.a(),c=b.text.split(".");a=a;for(var d,e=0;e<c.length;e++){d=c[e];if(a)a=a[d]}if(typeof a!="function")throw"Function '"+b.text+"' at column '"+(b.index+1)+"' in '"+this.text+"' is not defined.";return a};Parser.prototype.fa=function(){var a;if(this.a("(")){a=this.Na();this.k(")");a=a}else if(this.a("["))a=this.Cb();else if(this.a("{"))a=this.object();else if(this.a("{:"))a=this.Ga(false);else if(this.a("{("))a= -this.Ga(true);else{var b=this.a();(a=b.b)||this.error("not a primary expression",b)}for(;b=this.a("(","[",".");)if(b.text==="(")a=this.$b(a);else if(b.text==="[")a=this.vc(a);else if(b.text===".")a=this.Yb(a);else throw"IMPOSSIBLE";return a};Parser.prototype.Ga=function(a){var b=[];if(a){if(!this.a(")")){for(b.push(this.a().text);this.a(",");)b.push(this.a().text);this.k(")")}this.k(":")}var c=this.ua();this.k("}");return function(d){return function(e){var f=new Scope(d.scope.s);f.j("$",e);for(var g= -0;g<b.length;g++)f.j(b[g],arguments[g]);return c({scope:f})}}};Parser.prototype.Yb=function(a){var b=this.a().text;function c(d){return Scope.Y(a(d),b)}c.Z=b;return c};Parser.prototype.vc=function(a){var b=this.o();this.k("]");if(this.a("=")){var c=this.o();return function(d){return a(d)[b(d)]=c(d)}}else return function(d){var e=a(d);d=b(d);return e?e[d]:undefined}};Parser.prototype.$b=function(a){var b=[];if(this.ea().text!=")"){do b.push(this.o());while(this.a(","))}this.k(")");return function(c){for(var d= -[],e=0;e<b.length;e++)d.push(b[e](c));e=a(c);if(typeof e==="function")return e.apply(c,d);else throw"Expression '"+a.Z+"' is not a function.";}};Parser.prototype.Cb=function(){var a=[];if(this.ea().text!="]"){do a.push(this.o());while(this.a(","))}this.k("]");return function(b){for(var c=[],d=0;d<a.length;d++)c.push(a[d](b));return c}};Parser.prototype.object=function(){var a=[];if(this.ea().text!="}"){do{var b=this.a().text;this.k(":");var c=this.o();a.push({fc:b,value:c})}while(this.a(","))}this.k("}"); -return function(d){for(var e={},f=0;f<a.length;f++){var g=a[f],h=g.value(d);e[g.fc]=h}return e}};Parser.prototype.Wb=function(){for(var a=[];this.Ra();){a.push(this.Vb());this.a(";")||this.K()}return function(b){for(var c="",d=0;d<a.length;d++)c+=a[d](b);return c}};Parser.prototype.Vb=function(){var a=this.a().text,b,c;if(this.a("=")){b=a;a=this.a().text}if(this.a(":"))c=this.fa()(null);return function(d){var e=d.scope.get("$datastore").M(a,c);d.scope.j(a,e);if(b){e=e();e.ya=b;d.scope.j(b,e);return"$anchor."+ -b+":{"+b+"="+a+".load($anchor."+b+");"+b+".$$anchor="+m.String.quote(b)+";};"}else return""}};Parser.prototype.watch=function(){for(var a=[];this.Ra();){a.push(this.Qc());this.a(";")||this.K()}this.K();return function(b){for(var c=0;c<a.length;c++){var d=a[c](b);b.zb(d.name,d.b)}}};Parser.prototype.Qc=function(){var a=this.a().text;this.k(":");var b;if(this.ea().text=="{"){this.k("{");b=this.ua();this.k("}")}else b=this.o();return function(){return{name:a,b:b}}};Scope=function(a,b){this.ob=[];this.wa= -{};this.name=b;a=a||{};function c(){}c.prototype=a;this.s=new c;this.s.Uc=a;if(b=="ROOT")this.s.Vc=this.s};Scope.X={};Scope.prototype.c=function(){var a=this;this.Zb();_.each(this.ob,function(b){a.l(b,"",{},function(){this.c(a)})})};Scope.prototype.Bb=function(a){a&&this.ob.push(a)};Scope.prototype.dc=function(a){for(var b=0;b<a.length;b++){var c=a.charAt(b);if(c!="."&&!Lexer.prototype.$(c))return false}return true};Scope.Y=function(a,b){if(!b)return a;for(var c=b.split("."),d,e=a,f=c.length,g=0;g< -f;g++){d=c[g];if(!d.match(/^[\$\w][\$\w\d]*$/))throw"Expression '"+b+"' is not a valid expression for accesing variables.";if(a){e=a;a=a[d]}if(_.isUndefined(a)&&d.charAt(0)=="$"){var h=m.Global.typeOf(e);if(d=(h=m[h.charAt(0).toUpperCase()+h.substring(1)])?h[[d.substring(1)]]:undefined)return a=_.bind(d,e,e)}}if(typeof a==="function"&&!a.qb)return P(e,a);return a};Scope.prototype.get=function(a){return Scope.Y(this.s,a)};Scope.prototype.j=function(a,b){a=a.split(".");for(var c=this.s,d=0;a.length> -1;d++){var e=a.shift(),f=c[e];if(!f){f={};c[e]=f}c=f}return c[a.shift()]=b};Scope.prototype.m=function(a,b){this.eval(a+"="+toJson(b))};Scope.prototype.eval=function(a,b){var c=Scope.X[a];if(!c){var d=new Parser(a);c=d.ua();d.K();Scope.X[a]=c}b=b||{};b.scope=this;return c(b)};Scope.prototype.l=function(a,b,c,d,e){try{var f=this.eval(b,c);if(a.w){a.w=false;i(a.view).removeClass("ng-exception").removeAttr("ng-error")}d&&d.apply(a,[f]);return true}catch(g){J("Eval Widget Error:",g);b=toJson(g,true); -a.w=true;i(a.view).addClass("ng-exception").attr("ng-error",b);e&&e.apply(a,[g,b]);return false}};Scope.prototype.Oc=function(a,b){var c=Scope.X[a];if(!c){c=(new Parser(a)).Q();Scope.X[a]=c}a={scope:this};return c(a)(a,b)};Scope.prototype.M=function(a){return(new Parser(a)).Wb()({scope:this})};Scope.prototype.ic=function(a){this.s.Tc.push(a)};Scope.prototype.watch=function(a){var b=this;(new Parser(a)).watch()({scope:this,zb:function(c,d){b.Ab(c,function(e,f){try{return d({scope:b},e,f)}catch(g){alert(g)}})}})}; -Scope.prototype.Ab=function(a,b){var c=this.wa[a];if(!c){c={Va:[],o:a};this.wa[a]=c}c.Va.push(b)};Scope.prototype.Zb=function(){var a=this,b=false;o(this.wa,function(c){var d=a.eval(c.o);if(d!==c.d){o(c.Va,function(e){e(d,c.d);b=true});c.d=d}});return b};Server=function(a,b){this.url=a;this.kc=0;this.getScript=b;this.Nc="_"+(""+Math.random()).substr(2)+"_";this.pa=1800};Server.prototype.Eb=function(a){return H.Tb(a)};Server.prototype.P=function(a,b,c,d){var e=this.Nc+this.kc++;K[e]=function(h){delete m[e]; -d(200,h)};a={Kd:b,yd:a,Ad:c};a=this.Eb(toJson(a));b=Math.ceil(a.length/this.pa);c=this.url+"/$/"+e+"/"+b+"/";for(var f=0;f<b;f++){var g=a.substr(f*this.pa,this.pa);this.getScript(c+(f+1)+"?h="+g,x)}};FrameServer=A("frame");FrameServer.Xc="$DATASET:";FrameServer.prototype={P:p()};VisualServer=function(a,b,c){this.Sb=a;this.update=c;this.status=b};VisualServer.prototype={P:function(a,b,c,d){var e=this;this.status.Fb(c);this.Sb.P(a,b,c,function(){e.status.Ub();try{d.apply(this,arguments)}catch(f){alert(toJson(f))}e.update()})}}; -Users=function(a,b){this.z=a;this.ka=b};Users.prototype={Ma:function(a){var b=this;this.z.P("GET","/account.json",{},function(c,d){b.Ob=d.Mc;a(d.Mc)})},ab:function(a){var b=this;this.ka.ab(function(){delete b.Ob;(a||x)()})},ba:function(a){var b=this;this.ka.ba(function(){b.Ma(function(){(a||x)()})})},ra:function(){this.ka.ra()}};o({regexp:function(a,b,c){return a.match(b)?null:c||"Value does not match expected format "+b+"."},number:function(a,b,c){var d=1*a;if(d==a){if(typeof b!="undefined"&&d<b)return"Value can not be less than "+ -b+".";if(typeof b!="undefined"&&d>c)return"Value can not be greater than "+c+".";return null}else return"Value is not a number."},integer:function(a,b,c){b=W.number(a,b,c);if(b===null&&a!=Math.round(a))return"Value is not a whole number.";return b},date: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)."},ssn: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."},email: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."},phone: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."},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."},json:function(a){try{fromJson(a); -return null}catch(b){return b.toString()}}},function(a,b){W[b]=a});WidgetFactory=function(a,b){this.lc=0;this.ha=a;this.G=b;this.Ha=n.Id?swfobject.Ha:function(){alert("ERROR: swfobject not loaded!")};this.wc=p()};WidgetFactory.prototype.Mb=function(a,b){var c,d=a.attr("type").toLowerCase(),e=a.attr("name");if(e)e=e.split(":").pop();var f="change",g=true;if(d=="button"||d=="submit"||d=="reset"||d=="image"){c=new ButtonController(a[0],e);f="click";g=false}else if(d=="text"||d=="textarea"||d=="hidden"|| -d=="password"){c=new TextController(a[0],e);f="keyup change"}else if(d=="checkbox"){c=new CheckboxController(a[0],e);f="click"}else if(d=="radio"){c=new RadioController(a[0],e);f="click"}else if(d=="select-one")c=new SelectController(a[0],e);else if(d=="select-multiple")c=new MultiSelectController(a[0],e);else if(d=="file")c=this.Nb(a,e);else throw"Unknown type: "+d;a.data("controller",c);var h=b.get("$binder");i(c.view,":input").bind(f,function(){if(c.e(b)){var j=i(c.view).attr("ng-action")||""; -b.l(c,j)&&h.c(b)}return g});return c};WidgetFactory.prototype.Nb=function(a){var b="__uploadWidget_"+this.lc++,c=FileController.template(b);a.after(c);b=this.Ha({data:this.ha+"/admin/ServerAPI.swf",width:"95",height:"20",align:"top",Md:"transparent"},{wd:"uploadWidgetId="+b,Yc:"always"},b);a.remove();a=new FileController(c,a[0].name,b,this.ha+"/data/"+this.G);i(b).data("controller",a);return a};FileController=function(a,b,c,d){this.view=a;this.Ld=c;this.ib=b;this.Zc=d+"/_attachments";this.value=null; -this.d=undefined};FileController.dispatchEvent=function(a,b,c){a=r.getElementById(a);a=i(a).data("controller");FileController.prototype["_on_"+b].apply(a,c)};FileController.template=function(a){return i('<span class="ng-upload-widget"><input type="checkbox" ng-non-bindable="true"/><object id="'+a+'" /><a></a><span/></span>')};FileController.prototype.e=function(a){var b=this.view.find("input").attr("checked")?this.value:null;if(this.d===b)return false;else{a.j(this.ib,b);return true}};FileController.prototype.c= -function(a){if((a=a.get(this.ib))&&this.value!==a){this.value=a;this.view.find("a").attr("href",this.value.url).text(this.value.text);this.view.find("span").text(m.filter.bytes(this.value.size))}this.view.find("input").attr("checked",!!a)};NullController=A("view");NullController.prototype.e=E(true);NullController.prototype.c=p();NullController.cc=new NullController;ButtonController=A("view");ButtonController.prototype.e=E(true);ButtonController.prototype.c=p();TextController=function(a,b){this.view= -a;this.exp=b;this.Q=a.getAttribute("ng-validate");this.Dc=typeof a.attributes["ng-required"]!="undefined";this.Ta=null;this.d=undefined;this.p=a.value;a.getAttribute("ng-widget")==="datepicker"&&i(a).rd()};TextController.prototype.e=function(a){var b=this.view.value;if(this.d===b)return false;else{a.m(this.exp,b);this.d=b;return true}};TextController.prototype.c=function(a){var b=this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}c=c?c:"";if(this.d!=c)this.d=b.value=c; -var d=false;b.removeAttribute("ng-error");if(this.Dc)d=!(c&&c.length>0);var e=d?"Required Value":null;if(!d&&this.Q&&c){e=a.Oc(this.Q,c);d=!!e}if(this.Ta!==e){this.Ta=d;if(e!==null){b.setAttribute("ng-error",e);a.ic(this)}i(b).toggleClass("ng-validation-error",d)}};CheckboxController=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.p=a.checked?a.value:""};CheckboxController.prototype.e=function(a){var b=this.view;b=b.checked?b.value:"";if(this.d===b)return false;else{a.m(this.exp,b);this.d= -b;return true}};CheckboxController.prototype.c=function(a){var b=this.view,c=a.eval(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}b.checked=b.value==""+c};SelectController=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.p=a.value};SelectController.prototype.e=function(a){if(this.view.selectedIndex<0)a.m(this.exp,null);else{var b=this.view.value;if(this.d===b)return false;else{a.m(this.exp,b);this.d=b;return true}}};SelectController.prototype.c=function(a){var b=this.view, -c=a.get(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}if(c!==this.d){b.value=c?c:"";this.d=c}};MultiSelectController=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.p=this.selected()};MultiSelectController.prototype.selected=function(){for(var a=[],b=this.view.options,c=0;c<b.length;c++){var d=b[c];d.selected&&a.push(d.value)}return a};MultiSelectController.prototype.e=function(a){var b=this.selected();if(this.d===b)return false;else{a.m(this.exp,b);this.d=b;return true}}; -MultiSelectController.prototype.c=function(a){var b=this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}if(c!==this.d){a=b.options;for(b=0;b<a.length;b++){var d=a[b];d.selected=_.include(c,d.value)}this.d=c}};RadioController=function(a,b){this.view=a;this.exp=b;this.d=this.na=undefined;this.Sa=a.value;this.p=a.checked?a.value:null};RadioController.prototype.e=function(a){var b=this.view;if(this.na)return false;else{b.checked=true;this.d=a.m(this.exp,this.Sa);return this.na= -true}};RadioController.prototype.c=function(a){var b=this.view,c=a.get(this.exp);if(this.p&&typeof c==="undefined"){c=this.p;a.m(this.exp,c)}if(this.d!=c){this.na=b.checked=this.Sa==""+c;this.d=c}};BindUpdater=function(a,b){this.view=a;this.exp=Binder.da(b);this.w=false;this.Fc={element:a}};BindUpdater.jb=function(a){switch(typeof a){case "string":case "boolean":case "number":return C(a);case "function":return BindUpdater.jb(a());case "object":if(a&&a.tagName&&a.nodeName&&a.ownerDocument&&a.removeAttribute)return Q(a); -else if(a instanceof m.filter.h){switch(typeof a.html){case "string":case "number":return a.html;case "function":return a.html();case "object":if(a.html&&a.html.tagName&&a.html.nodeName&&a.html.ownerDocument&&a.html.removeAttribute)return Q(a.html);default:break}switch(typeof a.text){case "string":case "number":return C(a.text);case "function":return C(a.text());default:break}}if(a===null)return"";return C(toJson(a,true));default:return""}};BindUpdater.prototype.e=p();BindUpdater.prototype.c=function(a){for(var b= -[],c=this.exp,d=c.length,e=0;e<d;e++){var f=c[e],g=Binder.L(f);if(g){a.l(this,g,this.Fc,function(h){b.push(BindUpdater.jb(h))},function(h,j){O(this.view,j)});if(this.w)return}else b.push(C(f))}O(this.view,b.join(""))};BindAttrUpdater=function(a,b){this.view=a;this.Db=b};BindAttrUpdater.prototype.e=p();BindAttrUpdater.prototype.c=function(a){var b=i(this.view),c=this.Db;if(this.w){this.w=false;b.removeClass("ng-exception").removeAttr("ng-error")}var d=b.is("img");for(var e in c){for(var f=Binder.da(c[e]), -g=[],h=0;h<f.length;h++){var j=Binder.L(f[h]);if(j)try{var k=a.eval(j,{element:b[0],attrName:e});if(k&&(k.constructor!==array||k.length!==0))g.push(k)}catch(l){this.w=true;J("BindAttrUpdater",l);j=toJson(l,true);g.push("["+j+"]");b.addClass("ng-exception").attr("ng-error",j)}else g.push(f[h])}f=g.length?g.join(""):null;if(d&&e=="src"&&!f)f=a.get("config.server")+"/images/blank.gif";b.attr(e,f)}};EvalUpdater=function(a,b){this.view=a;this.exp=b;this.w=false};EvalUpdater.prototype.e=p();EvalUpdater.prototype.c= -function(a){a.l(this,this.exp)};HideUpdater=function(a,b){this.view=a;this.exp=b};HideUpdater.prototype.e=p();HideUpdater.prototype.c=function(a){a.l(this,this.exp,{},function(b){var c=i(this.view);R(b)?c.hide():c.show()})};ShowUpdater=function(a,b){this.view=a;this.exp=b};ShowUpdater.prototype.e=p();ShowUpdater.prototype.c=function(a){a.l(this,this.exp,{},function(b){var c=i(this.view);R(b)?c.show():c.hide()})};ClassUpdater=function(a,b){this.view=a;this.exp=b};ClassUpdater.prototype.e=p();ClassUpdater.prototype.c= -function(a){a.l(this,this.exp,{},function(b){if(b!==null&&b!==undefined)this.view.className=b})};ClassEvenUpdater=function(a,b){this.view=a;this.exp=b};ClassEvenUpdater.prototype.e=p();ClassEvenUpdater.prototype.c=function(a){a.l(this,this.exp,{},function(b){var c=a.get("$index");i(this.view).toggleClass(b,c%2===1)})};ClassOddUpdater=function(a,b){this.view=a;this.exp=b};ClassOddUpdater.prototype.e=p();ClassOddUpdater.prototype.c=function(a){a.l(this,this.exp,{},function(b){var c=a.get("$index"); -i(this.view).toggleClass(b,c%2===0)})};StyleUpdater=function(a,b){this.view=a;this.exp=b};StyleUpdater.prototype.e=p();StyleUpdater.prototype.c=function(a){a.l(this,this.exp,{},function(b){i(this.view).attr("style","").css(b)})};RepeaterUpdater=function(a,b,c,d){this.view=a;this.template=c;this.prefix=d;this.children=[];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.aa=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.Pc=a[3]||a[1];this.gc=a[2]};RepeaterUpdater.prototype.e=p();RepeaterUpdater.prototype.c=function(a){a.l(this,this.aa,{},function(b){var c=this;if(!b){b=[];a.dc(this.aa)&&a.j(this.aa,b)}var d=b.length,e=this.children.length,f=this.view,g=0,h=null,j=this.gc,k=this.Pc,l=0;o(b,function(z,t){if(l<e){h=c.children[l];h.scope.j(k,z)}else{var u=new Scope(a.s,c.prefix+k+" in "+c.aa+"["+l+"]");u.j("$index", -l);j&&u.j(j,t);u.j(k,z);h={scope:u,element:c.template(u,c.prefix,l)};f.after(h.element);c.children.push(h)}f=h.element;z=(new Date).getTime();h.scope.c();g+=(new Date).getTime()-z;l++});for(b=e;b>d;--b){var y=this.children.pop().element[0];y.parentNode.removeChild(y)}if(h&&h.element[0].nodeName==="OPTION")if(d=i(h.element[0].parentNode).data("controller")){d.d=undefined;d.c(a)}})};PopUp=A("B");PopUp.Ca="mouseleave mouseout click dblclick keypress keyup";PopUp.prototype.bind=function(){this.B.find(".ng-validation-error,.ng-exception").live("mouseover", -PopUp.xc)};PopUp.xc=function(){PopUp.sa();var a=i(this);a.bind(PopUp.Ca,PopUp.sa);var b=a.position(),c=r.documentElement,d=(self.innerWidth||c&&c.clientWidth||r.body.clientWidth)-b.left;c=a.hasClass("ng-exception")?"EXCEPTION:":"Validation error...";a=a.attr("ng-error");d=d>375?"left":"right";c=i("<div id='ng-callout' style='width:300px'><div class='ng-arrow-"+d+"'/><div class='ng-title'>"+c+"</div><div class='ng-content'>"+a+"</div></div>");i("body").append(c);if(d==="left")a=b.left+this.offsetWidth+ -11;else{a=b.left-315;c.find(".ng-arrow-right").css({left:301})}c.css({left:a+"px",top:b.top-3+"px"});return true};PopUp.sa=function(){i("#ng-callout").unbind(PopUp.Ca,PopUp.sa).remove();return true};Status=function(a){this.Ya=a.append(Status.sb).find("#ng-loading");this.ga=0};Status.sb='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>';Status.prototype.Fb=function(){this.ga===0&&this.Ya.show();this.ga++};Status.prototype.Ub=function(){this.ga--;this.ga===0&&this.Ya.hide("fold")}})(window, -document); +function J(){return function(){}}function K(o){return function(){return o}} +(function(o,x){function n(){}function da(a,b){var c=x.createElement("div");c.className=a;for(var d=a="",e=0;e<b.length;e++){var f=b[e];a+=d+(typeof f=="string"?f:y(f));d=" "}c.appendChild(x.createTextNode(a));R.appendChild(c)}function ea(a){switch(a.nodeName){case "OPTION":case "PRE":case "TITLE":return true;default:return false}}function fa(a,b){if(ea(a))if(W)a.innerText=b;else a.textContent=b;else a.innerHTML=b}function L(a){if(!a||!a.replace)return a;return a.replace(/&/g,"&").replace(/</g, +"<").replace(/>/g,">")}function Da(a){if(!a||!a.replace)return a;return a.replace(/</g,"<").replace(/>/g,">").replace(/\"/g,""")}function ga(a,b){if(!a)throw"Missing this";if(!_.isFunction(b))throw"Missing function";return function(){return b.apply(a,arguments)}}function Ea(a,b){return function(){for(var c=[this],d=0;d<arguments.length;d++)c.push(arguments[d]);return b.apply(a,c)}}function ha(a){var b=x.createElement("div");b.appendChild(a);var c=b.innerHTML;b.removeChild(a);return c} +function X(a){var b=(""+a).toLowerCase();if(b=="f"||b=="0"||b=="false"||b=="no")a=false;return!!a}function Y(a,b){for(var c in a){var d=b[c],e=typeof d;if(e=="undefined")b[c]=O(y(a[c]));else e=="object"&&d.constructor!=array&&c.substring(0,1)!="$"&&Y(a[c],d)}}function ia(a,b,c){this.document=k(a);this.head=k(b);this.i=c;this.location=o.location}function ja(a){this.location=a;this.delay=25;this.setTimeout=function(b,c){o.setTimeout(b,c)};this.La=function(b){return b};this.L=a.href}function G(a,b,c){var d= +_.last(b);q(c,function(e){d[e]=_[e]});m[a]=m[a]||{};q(b,function(e){S(m[a],e)})}function z(a,b,c,d){this.C=a;this.pa=c;this.anchor={};this.Pc=b;this.i=d||{};this.cb=[]}function P(a,b){this.document=a;this.da=b;this.window=o;this.F=[]}function T(a,b,c){this.post=a;this.eb=b;this.B={P:[]};this.anchor=c;this.S=[]}function y(a,b){var c=[];Z(c,a,b?"\n ":null);return c.join("")}function O(a){try{var b=new D(a,true),c=b.ba();b.I();return c()}catch(d){$("fromJson error: ",a,d);throw d;}}function Z(a,b,c){var d= +typeof b;if(b===null)a.push("null");else if(d!=="function")if(d==="boolean")a.push(""+b);else if(d==="number")isNaN(b)?a.push("null"):a.push(""+b);else if(d==="string")return a.push(m.String.quoteUnicode(b));else if(d==="object")if(b instanceof Array){a.push("[");var e=b.length;d=false;for(var f=0;f<e;f++){var g=b[f];d&&a.push(",");typeof g=="function"||typeof g=="undefined"?a.push("null"):Z(a,g,c);d=true}a.push("]")}else if(b instanceof Date)a.push(m.String.quoteUnicode(m.Date.toString(b)));else{a.push("{"); +c&&a.push(c);d=false;f=c?c+" ":false;g=[];for(var h in b)h.indexOf("$$")!==0&&g.push(h);g.sort();for(h=0;h<g.length;h++){var i=g[h];try{e=b[i];if(typeof e!="function"){if(d){a.push(",");c&&a.push(c)}a.push(m.String.quote(i));a.push(":");Z(a,e,f);d=true}}catch(j){}}a.push("}")}}function E(a,b){this.$$entity=a;this.R(b||{});this.Q=a.title;this.ib()}function F(a,b){this.text=a;this.Kb=b?20:-1;this.g=[];this.index=0}function D(a,b){this.text=a;this.g=(new F(a,b)).parse();this.index=0}function w(a,b){this.fb= +[];this.qa={};this.name=b;a=a||{};function c(){}c.prototype=a;this.q=new c;this.q.Vc=a;if(b=="ROOT")this.q.Wc=this.q}function ka(a,b){this.url=a;this.hc=0;this.getScript=b;this.Lc="_"+(""+Math.random()).substr(2)+"_";this.ka=1800}function aa(a){this.frame=a}function la(a,b,c){this.Mb=a;this.update=c;this.status=b}function ma(a,b){this.z=a;this.fa=b}function na(a,b){this.ic=0;this.da=a;this.G=b;this.Aa=o.Kd?swfobject.Aa:function(){alert("ERROR: swfobject not loaded!")};this.tc=J()}function M(a,b,c, +d){this.view=a;this.Nd=c;this.$a=b;this.ad=d+"/_attachments";this.value=null;this.d=undefined}function Q(a){this.view=a}function oa(a,b){this.view=a;this.exp=b;this.O=a.getAttribute("ng-validate");this.Bc=typeof a.attributes["ng-required"]!="undefined";this.Ka=null;this.d=undefined;this.p=a.value;a.getAttribute("ng-widget")==="datepicker"&&k(a).td()}function pa(a,b){this.view=a;this.exp=b;this.d=undefined;this.p=a.checked?a.value:""}function qa(a,b){this.view=a;this.exp=b;this.d=undefined;this.p= +a.value}function ra(a,b){this.view=a;this.exp=b;this.d=undefined;this.p=this.selected()}function sa(a,b){this.view=a;this.exp=b;this.d=this.ia=undefined;this.Ja=a.value;this.p=a.checked?a.value:null}function N(a,b){this.view=a;this.exp=z.$(b);this.w=false;this.Dc={element:a}}function ta(a,b){this.view=a;this.wb=b}function ua(a,b){this.view=a;this.exp=b;this.w=false}function va(a,b){this.view=a;this.exp=b}function wa(a,b){this.view=a;this.exp=b}function xa(a,b){this.view=a;this.exp=b}function ya(a, +b){this.view=a;this.exp=b}function za(a,b){this.view=a;this.exp=b}function Aa(a,b){this.view=a;this.exp=b}function Ba(a,b,c,d){this.view=a;this.template=c;this.prefix=d;this.children=[];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.Y=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.Nc=a[3]||a[1];this.bc= +a[2]}function B(a){this.C=a}function U(a){this.Pa=a.append(U.kb).find("#ng-loading");this.ca=0}var ba={u:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",Nb:function(a){var b="",c,d,e,f,g,h,i=0;for(a=ba.rb(a);i<a.length;){c=a.charCodeAt(i++);d=a.charCodeAt(i++);e=a.charCodeAt(i++);f=c>>2;c=(c&3)<<4|d>>4;g=(d&15)<<2|e>>6;h=e&63;if(isNaN(d))g=h=64;else if(isNaN(e))h=64;b=b+this.u.charAt(f)+this.u.charAt(c)+this.u.charAt(g)+this.u.charAt(h)}return b},ud:function(a){var b="",c,d,e, +f,g,h=0;for(a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");h<a.length;){c=this.u.indexOf(a.charAt(h++));d=this.u.indexOf(a.charAt(h++));f=this.u.indexOf(a.charAt(h++));g=this.u.indexOf(a.charAt(h++));c=c<<2|d>>4;d=(d&15)<<4|f>>2;e=(f&3)<<6|g;b+=String.fromCharCode(c);if(f!=64)b+=String.fromCharCode(d);if(g!=64)b+=String.fromCharCode(e)}return b=ba.qb(b)},rb:function(a){a=a.replace(/\r\n/g,"\n");for(var b="",c=0;c<a.length;c++){var d=a.charCodeAt(c);if(d<128)b+=String.fromCharCode(d);else{if(d>127&&d<2048)b+= +String.fromCharCode(d>>6|192);else{b+=String.fromCharCode(d>>12|224);b+=String.fromCharCode(d>>6&63|128)}b+=String.fromCharCode(d&63|128)}}return b},qb:function(a){for(var b="",c=0,d=c1=c2=0;c<a.length;){d=a.charCodeAt(c);if(d<128){b+=String.fromCharCode(d);c++}else if(d>191&&d<224){c2=a.charCodeAt(c+1);b+=String.fromCharCode((d&31)<<6|c2&63);c+=2}else{c2=a.charCodeAt(c+1);c3=a.charCodeAt(c+2);b+=String.fromCharCode((d&15)<<12|(c2&63)<<6|c3&63);c+=3}}return b}};if(typeof x.getAttribute=="undefined")x.getAttribute= +J();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};o.console||(o.console={log:n,error:n});var R,q=_.each,S=_.extend,k=o.jQuery,W=k.browser.msie;function r(){o.console.log.apply(this,arguments)}function $(){o.console.error.apply(this,arguments)}var m=o.angular||(o.angular={}),Ca=m.validator|| +(m.validator={}),s=m.filter||(m.filter={}),ca=m.callbacks||(m.callbacks={});m.alert||(m.alert=function(){r(arguments);o.alert.apply(o,arguments)});ia.prototype={load:function(){this.Fb();this.Na("/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css");this.Na("/stylesheets/css");r("Server: "+this.i.z);this.Eb();this.Db();this.zb()},Eb:function(){r("Loader.configureJQueryPlugins()");k.fn.scope=function(){for(var a=this;a&&a.get(0);){var b=a.data("scope");if(b)return b;a=a.parent()}return null};k.fn.controller= +function(){return this.data("controller")||Q.Yb}},Ic:function(){return""+(new Date).getTime()},Db:function(){var a=this.i;if(!a.G){var b=a.z.match(/https?:\/\/([\w]*)/);a.G=b?b[1]:"$MEMORY"}},zb:function(){function a(){g.Sb(function(l){!l&&c.find("[ng-auth=eager]").length&&g.ja()})}r("Loader.bindHtml()");var b=new ja(this.location),c=this.document,d=new na(this.i.z,this.i.G),e=new z(c[0],d,b,this.i);d.tc=Ea(e,e.e);d=new P(c.find("body"),this.i.z);var f=this.i.G=="$MEMORY"?new aa(this.window):new ka(this.i.z, +k.getScript);f=new la(f,new U(k(c.body)),function(){e.c()});var g=new ma(f,d),h="/data/"+this.i.G,i=new T(function(l,p){f.N("POST",h,l,p)},g,e.anchor);e.cb.push(function(){i.Fa()});var j=new w({$anchor:e.anchor,$binder:e,$config:this.i,$console:o.console,$datastore:i,$save:function(l){i.Cc(j.q,l,e.anchor)},$window:o,$uid:this.Ic,$users:g},"ROOT");c.data("scope",j);r("$binder.entity()");e.K(j);r("$binder.compile()");e.compile();r("ControlBar.bind()");d.bind();r("$users.fetchCurrentUser()");a();r("PopUp.bind()"); +(new B(c)).bind();r("$binder.parseAnchor()");e.Va();r("$binder.executeInit()");e.Rb();r("$binder.updateView()");e.c();b.La=ga(e,e.vc,b);b.Cd=function(){alert("update")};b.watch();c.find("body").show();r("ready()")},Fb:function(){var a=o.location.href+"#";a=a.split("#")[1];var b={Lb:null};a=a.split("&");for(var c=0;c<a.length;c++){var d=(a[c]+"=").split("=");b[d[0]]=d[1]}if(b.Lb=="console"){R=x.createElement("div");R.id="ng-console";x.getElementsByTagName("body")[0].appendChild(R);r=function(){da("ng-console-info", +arguments)};console.error=function(){da("ng-console-error",arguments)}}},Na:function(a){var b=x.createElement("link");b.rel="stylesheet";b.type="text/css";a.match(/^http:/)||(a=this.i.z+a);b.href=a;this.head[0].appendChild(b)}};ja.prototype={watch:function(){var a=this;function b(){if(a.L!==a.location.href){var c=a.location.hash.match(/^#\$iframe_notify=(.*)$/);if(c){a.L.match(/#/)||(a.L+="#");a.location.href=a.L;c="_iframe_notify_"+c[1];var d=ca[c];delete ca[c];try{(d||n)()}catch(e){alert(e)}}else{a.La(a.location.href); +a.L=a.location.href}}a.setTimeout(b,a.delay)}b()},Fc:function(a){var b=o.location.href;b.match(/#/)||(b+="#");if(b!=a)o.location.href=a;this.xd=a},Ha:function(){return o.location.href}};m.compile=function(a,b){b=b||{};(new ia(a,k("head"),_({z:""}).extend(b))).load();var c=k(a).scope();return{updateView:function(){return c.c.apply(c,arguments)},set:function(){return c.j.apply(c,arguments)},get:function(){return c.get.apply(c,arguments)}}};var H={typeOf:function(a){if(a===null)return"null";var b=typeof a; +if(b=="object"){if(a instanceof Array)return"array";if(a instanceof Date)return"date";if(a.nodeType==1)return"element"}return b}},V={},Fa={includeIf:function(a,b,c){var d=_.indexOf(a,b);if(c)d==-1&&a.push(b);else a.splice(d,1);return a},sum:function(a,b){b=m.Function.compile(b);for(var c=0,d=0;d<a.length;d++){var e=1*b(a[d]);isNaN(e)||(c+=e)}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 d=m.Function.compile(b);_.detect(a,function(e){if(d(e)){c= +e;return true}});return c}},findById:function(a,b){return m.Xc.find(a,function(c){return c.A==b},null)},filter:function(a,b){var c=[];c.Ab=function(j){for(var l=0;l<c.length;l++)if(!c[l](j))return false;return true};var d=w.M;function e(j,l){if(l.charAt(0)==="!")return!e(j,l.substr(1));switch(typeof j){case "boolean":case "number":case "string":return(""+j).toLowerCase().indexOf(l)>-1;case "object":for(var p in j)if(p.charAt(0)!=="$"&&e(j[p],l))return true;return false;case "array":for(p=0;p<j.length;p++)if(e(j[p], +l))return true;return false;default:return false}}switch(typeof b){case "boolean":case "number":case "string":b={Rc:b};case "object":for(var f in b)f=="$"?function(){var j=(""+b[f]).toLowerCase();j&&c.push(function(l){return e(l,j)})}():function(){var j=f,l=(""+b[f]).toLowerCase();l&&c.push(function(p){return e(d(p,j),l)})}();break;case "function":c.push(b);break;default:return a}for(var g=[],h=0;h<a.length;h++){var i=a[h];c.Ab(i)&&g.push(i)}return g},add:function(a,b){a.push(_.isUndefined(b)?{}: +b);return a},count:function(a,b){if(!b)return a.length;var c=m.Function.compile(b);return _.reduce(a,0,function(d,e){return d+(c(e)?1:0)})},orderBy:function(a,b,c){function d(f,g){return X(g)?function(h,i){return f(i,h)}:f}function e(f,g){var h=typeof f,i=typeof g;if(h==i){if(h=="string")f=f.toLowerCase();if(h=="string")g=g.toLowerCase();if(f===g)return 0;return f<g?-1:1}else return h<i?-1:1}b=_.isArray(b)?b:[b];b=_.map(b,function(f){var g=false;if(typeof f=="string"&&(f.charAt(0)=="+"||f.charAt(0)== +"-")){g=f.charAt(0)=="-";f=f.substring(1)}var h=f?m.Function.compile(f):_.identity;return d(function(i,j){return e(h(i),h(j))},g)});return _.clone(a).sort(d(function(f,g){for(var h=0;h<b.length;h++){var i=b[h](f,g);if(i!==0)return i}return 0},c))},orderByToggle:function(a,b){var c=false,d=-1;_.detect(a,function(e,f){if(e==b){c=true;d=f;return true}if((e.charAt(0)=="+"||e.charAt(0)=="-")&&e.substring(1)==b){c=e.charAt(0)=="+";d=f;return true}});d>=0&&a.splice(d,1);a.unshift((c?"-":"+")+b);return a}, +orderByDirection:function(a,b,c,d){c=c||"ng-ascend";d=d||"ng-descend";a=a[0]||"";var e=true;if(a.charAt(0)=="-"){a=a.substring(1);e=false}else if(a.charAt(0)=="+")a=a.substring(1);return a==b?e?c:d:""},merge:function(a,b,c){var d=a[b];if(!d){d={};a[b]=d}Y(c,d);return a}},Ga={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")+'"'},quoteUnicode:function(a){a=m.String.quote(a); +for(var b=[],c=0;c<a.length;c++){var d=a.charCodeAt(c);if(d<128)b.push(a.charAt(c));else{d="000"+d.toString(16);b.push("\\u"+d.substring(d.length-4))}}return b.join("")},toDate: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}},Ha={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"}},Ia={compile:function(a){if(_.isFunction(a))return a;else if(a){var b=new w;return function(c){b.q=c;return b.eval(a)}}else return function(c){return c}}};G("Global",[H],["extend","clone","isEqual","isElement","isArray","isFunction","isUndefined"]);G("Collection",[H,V],["each","map","reduce","reduceRight","detect","select","reject","all","any","include","invoke","pluck","max","min","sortBy","sortedIndex","toArray","size"]); +G("Array",[H,V,Fa],["first","last","compact","flatten","without","uniq","intersect","zip","indexOf","lastIndexOf"]);G("Object",[H,V,{}],["keys","values"]);G("String",[H,Ga],[]);G("Date",[H,Ha],[]);G("Function",[H,V,Ia],["bind","bindAll","delay","defer","wrap","compose"]);z.$=function(a){for(var b=[],c=0,d;(d=a.indexOf("{{",c))>-1;){c<d&&b.push(a.substr(c,d-c));c=d;d=a.indexOf("}}",d);d=d<0?a.length:d+2;b.push(a.substr(c,d-c));c=d}c!=a.length&&b.push(a.substr(c,a.length-c));return b.length===0?[a]: +b};z.Xb=function(a){a=z.$(a);return a.length>1||z.J(a[0])!==null};z.J=function(a){return(a=a.replace(/\n/gm," ").match(/^\{\{(.*)\}\}$/))?a[1]:null};z.prototype={wc:function(a){var b={};a.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,function(c,d,e){if(d)b[decodeURIComponent(d)]=decodeURIComponent(e)});return b},Va:function(a){var b=this;a=a||this.pa.Ha();var c=a.indexOf("#");if(!(c<0)){a=this.wc(a.substring(c+1));q(b.anchor,function(d,e){delete b.anchor[e]});q(a,function(d,e){b.anchor[e]=d})}},vc:function(a){r("URL change detected", +a);this.Va(a);this.c()},Jc:function(){var a=this.pa.Ha(),b=a.indexOf("#");if(b>-1)a=a.substring(0,b);a+="#";b="";for(var c in this.anchor){var d=this.anchor[c];if(typeof d==="undefined"||d===null)delete this.anchor[c];else{a+=b+encodeURIComponent(c);if(d!==true)a+="="+encodeURIComponent(d);b="&"}}this.pa.Fc(a);return a},c:function(){(new Date).getTime();var a=k(this.C).scope();a.j("$invalidWidgets",[]);a.c();(new Date).getTime();this.Jc();_.each(this.cb,function(b){b()})},U:function(a){var b=k(this.C), +c=b.find(a);if(b.is(a))c=c.andSelf();return c},Rb:function(){this.U("[ng-init]").each(function(){var a=k(this),b=a.scope();try{b.eval(a.attr("ng-init"))}catch(c){alert("EVAL ERROR:\n"+a.attr("ng-init")+"\n"+y(c,true))}})},K:function(a){this.U("[ng-entity]").attr("ng-watch",function(){try{var b=k(this);return a.K(b.attr("ng-entity"))+(b.attr("ng-watch")||"")}catch(c){alert(c)}})},compile:function(){var a=k(this.C),b=this;if(this.i.bd){var c=this.U(":submit").not("[ng-action]");c.attr("ng-action","$save()"); +c.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr",'{disabled:"{{$invalidWidgets}}"}')}this.Wa(this.C)(this.C,a.scope(),"");this.U("a[ng-action]").live("click",function(){var d=k(this);try{d.scope().eval(d.attr("ng-action"));d.removeAttr("ng-error");d.removeClass("ng-exception")}catch(e){d.addClass("ng-exception");d.attr("ng-error",y(e,true))}b.c();return false})},Hc:function(a,b,c){b=b.concat();var d=b.pop(),e=z.$(a.nodeValue);if(e.length>1||z.J(e[0])){var f=a.parentNode;if(ea(f)){f.setAttribute("ng-bind-template", +a.nodeValue);c.push({path:b,b:function(l){return new N(l,l.getAttribute("ng-bind-template"))}})}else for(var g=0;g<e.length;g++){var h=e[g],i=z.J(h),j;if(i){j=x.createElement("span");k(j).attr("ng-bind",i);g===0&&c.push({path:b.concat(d+g),b:this.Ta})}else if(W&&h.charAt(0)==" "){j=x.createElement("span");j.innerHTML=" "+h.substring(1)}else j=x.createTextNode(h);f.insertBefore(j,a)}f.removeChild(a)}},Wa:function(a){var b=[];this.Xa(a,[],b);return function(c,d,e){for(var f=b.length,g=0;g<f;g++){for(var h= +b[g],i=c,j=h.path,l=0;l<j.length;l++)i=i.childNodes[j[l]];try{d.ub(h.b(i,d,e))}catch(p){alert(p)}}}},Xa:function(a,b,c){var d=a.nodeType;if(d==Node.TEXT_NODE)this.Hc(a,b,c);else if(!(d!=Node.ELEMENT_NODE&&d!=Node.DOCUMENT_NODE))if(a.getAttribute){d=a.getAttribute("ng-non-bindable");if(!(d||d==="")){if(d=a.attributes){var e=a.getAttribute("ng-bind-attr");a.removeAttribute("ng-bind-attr");e=e?O(e):{};for(var f=d.length,g=0;g<f;g++){var h=d[g],i=h.name;h=W&&i=="href"?decodeURI(a.getAttribute(i,2)):h.value; +if(z.Xb(h))e[i]=h}d=y(e);d.length>2&&a.setAttribute("ng-bind-attr",d)}a.getAttribute||r(a);var j=a.getAttribute("ng-repeat");if(j){a.removeAttribute("ng-repeat");var l=this.Wa(a);d=x.createComment("ng-repeat: "+j);e=a.parentNode;e.insertBefore(d,a);e.removeChild(a);function p(t,u,A){var I=k(a).clone();I.css("display","");I.attr("ng-repeat-index",""+A);I.data("scope",t);l(I[0],t,u+A+":");return I}c.push({path:b,b:function(t,u,A){return new Ba(k(t),j,p,A)}})}else{a.getAttribute("ng-eval")&&c.push({path:b, +b:this.nc});a.getAttribute("ng-bind")&&c.push({path:b,b:this.Ta});a.getAttribute("ng-bind-attr")&&c.push({path:b,b:this.jc});a.getAttribute("ng-hide")&&c.push({path:b,b:this.oc});a.getAttribute("ng-show")&&c.push({path:b,b:this.pc});a.getAttribute("ng-class")&&c.push({path:b,b:this.kc});a.getAttribute("ng-class-odd")&&c.push({path:b,b:this.mc});a.getAttribute("ng-class-even")&&c.push({path:b,b:this.lc});a.getAttribute("ng-style")&&c.push({path:b,b:this.qc});a.getAttribute("ng-watch")&&c.push({path:b, +b:this.rc});d=a.nodeName;if(d=="INPUT"||d=="TEXTAREA"||d=="SELECT"||d=="BUTTON"){var C=this;c.push({path:b,b:function(t,u,A){t.name=A+t.name.split(":").pop();return C.Pc.Gb(k(t),u)}})}if(d=="OPTION")if(!k("<select/>").append(k(a).clone()).html().match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi))a.value=a.text;d=a.childNodes;for(e=0;e<d.length;e++)this.Xa(d[e],b.concat(e),c)}}}},nc:function(a){return new ua(a,a.getAttribute("ng-eval"))},Ta:function(a){return new N(a,"{{"+a.getAttribute("ng-bind")+ +"}}")},jc:function(a){return new ta(a,O(a.getAttribute("ng-bind-attr")))},oc:function(a){return new va(a,a.getAttribute("ng-hide"))},pc:function(a){return new wa(a,a.getAttribute("ng-show"))},kc:function(a){return new xa(a,a.getAttribute("ng-class"))},lc:function(a){return new ya(a,a.getAttribute("ng-class-even"))},mc:function(a){return new za(a,a.getAttribute("ng-class-odd"))},qc:function(a){return new Aa(a,a.getAttribute("ng-style"))},rc:function(a,b){b.watch(a.getAttribute("ng-watch"))}};P.Yc= +'<div><div class="ui-widget-overlay"></div><div id="ng-login" ng-non-bindable="true"><div class="ng-login-container"></div></div></div>';P.mb='<div ng-non-bindable="true" title="Permission Error:">Sorry, you do not have permission for this!</div>';P.prototype={bind:J(),ja:function(a){this.F.push(a);this.F.length==1&&this.Ca("/user_session/new.mini?return_url="+encodeURIComponent(this.db()))},ec:function(a){this.F.push(a);this.F.length==1&&this.Ca("/user_session/do_destroy.mini")},db:function(){return this.window.location.href.split("#")[0]}, +Ca:function(a){var b=this,c=(new Date).getTime(),d=this.db();d+="#$iframe_notify="+c;var e=k('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+d+'" src="'+this.da+a+'" width="500" height="330"/></div>');this.document.append(e);e.Ba({height:363,width:500,Hd:false,gc:true,title:'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>'});callbacks["_iframe_notify_"+c]=function(){e.Ba("destroy");e.remove();q(b.F,function(f){f()});b.F=[]}},Ua:function(){if(!this.Ga){this.Ga= +k(P.mb);this.Ga.Ba({gd:true,height:70,gc:true})}}};T.nb=S(J(),{all:function(){return[]},query:function(){return[]},load:function(){return{}},title:undefined});T.prototype={T:function(a){if(!a instanceof E)throw"Parameter must be an instance of Entity! "+y(a);var b=a.Q+"/"+a.A,c=this.B[b];if(c)E.ga(a,c);else c=this.B[b]=a;return c},load:function(a,b,c,d){if(b&&b!=="*"){var e=this;this.D(["GET",a.Q+"/"+b],function(f){a.R(f);a.ib();f=a.Sc(a);e.T(f);(c||n)(a)},d)}return a},Oa:function(a,b,c){var d=this, +e=[],f=0;q(b,function(g){e.push(d.load(a(),g,function(){f++;if(f==b.length)(c||n)(e)}))});return e},dc:function(a,b,c){return this.load(a,b,c,function(d){if(d.ea==404){a.A=b;(c||n)(a)}else throw d;})},cc:function(a,b){var c=this,d=[];d.ra=function(e){return e.Q==a.title};this.B.P.push(d);this.D(["GET",a.title],function(e){for(var f=0;f<e.length;f++){var g=a();g.R(e[f]);d.push(c.T(g))}(b||n)(d)});return d},save:function(a,b){var c=this,d={};a.jb(d);this.D(["POST","",d],function(e){a.R(e);var f=c.T(a); +_.each(c.B.P,function(g){g.ra(a)&&m.Array.includeIf(g,f,true)});if(a.sa)c.anchor[a.sa]=a.A;b&&b(a)})},remove:function(a,b){var c=this,d={};a.jb(d);this.D(["DELETE","",d],function(e){delete c.B[a.Q+"/"+a.A];_.each(c.B.P,function(f){for(var g=0;g<f.length;g++)f[g].A==a.A&&f.splice(g,1)});(b||n)(e)})},D:function(a,b,c){a.gb=b;a.hb=c||function(d){throw d;};this.S.push(a)},Fa:function(){function a(d,e){r("RESPONSE["+d+"]: ",e);if(e.ea==401)b.eb.ja(function(){b.post(c,a)});else if(e.ea)alert(y(e));else for(d= +0;d<e.length;d++){var f=e[d],g=c[d],h=f.ea;if(h)h==403?b.eb.Ua():g.hb(f);else g.gb(f)}}if(this.S.length!==0){var b=this,c=this.S;this.S=[];r("REQUEST:",c);this.post(c,a)}},Cc:function(a,b){function c(){d--;d===0&&b&&b()}var d=1;for(var e in a){var f=a[e];if(f&&f.ta==E.prototype.ta){d++;f.ta(c)}}c()},xc:function(a,b,c,d){var e=this,f=[];f.ra=K(false);this.B.P.push(f);this.D(["GET",a.title+"/"+b+"="+c],function(g){for(var h=0;h<g.length;h++){var i=(new a).R(g[h]);f.push(e.T(i))}d&&d(f)});return f}, +K:function(a,b){if(!a)return T.nb;var c=this,d=S(function(e){return new E(d,e)},{title:a,$$factory:true,datastore:this,defaults:b||{},load:function(e,f){return c.load(d(),e,f)},loadMany:function(e,f){return c.Oa(d,e,f)},loadOrCreate:function(e,f){return c.dc(d(),e,f)},all:function(e){return c.cc(d,e)},query:function(e,f,g){return c.xc(d,e,f,g)},properties:function(e){c.D(["GET",a+"/$properties"],e)}});return d},join:function(a){function b(){throw"Joined entities can not be instantiated into a document."; +}var c=_(a).hd().map(function(d,e){return e}).sortBy(function(d){var e=[];do{if(_(e).include(d))throw"Infinite loop in join: "+e.join(" -> ");e.push(d);if(!a[d])throw _("Named entity '<%=name%>' is undefined.").template({name:d});d=a[d].Z?a[d].Z.substring(0,a[d].Z.indexOf(".")):undefined}while(d);return e.length}).value();if(_(c).select(function(d){return a[d].Z}).length!=c.length-1)throw"Exactly one entity needs to be primary.";b.query=function(d,e){var f=[],g=d?d.substring(0,d.indexOf(".")):undefined; +if(g!=c[0])throw _("Named entity '<%=name%>' is not a primary entity.").template({name:g});var h=1;a[g].join.query(d.substring(d.indexOf(".")+1),e,function(i){var j=c[h++],l=a[j],p=l.Z,C={};_(i).each(function(t){var u={};f.push(u);u[g]=t;t=w.M(u,p);C[t]=t});l.join.Oa(_.toArray(C),function(t){var u={};_(t).each(function(A){u[A.A]=A});_(f).each(function(A){var I=w.M(A,p);A[j]=u[I]})})});return f};return b}};s.h=function(a){if(a)for(var b in a)this[b]=a[b]};s.h.get=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}};var v;q({currency:function(a){k(this.element).toggleClass("ng-format-negative",a<0);return"$"+s.number.apply(this,[a,2])},number:function(a,b){if(isNaN(a)||!isFinite(a))return"";b=typeof b=="undefined"?2:b;var c=a<0;a=Math.abs(a);var d=Math.pow(10,b);a=""+Math.round(a*d);var e=a.substring(0,a.length-b);e=e||"0";d=a.substring(a.length-b);a=c?"-":"";for(c=0;c<e.length;c++){if((e.length-c)%3===0&&c!== +0)a+=",";a+=e.charAt(c)}if(b>0){for(c=d.length;c<b;c++)d+="0";a+="."+d.substring(0,b)}return a},date:J(),json:function(a){k(this.element).addClass("ng-monospace");return y(a,true)},trackPackage:function(){var a=[{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]}];return function(b,c){b=b.replace(/^ */,"").replace(/ *$/,"");var d=b.replace(/ /g,""),e;q(a,function(f){q(f.na,function(g){if(g.test(d)){g=f.name+": "+b;var h=f.url+b;e=new s.h({text:g,url:h,html:'<a href="'+Da(h)+'">'+g+"</a>",Ld:b});_.breakLoop()}});e&&_.breakLoop()});return e?e:b?c||new s.h({text:b+" is not recognized"}): +null}}(),link:function(a,b){b=b||s.h.get(a);var c=s.h.get(a,"url")||s.h.get(a);if(c){if(m.O.wd(c)===null)c="mailto:"+c;a='<a href="'+L(c)+'">'+b+"</a>";return new s.h({text:b,url:c,html:a})}return a},bytes:function(){var a=["bytes","KB","MB","GB","TB","PB"];return function(b){if(b===null)return"";for(var c=0;b>1E3;){b/=1024;c++}b=""+b;var d=b.indexOf(".");if(d>-1&&d+2<b.length)b=b.substring(0,d+2);return b+" "+a[c]}}(),image:function(a,b,c){if(a&&a.url){var d="";if(b)d=' style="max-width: '+b+"px; max-height: "+ +(c||b)+'px;"';return new s.h({url:a.url,text:a.url,html:'<img src="'+a.url+'"'+d+"/>"})}return null},lowercase:function(a){return(a=s.h.get(a))?(""+a).toLowerCase():a},uppercase:function(a){return(a=s.h.get(a))?(""+a).toUpperCase():a},linecount:function(a){a=s.h.get(a);if(a===""||!a)return 1;return a.split(/\n|\f/).length},"if":function(a,b){return b?a:undefined},unless:function(a,b){return b?undefined:a},googleChartApi:S(function(a,b,c,d){b=b||{};a={Bb:a,jd:v.Cb(b,"color"),pd:v.title(b),ld:v.Cb(b, +"label"),kd:v.values(b),md:"bg,s,FFFFFF00"};if(_.isArray(b.Qc)){a.rd="x";a.qd="0:|"+b.Qc.join("|")}return v.encode(a,c,d)},{values:function(a){var b=[];q(a.Ec||[],function(c){var d=[];q(c.values||[],function(e){d.push(e)});b.push(d.join(","))});a=b.join("|");return a===""?null:"t:"+a},title:function(a){var b=[];a=a.title||[];q(_.isArray(a)?a:[a],function(c){b.push(encodeURIComponent(c))});return b.join("|")},collect:function(a,b){var c=[],d=0;q(a.Ec||[],function(e){var f=[];e=e[b]||[];q(_.isArray(e)? +e:[e],function(g){f.push(encodeURIComponent(g));d++});c.push(f.join("|"))});return d?c.join(","):null},encode:function(a,b,c){b=b||200;c=c||b;var d="http://chart.apis.google.com/chart?",e=[];a.od=b+"x"+c;q(a,function(f,g){f&&e.push(g+"="+f)});e.sort();d+=e.join("&");return new s.h({url:d,html:'<img width="'+b+'" height="'+c+'" src="'+d+'"/>'})}}),qrcode:function(a,b,c){return v.encode({Bb:"qr",nd:encodeURIComponent(a)},b,c)},chart:{Ed:function(a,b,c){return v("p",a,b,c)},Fd:function(a,b,c){return v("p3", +a,b,c)},Gd:function(a,b,c){return v("pc",a,b,c)},dd:function(a,b,c){return v("bhs",a,b,c)},cd:function(a,b,c){return v("bhg",a,b,c)},fd:function(a,b,c){return v("bvs",a,b,c)},ed:function(a,b,c){return v("bvg",a,b,c)},Ad:function(a,b,c){return v("lc",a,b,c)},Jd:function(a,b,c){return v("ls",a,b,c)},Id:function(a,b,c){return v("s",a,b,c)}},html:function(a){return new s.h({html:a})}},function(a,b){s[b]=a});v=s.googleChartApi;array=[].constructor;m.toJson=y;m.fromJson=O;E.ga=function(a,b){if(!(a===b|| +!a||!b)){var c=function(e,f,g){return g.substring(0,2)!=="$$"&&typeof e[g]!=="function"&&typeof f[g]!=="function"};for(var d in b)c(a,b,d)&&delete b[d];for(d in a)if(c(a,b,d))b[d]=a[d]}};E.prototype={$migrate:function(){Y(this.$$entity.vd,this);return this},$save:function(a){this.$$entity.Jb.save(this,a===true?undefined:a);a===true&&this.$$entity.Jb.Fa();return this},$loadFrom:function(a){E.ga(a,this);return this},$saveTo:function(a){E.ga(this,a);return this}};F.ua={"null":K(null),"true":K(true), +"false":K(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 a.scope.j(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}};F.lb={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'};F.prototype={H:function(){return this.index+1<this.text.length?this.text.charAt(this.index+1):false},parse:function(){for(var a=this.g,b=F.ua,c=true;this.index<this.text.length;){var d=this.text.charAt(this.index);if(d=='"'||d=="'"){this.Ac(d);c=true}else if(d=="("||d=="["){a.push({index:this.index,text:d}); +this.index++}else if(d=="{"){c=this.H();if(c==":"||c=="("){a.push({index:this.index,text:d+c});this.index++}else a.push({index:this.index,text:d});this.index++;c=true}else if(d==")"||d=="]"||d=="}"){a.push({index:this.index,text:d});this.index++;c=false}else if(d==":"||d=="."||d==","||d==";"){a.push({index:this.index,text:d});this.index++;c=true}else if(c&&d=="/"){this.zc();c=false}else if(this.ha(d)){this.yc();c=false}else if(this.X(d)){this.Ya();c=false}else if(this.$b(d))this.index++;else{c=d+ +this.H();var e=b[d],f=b[c];if(f){a.push({index:this.index,text:c,b:f});this.index+=2}else if(e){a.push({index:this.index,text:d,b:e});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},ha:function(a){return"0"<=a&&a<="9"},$b:function(a){return a==" "||a=="\r"||a=="\t"||a=="\n"||a=="\u000b"},X:function(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"},yc:function(){for(var a= +"",b=this.index;this.index<this.text.length;){var c=this.text.charAt(this.index);if(c=="."||this.ha(c))a+=c;else break;this.index++}a=1*a;this.g.push({index:b,text:a,b:function(){return a}})},Ya:function(){for(var a="",b=this.index;this.index<this.text.length;){var c=this.text.charAt(this.index);if(c=="."||this.X(c)||this.ha(c))a+=c;else break;this.index++}c=F.ua[a];if(!c){c=function(d){return d.scope.get(a)};c.W=a}this.g.push({index:b,text:a,b:c})},Ac:function(a){var b=this.index,c=this.Kb;this.index++; +for(var d="",e=false;this.index<this.text.length;){var f=this.text.charAt(this.index);if(e){if(f=="u"){f=this.text.substring(this.index+1,this.index+5);this.index+=4;d+=String.fromCharCode(parseInt(f,16))}else{e=F.lb[f];d+=e?e:f}e=false}else if(f=="\\")e=true;else if(f==a){this.index++;this.g.push({index:b,text:d,b:function(){return d.length==c?m.String.toDate(d):d}});return}else d+=f;this.index++}throw"Lexer Error: Unterminated quote ["+this.text.substring(b)+"] starting at column '"+(b+1)+"' in expression '"+ +this.text+"'.";},zc:function(){var a=this.index;this.index++;for(var b="",c=false;this.index<this.text.length;){var d=this.text.charAt(this.index);if(c){b+=d;c=false}else if(d==="\\"){b+=d;c=true}else if(d==="/"){this.index++;c="";if(this.X(this.text.charAt(this.index))){this.Ya();c=this.g.pop().text}var e=new RegExp(b,c);this.g.push({index:a,text:b,yd:c,b:function(){return e}});return}else b+=d;this.index++}throw"Lexer Error: Unterminated RegExp ["+this.text.substring(a)+"] starting at column '"+ +(a+1)+"' in expression '"+this.text+"'.";}};D.ob=K(0);D.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)+"'.";},aa:function(){if(this.g.length===0)throw"Unexpected end of expression: "+this.text;return this.g[0]},H:function(a,b,c,d){var e=this.g;if(e.length>0){e=e[0];var f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return false},a:function(a,b,c,d){if(a=this.H(a,b,c, +d)){this.g.shift();return this.sd=a}return false},k:function(a){if(!this.a(a)){var b=this.H();throw"Expecting '"+a+"' at column '"+(b.index+1)+"' in '"+this.text+"' got '"+this.text.substring(b.index)+"'.";}},pb:function(a,b){var c=b.apply(this);return function(d){return a(d,c(d))}},s:function(a,b,c){var d=c.apply(this);return function(e){return b(e,a(e),d(e))}},Ia:function(){return this.g.length>0},I:function(){if(this.g.length!==0)throw"Did not understand '"+this.text.substring(this.g[0].index)+ +"' while evaluating '"+this.text+"'.";},oa:function(){for(var a=[];;){this.g.length>0&&!this.H("}",")",";","]")&&a.push(this.Ea());if(!this.a(";"))return function(b){for(var c,d=0;d<a.length;d++){var e=a[d];if(e)c=e(b)}return c}}},Ea:function(){for(var a=this.o(),b;;)if(b=this.a("|"))a=this.s(a,b.b,this.filter);else return a},filter:function(){return this.wa(m.filter)},O:function(){return this.wa(m.validator)},wa:function(a){for(var b=this.Wb(a),c=[];;)if(this.a(":"))c.push(this.o());else{var d=function(e, +f){f=[f];for(var g=0;g<c.length;g++)f.push(c[g](e));return b.apply(e,f)};return function(){return d}}},o:function(){return this.Gc()},Gc:function(){if(this.a("throw")){var a=this.ya();return function(b){throw a(b);}}else return this.ya()},ya:function(){var a=this.Ra(),b;if(b=this.a("=")){if(!a.W)throw"Left hand side '"+this.text.substring(0,b.index)+"' of assignment '"+this.text.substring(b.index)+"' is not assignable.";return this.s(function(){return a.W},b.b,this.Ra)}else return a},Ra:function(){for(var a= +this.Qa(),b;;)if(b=this.a("||"))a=this.s(a,b.b,this.Qa);else return a},Qa:function(){for(var a=this.Sa(),b;;)if(b=this.a("&&"))a=this.s(a,b.b,this.Sa);else return a},Sa:function(){var a;return(a=this.a("!"))?this.pb(a.b,this.Da):this.Da()},Da:function(){for(var a=this.Za(),b;;)if(b=this.a("==","!="))a=this.s(a,b.b,this.Za);else return a},Za:function(){for(var a=this.xa(),b;;)if(b=this.a("<",">","<=",">="))a=this.s(a,b.b,this.xa);else return a},xa:function(){for(var a=this.la(),b;b=this.a("+","-");)a= +this.s(a,b.b,this.la);return a},la:function(){for(var a=this.bb(),b;b=this.a("*","/","%");)a=this.s(a,b.b,this.bb);return a},bb:function(){var a;return this.a("+")?this.ba():(a=this.a("-"))?this.s(D.ob,a.b,this.la):this.ba()},Wb:function(a){var b=this.a(),c=b.text.split(".");a=a;for(var d,e=0;e<c.length;e++){d=c[e];if(a)a=a[d]}if(typeof a!="function")throw"Function '"+b.text+"' at column '"+(b.index+1)+"' in '"+this.text+"' is not defined.";return a},ba:function(){var a;if(this.a("(")){a=this.Ea(); +this.k(")");a=a}else if(this.a("["))a=this.vb();else if(this.a("{"))a=this.object();else if(this.a("{:"))a=this.za(false);else if(this.a("{("))a=this.za(true);else{var b=this.a();(a=b.b)||this.error("not a primary expression",b)}for(;b=this.a("(","[",".");)if(b.text==="(")a=this.Vb(a);else if(b.text==="[")a=this.sc(a);else if(b.text===".")a=this.Tb(a);else throw"IMPOSSIBLE";return a},za:function(a){var b=[];if(a){if(!this.a(")")){for(b.push(this.a().text);this.a(",");)b.push(this.a().text);this.k(")")}this.k(":")}var c= +this.oa();this.k("}");return function(d){return function(e){var f=new w(d.scope.q);f.j("$",e);for(var g=0;g<b.length;g++)f.j(b[g],arguments[g]);return c({scope:f})}}},Tb:function(a){var b=this.a().text;function c(d){return w.M(a(d),b)}c.W=b;return c},sc:function(a){var b=this.o();this.k("]");if(this.a("=")){var c=this.o();return function(d){return a(d)[b(d)]=c(d)}}else return function(d){var e=a(d);d=b(d);return e?e[d]:undefined}},Vb:function(a){var b=[];if(this.aa().text!=")"){do b.push(this.o()); +while(this.a(","))}this.k(")");return function(c){for(var d=[],e=0;e<b.length;e++)d.push(b[e](c));e=a(c);if(typeof e==="function")return e.apply(c,d);else throw"Expression '"+a.W+"' is not a function.";}},vb:function(){var a=[];if(this.aa().text!="]"){do a.push(this.o());while(this.a(","))}this.k("]");return function(b){for(var c=[],d=0;d<a.length;d++)c.push(a[d](b));return c}},object:function(){var a=[];if(this.aa().text!="}"){do{var b=this.a().text;this.k(":");var c=this.o();a.push({ac:b,value:c})}while(this.a(",")) +}this.k("}");return function(d){for(var e={},f=0;f<a.length;f++){var g=a[f],h=g.value(d);e[g.ac]=h}return e}},Qb:function(){for(var a=[];this.Ia();){a.push(this.Pb());this.a(";")||this.I()}return function(b){for(var c="",d=0;d<a.length;d++)c+=a[d](b);return c}},Pb:function(){var a=this.a().text,b,c;if(this.a("=")){b=a;a=this.a().text}if(this.a(":"))c=this.ba()(null);return function(d){var e=d.scope.get("$datastore").K(a,c);d.scope.j(a,e);if(b){e=e();e.sa=b;d.scope.j(b,e);return"$anchor."+b+":{"+b+ +"="+a+".load($anchor."+b+");"+b+".$$anchor="+m.String.quote(b)+";};"}else return""}},watch:function(){for(var a=[];this.Ia();){a.push(this.Oc());this.a(";")||this.I()}this.I();return function(b){for(var c=0;c<a.length;c++){var d=a[c](b);b.sb(d.name,d.b)}}},Oc:function(){var a=this.a().text;this.k(":");var b;if(this.aa().text=="{"){this.k("{");b=this.oa();this.k("}")}else b=this.o();return function(){return{name:a,b:b}}}};w.V={};w.M=function(a,b){if(!b)return a;for(var c=b.split("."),d,e=a,f=c.length, +g=0;g<f;g++){d=c[g];if(!d.match(/^[\$\w][\$\w\d]*$/))throw"Expression '"+b+"' is not a valid expression for accesing variables.";if(a){e=a;a=a[d]}if(_.isUndefined(a)&&d.charAt(0)=="$"){var h=m.Global.typeOf(e);if(d=(h=m[h.charAt(0).toUpperCase()+h.substring(1)])?h[[d.substring(1)]]:undefined)return a=_.bind(d,e,e)}}if(typeof a==="function"&&!a.Tc)return ga(e,a);return a};w.prototype={c:function(){var a=this;this.Ub();_.each(this.fb,function(b){a.l(b,"",{},function(){this.c(a)})})},ub:function(a){a&& +this.fb.push(a)},Zb:function(a){for(var b=0;b<a.length;b++){var c=a.charAt(b);if(c!="."&&!F.prototype.X(c))return false}return true},get:function(a){return w.M(this.q,a)},j:function(a,b){a=a.split(".");for(var c=this.q,d=0;a.length>1;d++){var e=a.shift(),f=c[e];if(!f){f={};c[e]=f}c=f}return c[a.shift()]=b},m:function(a,b){this.eval(a+"="+y(b))},eval:function(a,b){var c=w.V[a];if(!c){var d=new D(a);c=d.oa();d.I();w.V[a]=c}b=b||{};b.scope=this;return c(b)},l:function(a,b,c,d,e){try{var f=this.eval(b, +c);if(a.w){a.w=false;k(a.view).removeClass("ng-exception").removeAttr("ng-error")}d&&d.apply(a,[f]);return true}catch(g){$("Eval Widget Error:",g);b=y(g,true);a.w=true;k(a.view).addClass("ng-exception").attr("ng-error",b);e&&e.apply(a,[g,b]);return false}},Mc:function(a,b){var c=w.V[a];if(!c){c=(new D(a)).O();w.V[a]=c}a={scope:this};return c(a)(a,b)},K:function(a){return(new D(a)).Qb()({scope:this})},fc:function(a){this.q.Uc.push(a)},watch:function(a){var b=this;(new D(a)).watch()({scope:this,sb:function(c, +d){b.tb(c,function(e,f){try{return d({scope:b},e,f)}catch(g){alert(g)}})}})},tb:function(a,b){var c=this.qa[a];if(!c){c={Ma:[],o:a};this.qa[a]=c}c.Ma.push(b)},Ub:function(){var a=this,b=false;q(this.qa,function(c){var d=a.eval(c.o);if(d!==c.d){q(c.Ma,function(e){e(d,c.d);b=true});c.d=d}});return b}};ka.prototype={xb:function(a){return ba.Nb(a)},N:function(a,b,c,d){var e=this.Lc+this.hc++;ca[e]=function(h){delete m[e];d(200,h)};a={Md:b,Bd:a,Dd:c};a=this.xb(y(a));b=Math.ceil(a.length/this.ka);c=this.url+ +"/$/"+e+"/"+b+"/";for(var f=0;f<b;f++){var g=a.substr(f*this.ka,this.ka);this.getScript(c+(f+1)+"?h="+g,n)}}};aa.Zc="$DATASET:";aa.prototype={N:J()};la.prototype={N:function(a,b,c,d){var e=this;this.status.yb(c);this.Mb.N(a,b,c,function(){e.status.Ob();try{d.apply(this,arguments)}catch(f){alert(y(f))}e.update()})}};ma.prototype={fetchCurrentUser:function(a){var b=this;this.z.N("GET","/account.json",{},function(c,d){b.Ib=d.Kc;a(d.Kc)})},logout:function(a){var b=this;this.fa.ec(function(){delete b.Ib; +(a||n)()})},login:function(a){var b=this;this.fa.ja(function(){b.Sb(function(){(a||n)()})})},notAuthorized:function(){this.fa.Ua()}};q({regexp:function(a,b,c){return a.match(b)?null:c||"Value does not match expected format "+b+"."},number:function(a,b,c){var d=1*a;if(d==a){if(typeof b!="undefined"&&d<b)return"Value can not be less than "+b+".";if(typeof b!="undefined"&&d>c)return"Value can not be greater than "+c+".";return null}else return"Value is not a number."},integer:function(a,b,c){b=Ca.number(a, +b,c);if(b===null&&a!=Math.round(a))return"Value is not a whole number.";return b},date: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)."},ssn: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."},email: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."},phone: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."},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."},json:function(a){try{O(a);return null}catch(b){return b.toString()}}},function(a,b){Ca[b]=a});na.prototype={Gb:function(a,b){var c,d=a.attr("type").toLowerCase(), +e=a.attr("name");if(e)e=e.split(":").pop();var f="change",g=true;if(d=="button"||d=="submit"||d=="reset"||d=="image"){c=new Ja(a[0],e);f="click";g=false}else if(d=="text"||d=="textarea"||d=="hidden"||d=="password"){c=new oa(a[0],e);f="keyup change"}else if(d=="checkbox"){c=new pa(a[0],e);f="click"}else if(d=="radio"){c=new sa(a[0],e);f="click"}else if(d=="select-one")c=new qa(a[0],e);else if(d=="select-multiple")c=new ra(a[0],e);else if(d=="file")c=this.Hb(a,e);else throw"Unknown type: "+d;a.data("controller", +c);var h=b.get("$binder");k(c.view,":input").bind(f,function(){if(c.e(b)){var i=k(c.view).attr("ng-action")||"";b.l(c,i)&&h.c(b)}return g});return c},Hb:function(a){var b="__uploadWidget_"+this.ic++,c=M.template(b);a.after(c);b=this.Aa({data:this.da+"/admin/ServerAPI.swf",width:"95",height:"20",align:"top",Od:"transparent"},{zd:"uploadWidgetId="+b,$c:"always"},b);a.remove();a=new M(c,a[0].name,b,this.da+"/data/"+this.G);k(b).data("controller",a);return a}};M.dispatchEvent=function(a,b,c){a=x.getElementById(a); +a=k(a).data("controller");M.prototype["_on_"+b].apply(a,c)};M.template=function(a){return k('<span class="ng-upload-widget"><input type="checkbox" ng-non-bindable="true"/><object id="'+a+'" /><a></a><span/></span>')};M.prototype={e:function(a){var b=this.view.find("input").attr("checked")?this.value:null;if(this.d===b)return false;else{a.j(this.$a,b);return true}},c:function(a){if((a=a.get(this.$a))&&this.value!==a){this.value=a;this.view.find("a").attr("href",this.value.url).text(this.value.text); +this.view.find("span").text(m.filter.bytes(this.value.size))}this.view.find("input").attr("checked",!!a)}};Q.prototype={e:K(true),c:n};Q.Yb=new Q;var Ja=Q;oa.prototype={e:function(a){var b=this.view.value;if(this.d===b)return false;else{a.m(this.exp,b);this.d=b;return true}},c:function(a){var b=this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}c=c?c:"";if(this.d!=c)this.d=b.value=c;var d=false;b.removeAttribute("ng-error");if(this.Bc)d=!(c&&c.length>0);var e=d?"Required Value": +null;if(!d&&this.O&&c){e=a.Mc(this.O,c);d=!!e}if(this.Ka!==e){this.Ka=d;if(e!==null){b.setAttribute("ng-error",e);a.fc(this)}k(b).toggleClass("ng-validation-error",d)}}};pa.prototype={e:function(a){var b=this.view;b=b.checked?b.value:"";if(this.d===b)return false;else{a.m(this.exp,b);this.d=b;return true}},c:function(a){var b=this.view,c=a.eval(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}b.checked=b.value==""+c}};qa.prototype={e:function(a){if(this.view.selectedIndex<0)a.m(this.exp, +null);else{var b=this.view.value;if(this.d===b)return false;else{a.m(this.exp,b);this.d=b;return true}}},c:function(a){var b=this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}if(c!==this.d){b.value=c?c:"";this.d=c}}};ra.prototype={selected:function(){for(var a=[],b=this.view.options,c=0;c<b.length;c++){var d=b[c];d.selected&&a.push(d.value)}return a},e:function(a){var b=this.selected();if(this.d===b)return false;else{a.m(this.exp,b);this.d=b;return true}},c:function(a){var b= +this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.p;a.m(this.exp,c)}if(c!==this.d){a=b.options;for(b=0;b<a.length;b++){var d=a[b];d.selected=_.include(c,d.value)}this.d=c}}};sa.prototype={e:function(a){var b=this.view;if(this.ia)return false;else{b.checked=true;this.d=a.m(this.exp,this.Ja);return this.ia=true}},c:function(a){var b=this.view,c=a.get(this.exp);if(this.p&&typeof c==="undefined"){c=this.p;a.m(this.exp,c)}if(this.d!=c){this.ia=b.checked=this.Ja==""+c;this.d=c}}};N.ab=function(a){switch(typeof a){case "string":case "boolean":case "number":return L(a); +case "function":return N.ab(a());case "object":if(a&&a.tagName&&a.nodeName&&a.ownerDocument&&a.removeAttribute)return ha(a);else if(a instanceof m.filter.h){switch(typeof a.html){case "string":case "number":return a.html;case "function":return a.html();case "object":if(a.html&&a.html.tagName&&a.html.nodeName&&a.html.ownerDocument&&a.html.removeAttribute)return ha(a.html);default:break}switch(typeof a.text){case "string":case "number":return L(a.text);case "function":return L(a.text());default:break}}if(a=== +null)return"";return L(y(a,true));default:return""}};N.prototype={e:n,c:function(a){for(var b=[],c=this.exp,d=c.length,e=0;e<d;e++){var f=c[e],g=z.J(f);if(g){a.l(this,g,this.Dc,function(h){b.push(N.ab(h))},function(h,i){fa(this.view,i)});if(this.w)return}else b.push(L(f))}fa(this.view,b.join(""))}};ta.prototype={e:n,c:function(a){var b=k(this.view),c=this.wb;if(this.w){this.w=false;b.removeClass("ng-exception").removeAttr("ng-error")}var d=b.is("img");for(var e in c){for(var f=z.$(c[e]),g=[],h=0;h< +f.length;h++){var i=z.J(f[h]);if(i)try{var j=a.eval(i,{element:b[0],attrName:e});if(j&&(j.constructor!==array||j.length!==0))g.push(j)}catch(l){this.w=true;$("BindAttrUpdater",l);i=y(l,true);g.push("["+i+"]");b.addClass("ng-exception").attr("ng-error",i)}else g.push(f[h])}f=g.length?g.join(""):null;if(d&&e=="src"&&!f)f=a.get("config.server")+"/images/blank.gif";b.attr(e,f)}}};ua.prototype={e:n,c:function(a){a.l(this,this.exp)}};va.prototype={e:n,c:function(a){a.l(this,this.exp,{},function(b){var c= +k(this.view);X(b)?c.hide():c.show()})}};wa.prototype={e:n,c:function(a){a.l(this,this.exp,{},function(b){var c=k(this.view);X(b)?c.show():c.hide()})}};xa.prototype={e:n,c:function(a){a.l(this,this.exp,{},function(b){if(b!==null&&b!==undefined)this.view.className=b})}};ya.prototype={e:n,c:function(a){a.l(this,this.exp,{},function(b){var c=a.get("$index");k(this.view).toggleClass(b,c%2===1)})}};za.prototype={e:n,c:function(a){a.l(this,this.exp,{},function(b){var c=a.get("$index");k(this.view).toggleClass(b, +c%2===0)})}};Aa.prototype={e:n,c:function(a){a.l(this,this.exp,{},function(b){k(this.view).attr("style","").css(b)})}};Ba.prototype={e:n,c:function(a){a.l(this,this.Y,{},function(b){var c=this;if(!b){b=[];a.Zb(this.Y)&&a.j(this.Y,b)}var d=b.length,e=this.children.length,f=this.view,g=0,h=null,i=this.bc,j=this.Nc,l=0;q(b,function(C,t){if(l<e){h=c.children[l];h.scope.j(j,C)}else{var u=new w(a.q,c.prefix+j+" in "+c.Y+"["+l+"]");u.j("$index",l);i&&u.j(i,t);u.j(j,C);h={scope:u,element:c.template(u,c.prefix, +l)};f.after(h.element);c.children.push(h)}f=h.element;C=(new Date).getTime();h.scope.c();g+=(new Date).getTime()-C;l++});for(b=e;b>d;--b){var p=this.children.pop().element[0];p.parentNode.removeChild(p)}if(h&&h.element[0].nodeName==="OPTION")if(d=k(h.element[0].parentNode).data("controller")){d.d=undefined;d.c(a)}})}};B.va="mouseleave mouseout click dblclick keypress keyup";B.uc=function(){B.ma();var a=k(this);a.bind(B.va,B.ma);var b=a.position(),c=x.documentElement,d=(self.innerWidth||c&&c.clientWidth|| +x.body.clientWidth)-b.left;c=a.hasClass("ng-exception")?"EXCEPTION:":"Validation error...";a=a.attr("ng-error");d=d>375?"left":"right";c=k("<div id='ng-callout' style='width:300px'><div class='ng-arrow-"+d+"'/><div class='ng-title'>"+c+"</div><div class='ng-content'>"+a+"</div></div>");k("body").append(c);if(d==="left")a=b.left+this.offsetWidth+11;else{a=b.left-315;c.find(".ng-arrow-right").css({left:301})}c.css({left:a+"px",top:b.top-3+"px"});return true};B.ma=function(){k("#ng-callout").unbind(B.va, +B.ma).remove();return true};B.prototype={bind:function(){this.C.find(".ng-validation-error,.ng-exception").live("mouseover",B.uc)}};U.kb='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>';U.prototype={yb:function(){this.ca===0&&this.Pa.show();this.ca++},Ob:function(){this.ca--;this.ca===0&&this.Pa.hide("fold")}}})(window,document); @@ -1,3 +1,26 @@ +/** + * The MIT License + * + * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ (function(window, document){ /** * @@ -140,11 +163,7 @@ var Base64 = { return string; } -};// Copyright (C) 2008,2009 BRAT Tech LLC - -// IE compatibility - -if (typeof document.getAttribute == 'undefined') +};if (typeof document.getAttribute == 'undefined') document.getAttribute = function() {}; if (typeof Node == 'undefined') { Node = { @@ -163,16 +182,16 @@ if (typeof Node == 'undefined') { }; } -function noop() {}; +function noop() {} +if (!window['console']) window['console']={'log':noop, 'error':noop}; var consoleNode, foreach = _.each, extend = _.extend, - console = window['console'] || ({'log':noop, 'error':noop }), jQuery = window['jQuery'], msie = jQuery['browser']['msie'], - log = function(){console.log.apply(this, arguments);}, - error = function(){console.error.apply(this, arguments);}, + log = function(){window['console']['log'].apply(this, arguments);}, + error = function(){window['console']['error'].apply(this, arguments);}, angular = window['angular'] || (window['angular'] = {}), angularValidator = angular['validator'] || (angular['validator'] = {}), angularFilter = angular['filter'] || (angular['filter'] = {}), @@ -180,6 +199,7 @@ var consoleNode, angularAlert = angular['alert'] || (angular['alert'] = function(){ log(arguments); window.alert.apply(window, arguments); }); + function consoleLog(level, objs) { var log = document.createElement("div"); @@ -212,7 +232,7 @@ function isLeafNode (node) { default: return false; } -}; +} function setHtml(node, html) { if (isLeafNode(node)) { @@ -233,14 +253,14 @@ function escapeHtml(html) { replace(/&/g, '&'). replace(/</g, '<'). replace(/>/g, '>'); -}; +} function escapeAttr(html) { if (!html || !html.replace) return html; return html.replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"'); -}; +} function bind(_this, _function) { if (!_this) @@ -250,7 +270,7 @@ function bind(_this, _function) { return function() { return _function.apply(_this, arguments); }; -}; +} function shiftBind(_this, _function) { return function() { @@ -260,7 +280,7 @@ function shiftBind(_this, _function) { } return _function.apply(_this, args); }; -}; +} function outerHTML(node) { var temp = document.createElement('div'); @@ -268,18 +288,18 @@ function outerHTML(node) { var outerHTML = temp.innerHTML; temp.removeChild(node); return outerHTML; -}; +} function trim(str) { return str.replace(/^ */, '').replace(/ *$/, ''); -}; +} function toBoolean(value) { var v = ("" + value).toLowerCase(); if (v == 'f' || v == '0' || v == 'false' || v == 'no') value = false; return !!value; -}; +} function merge(src, dst) { for ( var key in src) { @@ -292,7 +312,7 @@ function merge(src, dst) { merge(src[key], value); } } -}; +} // //////////////////////////// // Loader @@ -303,7 +323,7 @@ function Loader(document, head, config) { this.head = jQuery(head); this.config = config; this.location = window.location; -}; +} Loader.prototype = { load: function() { @@ -479,7 +499,7 @@ function UrlWatcher(location) { return url; }; this.expectedUrl = location.href; -}; +} UrlWatcher.prototype = { watch: function() { @@ -836,40 +856,41 @@ var angularFunction = { } }; -(function(){ - function define(dst, chain, names){ - foreach(chain, function(parent){ - extend(angular[dst], parent); - }); - foreach(names, function(name){ - angular[dst][name] = _[name]; - }); - } - define('Global', [angularGlobal], - ['extend', 'clone','isEqual', - 'isElement', 'isArray', 'isFunction', 'isUndefined']); - define('Collection', [angularGlobal, angularCollection], - ['each', 'map', 'reduce', 'reduceRight', 'detect', - 'select', 'reject', 'all', 'any', 'include', - 'invoke', 'pluck', 'max', 'min', 'sortBy', - 'sortedIndex', 'toArray', 'size']); - define('Array', [angularGlobal, angularCollection], - ['first', 'last', 'compact', 'flatten', 'without', - 'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']); - define('Object', [angularGlobal, angularCollection], - ['keys', 'values']); - define('String', [angularGlobal]); - define('Function', [angularGlobal], - ['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']); -})();// Copyright (C) 2009 BRAT Tech LLC -Binder = function(doc, widgetFactory, urlWatcher, config) { +function defineApi(dst, chain, underscoreNames){ + var lastChain = _.last(chain); + foreach(underscoreNames, function(name){ + lastChain[name] = _[name]; + }); + angular[dst] = angular[dst] || {}; + foreach(chain, function(parent){ + extend(angular[dst], parent); + }); +} +defineApi('Global', [angularGlobal], + ['extend', 'clone','isEqual', + 'isElement', 'isArray', 'isFunction', 'isUndefined']); +defineApi('Collection', [angularGlobal, angularCollection], + ['each', 'map', 'reduce', 'reduceRight', 'detect', + 'select', 'reject', 'all', 'any', 'include', + 'invoke', 'pluck', 'max', 'min', 'sortBy', + 'sortedIndex', 'toArray', 'size']); +defineApi('Array', [angularGlobal, angularCollection, angularArray], + ['first', 'last', 'compact', 'flatten', 'without', + 'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']); +defineApi('Object', [angularGlobal, angularCollection, angularObject], + ['keys', 'values']); +defineApi('String', [angularGlobal, angularString], []); +defineApi('Date', [angularGlobal, angularDate], []); +defineApi('Function', [angularGlobal, angularCollection, angularFunction], + ['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']); +function Binder(doc, widgetFactory, urlWatcher, config) { this.doc = doc; this.urlWatcher = urlWatcher; this.anchor = {}; this.widgetFactory = widgetFactory; this.config = config || {}; this.updateListeners = []; -}; +} Binder.parseBindings = function(string) { var results = []; @@ -902,327 +923,323 @@ Binder.binding = function(string) { }; -Binder.prototype.parseQueryString = function(query) { - var params = {}; - query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, - function (match, left, right) { - if (left) params[decodeURIComponent(left)] = decodeURIComponent(right); - }); - return params; -}; - -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); - foreach(self.anchor, function(newValue, key) { - delete self.anchor[key]; - }); - foreach(anchorQuery, function(newValue, key) { - self.anchor[key] = newValue; - }); -}; - -Binder.prototype.onUrlChange = function (url) { - log("URL change detected", url); - this.parseAnchor(url); - this.updateView(); -}; - -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; -}; - -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();}); -}; - -Binder.prototype.docFindWithSelf = function(exp){ - var doc = jQuery(this.doc); - var selection = doc.find(exp); - if (doc.is(exp)){ - selection = selection.andSelf(); - } - return selection; -}; - -Binder.prototype.executeInit = function() { - this.docFindWithSelf("[ng-init]").each(function() { - var jThis = jQuery(this); - var scope = jThis.scope(); - try { - scope.eval(jThis.attr('ng-init')); - } catch (e) { - alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + toJson(e, true)); +Binder.prototype = { + parseQueryString: function(query) { + var params = {}; + query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, + function (match, left, right) { + if (left) params[decodeURIComponent(left)] = decodeURIComponent(right); + }); + return params; + }, + + 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); + foreach(self.anchor, function(newValue, key) { + delete self.anchor[key]; + }); + foreach(anchorQuery, function(newValue, key) { + self.anchor[key] = newValue; + }); + }, + + onUrlChange: function (url) { + log("URL change detected", url); + this.parseAnchor(url); + this.updateView(); + }, + + 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 = '&'; + } } - }); -}; - -Binder.prototype.entity = function (scope) { - this.docFindWithSelf("[ng-entity]").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) { - alert(e); + this.urlWatcher.setUrl(url); + return url; + }, + + 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();}); + }, + + docFindWithSelf: function(exp){ + var doc = jQuery(this.doc); + var selection = doc.find(exp); + if (doc.is(exp)){ + selection = selection.andSelf(); } - }); -}; - -Binder.prototype.compile = function() { - var jNode = jQuery(this.doc); - var self = this; - if (this.config.autoSubmit) { - var submits = this.docFindWithSelf(":submit").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(), ""); - this.docFindWithSelf("a[ng-action]").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', toJson(e, true)); + return selection; + }, + + executeInit: function() { + this.docFindWithSelf("[ng-init]").each(function() { + var jThis = jQuery(this); + var scope = jThis.scope(); + try { + scope.eval(jThis.attr('ng-init')); + } catch (e) { + alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + toJson(e, true)); + } + }); + }, + + entity: function (scope) { + this.docFindWithSelf("[ng-entity]").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) { + alert(e); + } + }); + }, + + compile: function() { + var jNode = jQuery(this.doc); + var self = this; + if (this.config.autoSubmit) { + var submits = this.docFindWithSelf(":submit").not("[ng-action]"); + submits.attr("ng-action", "$save()"); + submits.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr", '{disabled:"{{$invalidWidgets}}"}'); } - self.updateView(); - return false; - }); -}; - -Binder.prototype.translateBinding = function(node, parentPath, factories) { - var path = parentPath.concat(); - var offset = path.pop(); - var parts = Binder.parseBindings(node.nodeValue); - if (parts.length > 1 || Binder.binding(parts[0])) { - var parent = node.parentNode; - if (isLeafNode(parent)) { - parent.setAttribute('ng-bind-template', node.nodeValue); - factories.push({path:path, fn:function(node, scope, prefix) { - return new BindUpdater(node, node.getAttribute('ng-bind-template')); - }}); - } else { - for (var i = 0; i < parts.length; i++) { - var part = parts[i]; - var binding = 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:Binder.prototype.ng_bind}); + this.precompile(this.doc)(this.doc, jNode.scope(), ""); + this.docFindWithSelf("a[ng-action]").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', toJson(e, true)); + } + self.updateView(); + return false; + }); + }, + + translateBinding: function(node, parentPath, factories) { + var path = parentPath.concat(); + var offset = path.pop(); + var parts = Binder.parseBindings(node.nodeValue); + if (parts.length > 1 || Binder.binding(parts[0])) { + var parent = node.parentNode; + if (isLeafNode(parent)) { + parent.setAttribute('ng-bind-template', node.nodeValue); + factories.push({path:path, fn:function(node, scope, prefix) { + return new BindUpdater(node, node.getAttribute('ng-bind-template')); + }}); + } else { + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + var binding = 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:this.ng_bind}); + } + } else if (msie && part.charAt(0) == ' ') { + newNode = document.createElement("span"); + newNode.innerHTML = ' ' + part.substring(1); + } else { + newNode = document.createTextNode(part); } - } else if (msie && part.charAt(0) == ' ') { - newNode = document.createElement("span"); - newNode.innerHTML = ' ' + part.substring(1); - } else { - newNode = document.createTextNode(part); + parent.insertBefore(newNode, node); } - parent.insertBefore(newNode, node); } + parent.removeChild(node); } - parent.removeChild(node); - } -}; - -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]]; + }, + + 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) { + alert(e); + } } - try { - scope.addWidget(factory.fn(node, scope, prefix)); - } catch (e) { - alert(e); + }; + }, + + 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 ? 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 = msie && attrName == 'href' ? + decodeURI(node.getAttribute(attrName, 2)) : attr.value; + if (Binder.hasBinding(attrValue)) { + bindings[attrName] = attrValue; + } + } + var json = toJson(bindings); + if (json.length > 2) { + node.setAttribute("ng-bind-attr", json); } } - }; -}; - -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 ? 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 = msie && attrName == 'href' ? - decodeURI(node.getAttribute(attrName, 2)) : attr.value; - if (Binder.hasBinding(attrValue)) { - bindings[attrName] = attrValue; - } - } - var json = toJson(bindings); - if (json.length > 2) { - node.setAttribute("ng-bind-attr", json); + + if (!node.getAttribute) 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); + function template(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 RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix); + }}); + return; } - } - - if (!node.getAttribute) 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 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; + + 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); + } + }, + + ng_eval: function(node) { + return new EvalUpdater(node, node.getAttribute('ng-eval')); + }, + + ng_bind: function(node) { + return new BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}"); + }, + + ng_bind_attr: function(node) { + return new BindAttrUpdater(node, fromJson(node.getAttribute('ng-bind-attr'))); + }, + + ng_hide: function(node) { + return new HideUpdater(node, node.getAttribute('ng-hide')); + }, + + ng_show: function(node) { + return new ShowUpdater(node, node.getAttribute('ng-show')); + }, + + ng_class: function(node) { + return new ClassUpdater(node, node.getAttribute('ng-class')); + }, + + ng_class_even: function(node) { + return new ClassEvenUpdater(node, node.getAttribute('ng-class-even')); + }, + + ng_class_odd: function(node) { + return new ClassOddUpdater(node, node.getAttribute('ng-class-odd')); + }, + + ng_style: function(node) { + return new StyleUpdater(node, node.getAttribute('ng-style')); + }, + + ng_watch: function(node, scope) { + scope.watch(node.getAttribute('ng-watch')); } - - var children = node.childNodes; - for (var k = 0; k < children.length; k++) { - this.precompileNode(children[k], path.concat(k), factories); - } -}; - -Binder.prototype.ng_eval = function(node) { - return new EvalUpdater(node, node.getAttribute('ng-eval')); -}; - -Binder.prototype.ng_bind = function(node) { - return new BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}"); -}; - -Binder.prototype.ng_bind_attr = function(node) { - return new BindAttrUpdater(node, fromJson(node.getAttribute('ng-bind-attr'))); -}; - -Binder.prototype.ng_hide = function(node) { - return new HideUpdater(node, node.getAttribute('ng-hide')); -}; - -Binder.prototype.ng_show = function(node) { - return new ShowUpdater(node, node.getAttribute('ng-show')); -}; - -Binder.prototype.ng_class = function(node) { - return new ClassUpdater(node, node.getAttribute('ng-class')); -}; - -Binder.prototype.ng_class_even = function(node) { - return new ClassEvenUpdater(node, node.getAttribute('ng-class-even')); -}; - -Binder.prototype.ng_class_odd = function(node) { - return new ClassOddUpdater(node, node.getAttribute('ng-class-odd')); -}; - -Binder.prototype.ng_style = function(node) { - return new StyleUpdater(node, node.getAttribute('ng-style')); -}; - -Binder.prototype.ng_watch = function(node, scope) { - scope.watch(node.getAttribute('ng-watch')); -}; -// Copyright (C) 2008,2009 BRAT Tech LLC - -ControlBar = function (document, serverUrl) { +};function ControlBar(document, serverUrl) { this.document = document; this.serverUrl = serverUrl; this.window = window; this.callbacks = []; }; -ControlBar.prototype.bind = function () { -}; - ControlBar.HTML = '<div>' + '<div class="ui-widget-overlay"></div>' + @@ -1231,60 +1248,64 @@ ControlBar.HTML = '</div>' + '</div>'; -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())); - } -}; - -ControlBar.prototype.logout = function (loginSubmitFn) { - this.callbacks.push(loginSubmitFn); - if (this.callbacks.length == 1) { - this.doTemplate("/user_session/do_destroy.mini"); - } -}; - -ControlBar.prototype.urlWithoutAnchor = function (path) { - return this.window.location.href.split("#")[0]; -}; - -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>' - }); - callbacks["_iframe_notify_" + id] = function() { - loginView.dialog("destroy"); - loginView.remove(); - foreach(self.callbacks, function(callback){ - callback(); - }); - self.callbacks = []; - }; -}; - ControlBar.FORBIDEN = '<div ng-non-bindable="true" title="Permission Error:">' + 'Sorry, you do not have permission for this!'+ '</div>'; -ControlBar.prototype.notAuthorized = function () { - if (this.forbidenView) return; - this.forbidenView = jQuery(ControlBar.FORBIDEN); - this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); -}; -// Copyright (C) 2009 BRAT Tech LLC -DataStore = function(post, users, anchor) { + +ControlBar.prototype = { + bind: function () { + }, + + login: function (loginSubmitFn) { + this.callbacks.push(loginSubmitFn); + if (this.callbacks.length == 1) { + this.doTemplate("/user_session/new.mini?return_url=" + encodeURIComponent(this.urlWithoutAnchor())); + } + }, + + logout: function (loginSubmitFn) { + this.callbacks.push(loginSubmitFn); + if (this.callbacks.length == 1) { + this.doTemplate("/user_session/do_destroy.mini"); + } + }, + + urlWithoutAnchor: function (path) { + return this.window.location.href.split("#")[0]; + }, + + 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>' + }); + callbacks["_iframe_notify_" + id] = function() { + loginView.dialog("destroy"); + loginView.remove(); + foreach(self.callbacks, function(callback){ + callback(); + }); + self.callbacks = []; + }; + }, + + notAuthorized: function () { + if (this.forbidenView) return; + this.forbidenView = jQuery(ControlBar.FORBIDEN); + this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); + } +};function DataStore(post, users, anchor) { this.post = post; this.users = users; this._cache = {$collections:[]}; @@ -1292,331 +1313,332 @@ DataStore = function(post, users, anchor) { this.bulkRequest = []; }; -DataStore.prototype.cache = function(document) { - if (document.constructor != Model) { - throw "Parameter must be an instance of Entity! " + toJson(document); - } - var key = document.$entity + '/' + document.$id; - var cachedDocument = this._cache[key]; - if (cachedDocument) { - Model.copyDirectFields(document, cachedDocument); - } else { - this._cache[key] = document; - cachedDocument = document; - } - return cachedDocument; -}; +DataStore.NullEntity = extend(function(){}, { + 'all': function(){return [];}, + 'query': function(){return [];}, + 'load': function(){return {};}, + 'title': undefined +}); -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||noop)(instance); - }, failure); - } - return instance; -}; - -DataStore.prototype.loadMany = function(entity, ids, callback) { - var self=this; - var list = []; - var callbackCount = 0; - foreach(ids, function(id){ - list.push(self.load(entity(), id, function(){ - callbackCount++; - if (callbackCount == ids.length) { - (callback||noop)(list); - } - })); - }); - return list; -} - -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||noop)(instance); +DataStore.prototype = { + cache: function(document) { + if (! document instanceof Model) { + throw "Parameter must be an instance of Entity! " + toJson(document); + } + var key = document.$entity + '/' + document.$id; + var cachedDocument = this._cache[key]; + if (cachedDocument) { + Model.copyDirectFields(document, cachedDocument); } else { - throw response; + this._cache[key] = document; + cachedDocument = document; } - }); -}; - -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||noop)(list); - }); - return list; -}; - -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); + return cachedDocument; + }, + + 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||noop)(instance); + }, failure); + } + return instance; + }, + + loadMany: function(entity, ids, callback) { + var self=this; + var list = []; + var callbackCount = 0; + foreach(ids, function(id){ + list.push(self.load(entity(), id, function(){ + callbackCount++; + if (callbackCount == ids.length) { + (callback||noop)(list); + } + })); + }); + return list; + }, + + 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||noop)(instance); + } else { + throw response; } }); - if (document.$$anchor) { - self.anchor[document.$$anchor] = document.$id; - } - if (callback) - callback(document); - }); -}; - -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); + }, + + 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||noop)(list); + }); + return list; + }, + + 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); }); - (callback||noop)(response); - }); -}; - -DataStore.prototype._jsonRequest = function(request, callback, failure) { - request.$$callback = callback; - request.$$failure = failure||function(response){ - throw response; - }; - this.bulkRequest.push(request); -}; - -DataStore.prototype.flush = function() { - if (this.bulkRequest.length === 0) return; - var self = this; - var bulkRequest = this.bulkRequest; - this.bulkRequest = []; - log('REQUEST:', bulkRequest); - function callback(code, bulkResponse){ - log('RESPONSE[' + code + ']: ', bulkResponse); - if(bulkResponse.$status_code == 401) { - self.users.login(function(){ - self.post(bulkRequest, callback); + }, + + 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); + } + } }); - } else if(bulkResponse.$status_code) { - alert(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(); + (callback||noop)(response); + }); + }, + + _jsonRequest: function(request, callback, failure) { + request.$$callback = callback; + request.$$failure = failure||function(response){ + throw response; + }; + this.bulkRequest.push(request); + }, + + flush: function() { + if (this.bulkRequest.length === 0) return; + var self = this; + var bulkRequest = this.bulkRequest; + this.bulkRequest = []; + log('REQUEST:', bulkRequest); + function callback(code, bulkResponse){ + log('RESPONSE[' + code + ']: ', bulkResponse); + if(bulkResponse.$status_code == 401) { + self.users.login(function(){ + self.post(bulkRequest, callback); + }); + } else if(bulkResponse.$status_code) { + alert(toJson(bulkResponse)); + } else { + for ( var i = 0; i < bulkResponse.length; i++) { + var response = bulkResponse[i]; + var request = bulkRequest[i]; + var responseCode = response.$status_code; + if(responseCode) { + if(responseCode == 403) { + self.users.notAuthorized(); + } else { + request.$$failure(response); + } } else { - request.$$failure(response); + request.$$callback(response); } - } else { - request.$$callback(response); } } } - } - this.post(bulkRequest, callback); -}; - -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 == Model.prototype.$save) { - saveCounter++; - item.$save(onSaveDone); - } - } - onSaveDone(); -}; - -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; -}; - -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)); + this.post(bulkRequest, callback); + }, + + 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 == Model.prototype.$save) { + saveCounter++; + item.$save(onSaveDone); + } } - entities.sort(function(a,b){return a.title > b.title ? 1 : -1;}); - if (callback) callback(entities); - }); - return entities; -}; - -DataStore.prototype.documentCountsByUser = function(){ - var counts = {}; - var self = this; - self.post([["GET", "$users"]], function(code, response){ - foreach(response[0], function(value, key){ - counts[key] = value; + onSaveDone(); + }, + + 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 counts; -}; - -DataStore.prototype.userDocumentIdsByEntity = function(user){ - var ids = {}; - var self = this; - self.post([["GET", "$users/" + user]], function(code, response){ - foreach(response[0], function(value, key){ - ids[key] = value; + return queryList; + }, + + 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 ids; -}; - -DataStore.NullEntity = function(){}; -DataStore.NullEntity.all = function(){return [];}; -DataStore.NullEntity.query = function(){return [];}; -DataStore.NullEntity.load = function(){return {};}; -DataStore.NullEntity.title = undefined; - -DataStore.prototype.entity = function(name, defaults){ - if (!name) { - return DataStore.NullEntity; - } - var self = this; - var entity = function(initialState){ - return new 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; -}; - -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 = Scope.getter(row, nextJoinOn); - joinIds[id] = id; + return entities; + }, + + documentCountsByUser: function(){ + var counts = {}; + var self = this; + self.post([["GET", "$users"]], function(code, response){ + foreach(response[0], function(value, key){ + counts[key] = value; }); - nextJoin.join.loadMany(_.toArray(joinIds), function(result){ - var byId = {}; + }); + return counts; + }, + + userDocumentIdsByEntity: function(user){ + var ids = {}; + var self = this; + self.post([["GET", "$users/" + user]], function(code, response){ + foreach(response[0], function(value, key){ + ids[key] = value; + }); + }); + return ids; + }, + + entity: function(name, defaults){ + if (!name) { + return DataStore.NullEntity; + } + var self = this; + var entity = extend(function(initialState){ + return new Model(entity, initialState); + }, { + // entity.name does not work as name seems to be reserved for functions + 'title': name, + '$$factory': true, + 'datastore': this, + 'defaults': defaults || {}, + 'load': function(id, callback){ + return self.load(entity(), id, callback); + }, + 'loadMany': function(ids, callback){ + return self.loadMany(entity, ids, callback); + }, + 'loadOrCreate': function(id, callback){ + return self.loadOrCreate(entity(), id, callback); + }, + 'all': function(callback){ + return self.loadAll(entity, callback); + }, + 'query': function(query, queryArgs, callback){ + return self.query(entity, query, queryArgs, callback); + }, + 'properties': function(callback) { + self._jsonRequest(["GET", name + "/$properties"], callback); + } + }); + return entity; + }, + + join: function(join){ + function fn(){ + 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){ - byId[doc.$id] = doc; - }); - _(joinedResult).each(function(row){ + var row = {}; + joinedResult.push(row); + row[baseName] = doc; var id = Scope.getter(row, nextJoinOn); - row[nextJoinName] = byId[id]; + 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 = Scope.getter(row, nextJoinOn); + row[nextJoinName] = byId[id]; + }); }); }); - }); - return joinedResult; - }; - return fn; -}; -// Copyright (C) 2009 BRAT Tech LLC - -angularFilter.Meta = function(obj){ + return joinedResult; + }; + return fn; + } +};angularFilter.Meta = function(obj){ if (obj) { for ( var key in obj) { this[key] = obj[key]; @@ -1917,17 +1939,17 @@ foreach({ angularFilterGoogleChartApi = angularFilter['googleChartApi']; array = [].constructor; -toJson = function(obj, pretty){ +function toJson(obj, pretty){ var buf = []; toJsonArray(buf, obj, pretty ? "\n " : null); return buf.join(''); }; -toPrettyJson = function(obj) { +function toPrettyJson(obj) { return toJson(obj, true); }; -fromJson = function(json) { +function fromJson(json) { try { var parser = new Parser(json, true); var expression = parser.primary(); @@ -1939,8 +1961,10 @@ fromJson = function(json) { } }; +angular['toJson'] = toJson; +angular['fromJson'] = fromJson; -toJsonArray = function(buf, obj, pretty){ +function toJsonArray(buf, obj, pretty){ var type = typeof obj; if (obj === null) { buf.push("null"); @@ -2007,15 +2031,13 @@ toJsonArray = function(buf, obj, pretty){ } } }; -// 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) -Model = function(entity, initial) { - this.$$entity = entity; +function Model(entity, initial) { + this['$$entity'] = entity; this.$loadFrom(initial||{}); - this.$entity = entity.title; + this.$entity = entity['title']; this.$migrate(); }; @@ -2036,43 +2058,44 @@ Model.copyDirectFields = function(src, dst) { } }; -Model.prototype.$migrate = function() { - merge(this.$$entity.defaults, this); - return this; -}; - -Model.prototype.$merge = function(other) { - merge(other, this); - return this; -}; - -Model.prototype.$save = function(callback) { - this.$$entity.datastore.save(this, callback === true ? undefined : callback); - if (callback === true) this.$$entity.datastore.flush(); - return this; -}; - -Model.prototype.$delete = function(callback) { - this.$$entity.datastore.remove(this, callback === true ? undefined : callback); - if (callback === true) this.$$entity.datastore.flush(); - return this; -}; - -Model.prototype.$loadById = function(id, callback) { - this.$$entity.datastore.load(this, id, callback); - return this; -}; - -Model.prototype.$loadFrom = function(other) { - Model.copyDirectFields(other, this); - return this; -}; - -Model.prototype.$saveTo = function(other) { - Model.copyDirectFields(this, other); - return this; -}; -Lexer = function(text, parsStrings){ +Model.prototype = { + '$migrate': function() { + merge(this['$$entity'].defaults, this); + return this; + }, + + '$merge': function(other) { + merge(other, this); + return this; + }, + + '$save': function(callback) { + this['$$entity'].datastore.save(this, callback === true ? undefined : callback); + if (callback === true) this['$$entity'].datastore.flush(); + return this; + }, + + '$delete': function(callback) { + this['$$entity'].datastore.remove(this, callback === true ? undefined : callback); + if (callback === true) this['$$entity'].datastore.flush(); + return this; + }, + + '$loadById': function(id, callback) { + this['$$entity'].datastore.load(this, id, callback); + return this; + }, + + '$loadFrom': function(other) { + Model.copyDirectFields(other, this); + return this; + }, + + '$saveTo': function(other) { + Model.copyDirectFields(this, other); + return this; + } +};function Lexer(text, parsStrings){ this.text = text; // UTC dates have 20 characters, we send them through parser this.dateParseLength = parsStrings ? 20 : -1; @@ -2104,210 +2127,214 @@ Lexer.OPERATORS = { '|':function(self, a,b){return b(self, a);}, '!':function(self, a){return !a;} }; +Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; -Lexer.prototype.peek = function() { - if (this.index + 1 < this.text.length) { - return this.text.charAt(this.index + 1); - } else { - return false; - } -}; - -Lexer.prototype.parse = function() { - var tokens = this.tokens; - var OPERATORS = 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}); +Lexer.prototype = { + peek: function() { + if (this.index + 1 < this.text.length) { + return this.text.charAt(this.index + 1); + } else { + return false; + } + }, + + parse: function() { + var tokens = this.tokens; + var OPERATORS = 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 { + } 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; } - 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; + } + return tokens; + }, + + isNumber: function(ch) { + return '0' <= ch && ch <= '9'; + }, + + isWhitespace: function(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || + ch == '\n' || ch == '\v'; + }, + + isIdent: function(ch) { + return 'a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' == ch || ch == '$'; + }, + + 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 { - throw "Lexer Error: Unexpected next character [" + - this.text.substring(this.index) + - "] in expression '" + this.text + - "' at column '" + (this.index+1) + "'."; + break; } - canStartRegExp = true; + this.index++; } - } - return tokens; -}; - -Lexer.prototype.isNumber = function(ch) { - return '0' <= ch && ch <= '9'; -}; - -Lexer.prototype.isWhitespace = function(ch) { - return ch == ' ' || ch == '\r' || ch == '\t' || - ch == '\n' || ch == '\v'; -}; - -Lexer.prototype.isIdent = function(ch) { - return 'a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' == ch || ch == '$'; -}; - -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; + number = 1 * number; + this.tokens.push({index:start, text:number, + fn:function(){return number;}}); + }, + + 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++; } - this.index++; - } - number = 1 * number; - this.tokens.push({index:start, text:number, - fn:function(){return number;}}); -}; - -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; + var fn = 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}); + }, + + readString: function(quote) { + var start = this.index; + var dateParseLength = this.dateParseLength; this.index++; - } - var fn = 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}); -}; -Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; -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 = Lexer.ESCAPE[ch]; - if (rep) { - string += rep; + 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 { - string += ch; + var rep = 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; } - 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; } + throw "Lexer Error: Unterminated quote [" + + this.text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + this.text + "'."; + }, + + readRegexp: function(quote) { + var start = this.index; this.index++; - } - throw "Lexer Error: Unterminated quote [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; -}; - -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 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; } - 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++; } - this.index++; + throw "Lexer Error: Unterminated RegExp [" + + this.text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + this.text + "'."; } - throw "Lexer Error: Unterminated RegExp [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; }; +///////////////////////////////////////// -Parser = function(text, parseStrings){ +function Parser(text, parseStrings){ this.text = text; this.tokens = new Lexer(text, parseStrings).parse(); this.index = 0; @@ -2317,505 +2344,505 @@ Parser.ZERO = function(){ return 0; }; -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) + "'."; -}; - -Parser.prototype.peekToken = function() { - if (this.tokens.length === 0) - throw "Unexpected end of expression: " + this.text; - return this.tokens[0]; -}; - -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)) { +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) + "'."; + }, + + peekToken: function() { + if (this.tokens.length === 0) + throw "Unexpected end of expression: " + this.text; + return this.tokens[0]; + }, + + 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; + }, + + 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; -}; - -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; -}; - -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) + "'."; - } -}; - -Parser.prototype._unary = function(fn, parse) { - var right = parse.apply(this); - return function(self) { - return fn(self, right(self)); - }; -}; - -Parser.prototype._binary = function(left, fn, parse) { - var right = parse.apply(this); - return function(self) { - return fn(self, left(self), right(self)); - }; -}; - -Parser.prototype.hasTokens = function () { - return this.tokens.length > 0; -}; - -Parser.prototype.assertAllConsumed = function(){ - if (this.tokens.length !== 0) { - throw "Did not understand '" + this.text.substring(this.tokens[0].index) + - "' while evaluating '" + this.text + "'."; - } -}; - -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; - }; + return false; + }, + + 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) + "'."; } - } -}; - -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; + }, + + _unary: function(fn, parse) { + var right = parse.apply(this); + return function(self) { + return fn(self, right(self)); + }; + }, + + _binary: function(left, fn, parse) { + var right = parse.apply(this); + return function(self) { + return fn(self, left(self), right(self)); + }; + }, + + hasTokens: function () { + return this.tokens.length > 0; + }, + + assertAllConsumed: function(){ + if (this.tokens.length !== 0) { + throw "Did not understand '" + this.text.substring(this.tokens[0].index) + + "' while evaluating '" + this.text + "'."; } - } -}; - -Parser.prototype.filter = function(){ - return this._pipeFunction(angular['filter']); -}; - -Parser.prototype.validator = function(){ - return this._pipeFunction(angular['validator']); -}; - -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; - }; + }, + + 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; + }; + } } - } -}; - -Parser.prototype.expression = function(){ - return this.throwStmt(); -}; - -Parser.prototype.throwStmt = function(){ - if (this.expect('throw')) { - var throwExp = this.assignment(); - return function (self) { - throw throwExp(self); - }; - } else { - return this.assignment(); - } -}; - -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."; + }, + + 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; + } } - var ident = function(){return left.isAssignable;}; - return this._binary(ident, token.fn, this.logicalOR); - } else { - return left; - } -}; - -Parser.prototype.logicalOR = function(){ - var left = this.logicalAND(); - var token; - while(true) { - if ((token = this.expect('||'))) { - left = this._binary(left, token.fn, this.logicalAND); + }, + + filter: function(){ + return this._pipeFunction(angular['filter']); + }, + + validator: function(){ + return this._pipeFunction(angular['validator']); + }, + + _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; + }; + } + } + }, + + expression: function(){ + return this.throwStmt(); + }, + + throwStmt: function(){ + if (this.expect('throw')) { + var throwExp = this.assignment(); + return function (self) { + throw throwExp(self); + }; } else { - return left; + return this.assignment(); } - } -}; - -Parser.prototype.logicalAND = function(){ - var left = this.negated(); - var token; - while(true) { - if ((token = this.expect('&&'))) { - left = this._binary(left, token.fn, this.negated); + }, + + 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; + return left; } - } -}; - -Parser.prototype.negated = function(){ - var token; - if (token = this.expect('!')) { - return this._unary(token.fn, this.equality); - } else { - return this.equality(); - } -}; - -Parser.prototype.equality = function(){ - var left = this.relational(); - var token; - while(true) { - if ((token = this.expect('==','!='))) { - left = this._binary(left, token.fn, this.relational); + }, + + 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; + } + } + }, + + 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; + } + } + }, + + negated: function(){ + var token; + if (token = this.expect('!')) { + return this._unary(token.fn, this.equality); } else { - return left; + return this.equality(); } - } -}; - -Parser.prototype.relational = function(){ - var left = this.additive(); - var token; - while(true) { - if ((token = this.expect('<', '>', '<=', '>='))) { - left = this._binary(left, token.fn, this.additive); + }, + + 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; + } + } + }, + + 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; + } + } + }, + + additive: function(){ + var left = this.multiplicative(); + var token; + while(token = this.expect('+','-')) { + left = this._binary(left, token.fn, this.multiplicative); + } + return left; + }, + + multiplicative: function(){ + var left = this.unary(); + var token; + while(token = this.expect('*','/','%')) { + left = this._binary(left, token.fn, this.unary); + } + return left; + }, + + unary: function(){ + var token; + if (this.expect('+')) { + return this.primary(); + } else if (token = this.expect('-')) { + return this._binary(Parser.ZERO, token.fn, this.multiplicative); } else { - return left; + return this.primary(); } - } -}; - -Parser.prototype.additive = function(){ - var left = this.multiplicative(); - var token; - while(token = this.expect('+','-')) { - left = this._binary(left, token.fn, this.multiplicative); - } - return left; -}; - -Parser.prototype.multiplicative = function(){ - var left = this.unary(); - var token; - while(token = this.expect('*','/','%')) { - left = this._binary(left, token.fn, this.unary); - } - return left; -}; - -Parser.prototype.unary = function(){ - var token; - if (this.expect('+')) { - return this.primary(); - } else if (token = this.expect('-')) { - return this._binary(Parser.ZERO, token.fn, this.multiplicative); - } else { - return this.primary(); - } -}; - -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; -}; - -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 { + }, + + functionIdent: function(fnScope) { 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); + 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; + }, + + 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 { - throw "IMPOSSIBLE"; + var token = this.expect(); + primary = token.fn; + if (!primary) { + this.error("not a primary expression", token); + } } - } - return primary; -}; - -Parser.prototype.closure = function(hasArgs) { - var args = []; - if (hasArgs) { - if (!this.expect(')')) { - args.push(this.expect().text); - while(this.expect(',')) { + 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; + }, + + 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(')'); + this.consume(":"); } - this.consume(":"); - } - var statements = this.statements(); - this.consume("}"); - return function(self){ - return function($){ - var scope = new Scope(self.scope.state); - scope.set('$', $); - for ( var i = 0; i < args.length; i++) { - scope.set(args[i], arguments[i]); - } - return statements({scope:scope}); - }; - }; -}; - -Parser.prototype.fieldAccess = function(object) { - var field = this.expect().text; - var fn = function (self){ - return Scope.getter(object(self), field); - }; - fn.isAssignable = field; - return fn; -}; - -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); + var statements = this.statements(); + this.consume("}"); + return function(self){ + return function($){ + var scope = new Scope(self.scope.state); + scope.set('$', $); + for ( var i = 0; i < args.length; i++) { + scope.set(args[i], arguments[i]); + } + return statements({scope:scope}); + }; }; - } else { - return function (self){ - var o = obj(self); - var i = indexFn(self); - return (o) ? o[i] : undefined; + }, + + fieldAccess: function(object) { + var field = this.expect().text; + var fn = function (self){ + return Scope.getter(object(self), field); }; - } -}; - -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); + fn.isAssignable = field; + return fn; + }, + + 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 { - throw "Expression '" + fn.isAssignable + "' is not a function."; + return function (self){ + var o = obj(self); + var i = indexFn(self); + return (o) ? o[i] : undefined; + }; } - }; -}; - -// This is used with json array declaration -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)); + }, + + functionCall: function(fn) { + var argsFn = []; + if (this.peekToken().text != ')') { + do { + argsFn.push(this.expression()); + } while (this.expect(',')); } - return array; - }; -}; - -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; - }; -}; - -Parser.prototype.entityDeclaration = function () { - var decl = []; - while(this.hasTokens()) { - decl.push(this.entityDecl()); - if (!this.expect(';')) { - this.assertAllConsumed(); + 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 + arrayDeclaration: function () { + var elementFns = []; + if (this.peekToken().text != ']') { + do { + elementFns.push(this.expression()); + } while (this.expect(',')); } - } - return function (self){ - var code = ""; - for ( var i = 0; i < decl.length; i++) { - code += decl[i](self); + this.consume(']'); + return function (self){ + var array = []; + for ( var i = 0; i < elementFns.length; i++) { + array.push(elementFns[i](self)); + } + return array; + }; + }, + + 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(',')); } - return code; - }; -}; - -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 ""; + 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; + }; + }, + + entityDeclaration: function () { + var decl = []; + while(this.hasTokens()) { + decl.push(this.entityDecl()); + if (!this.expect(';')) { + this.assertAllConsumed(); + } } - }; -}; - -Parser.prototype.watch = function () { - var decl = []; - while(this.hasTokens()) { - decl.push(this.watchDecl()); - if (!this.expect(';')) { - this.assertAllConsumed(); + return function (self){ + var code = ""; + for ( var i = 0; i < decl.length; i++) { + code += decl[i](self); + } + return code; + }; + }, + + 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 ""; + } + }; + }, + + 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); + this.assertAllConsumed(); + return function (self){ + for ( var i = 0; i < decl.length; i++) { + var d = decl[i](self); + self.addListener(d.name, d.fn); + } + }; + }, + + 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(); } - }; -}; - -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}; + }; } - return function(self) { - return {name:anchorName, fn:expression}; - }; }; -// Copyright (C) 2009 BRAT Tech LLC - -Scope = function(initialState, name) { +function Scope(initialState, name) { this.widgets = []; this.watchListeners = {}; this.name = name; @@ -2830,31 +2857,6 @@ Scope = function(initialState, name) { }; Scope.expressionCache = {}; - -Scope.prototype.updateView = function() { - var self = this; - this.fireWatchers(); - _.each(this.widgets, function(widget){ - self.evalWidget(widget, "", {}, function(){ - this.updateView(self); - }); - }); -}; - -Scope.prototype.addWidget = function(controller) { - if (controller) this.widgets.push(controller); -}; - -Scope.prototype.isProperty = function(exp) { - for ( var i = 0; i < exp.length; i++) { - var ch = exp.charAt(i); - if (ch!='.' && !Lexer.prototype.isIdent(ch)) { - return false; - } - } - return true; -}; - Scope.getter = function(instance, path) { if (!path) return instance; var element = path.split('.'); @@ -2885,135 +2887,158 @@ Scope.getter = function(instance, path) { return instance; }; -Scope.prototype.get = function(path) { - return Scope.getter(this.state, path); -}; - -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; -}; - -Scope.prototype.setEval = function(expressionText, value) { - this.eval(expressionText + "=" + toJson(value)); -}; - -Scope.prototype.eval = function(expressionText, context) { - var expression = Scope.expressionCache[expressionText]; - if (!expression) { - var parser = new Parser(expressionText); - expression = parser.statements(); - parser.assertAllConsumed(); - 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. -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]); +Scope.prototype = { + updateView: function() { + var self = this; + this.fireWatchers(); + _.each(this.widgets, function(widget){ + self.evalWidget(widget, "", {}, function(){ + this.updateView(self); + }); + }); + }, + + addWidget: function(controller) { + if (controller) this.widgets.push(controller); + }, + + isProperty: function(exp) { + for ( var i = 0; i < exp.length; i++) { + var ch = exp.charAt(i); + if (ch!='.' && !Lexer.prototype.isIdent(ch)) { + return false; + } } return true; - } catch (e){ - error('Eval Widget Error:', e); - var jsonError = toJson(e, true); - widget.hasError = true; - jQuery(widget.view). - addClass('ng-exception'). - attr('ng-error', jsonError); - if (onFailure) { - onFailure.apply(widget, [e, jsonError]); + }, + + get: function(path) { + return Scope.getter(this.state, path); + }, + + 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; } - return false; - } -}; - -Scope.prototype.validate = function(expressionText, value) { - var expression = Scope.expressionCache[expressionText]; - if (!expression) { - expression = new Parser(expressionText).validator(); - Scope.expressionCache[expressionText] = expression; - } - var self = {scope:this}; - return expression(self)(self, value); -}; - -Scope.prototype.entity = function(entityDeclaration) { - var expression = new Parser(entityDeclaration).entityDeclaration(); - return expression({scope:this}); -}; - -Scope.prototype.markInvalid = function(widget) { - this.state.$invalidWidgets.push(widget); -}; - -Scope.prototype.watch = function(declaration) { - var self = this; - new Parser(declaration).watch()({ - scope:this, - addListener:function(watch, exp){ - self.addWatchListener(watch, function(n,o){ - try { - return exp({scope:self}, n, o); - } catch(e) { - alert(e); - } - }); + instance[element.shift()] = value; + return value; + }, + + setEval: function(expressionText, value) { + this.eval(expressionText + "=" + toJson(value)); + }, + + eval: function(expressionText, context) { + var expression = Scope.expressionCache[expressionText]; + if (!expression) { + var parser = new Parser(expressionText); + expression = parser.statements(); + parser.assertAllConsumed(); + 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. + 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){ + error('Eval Widget Error:', e); + var jsonError = 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; } - }); -}; - -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); -}; - -Scope.prototype.fireWatchers = function() { - var self = this; - var fired = false; - foreach(this.watchListeners, function(watcher) { - var value = self.eval(watcher.expression); - if (value !== watcher.lastValue) { - foreach(watcher.listeners, function(listener){ - listener(value, watcher.lastValue); - fired = true; - }); - watcher.lastValue = value; + }, + + validate: function(expressionText, value) { + var expression = Scope.expressionCache[expressionText]; + if (!expression) { + expression = new Parser(expressionText).validator(); + Scope.expressionCache[expressionText] = expression; + } + var self = {scope:this}; + return expression(self)(self, value); + }, + + entity: function(entityDeclaration) { + var expression = new Parser(entityDeclaration).entityDeclaration(); + return expression({scope:this}); + }, + + markInvalid: function(widget) { + this.state.$invalidWidgets.push(widget); + }, + + watch: function(declaration) { + var self = this; + new Parser(declaration).watch()({ + scope:this, + addListener:function(watch, exp){ + self.addWatchListener(watch, function(n,o){ + try { + return exp({scope:self}, n, o); + } catch(e) { + alert(e); + } + }); + } + }); + }, + + addWatchListener: function(watchExpression, listener) { + var watcher = this.watchListeners[watchExpression]; + if (!watcher) { + watcher = {listeners:[], expression:watchExpression}; + this.watchListeners[watchExpression] = watcher; } - }); - return fired; -}; -// Copyright (C) 2008,2009 BRAT Tech LLC - -Server = function(url, getScript) { + watcher.listeners.push(listener); + }, + + fireWatchers: function() { + var self = this; + var fired = false; + foreach(this.watchListeners, function(watcher) { + var value = self.eval(watcher.expression); + if (value !== watcher.lastValue) { + foreach(watcher.listeners, function(listener){ + listener(value, watcher.lastValue); + fired = true; + }); + watcher.lastValue = value; + } + }); + return fired; + } +};function Server(url, getScript) { this.url = url; this.nextId = 0; this.getScript = getScript; @@ -3021,27 +3046,29 @@ Server = function(url, getScript) { this.maxSize = 1800; }; -Server.prototype.base64url = function(txt) { - return Base64.encode(txt); -}; - -Server.prototype.request = function(method, url, request, callback) { - var requestId = this.uuid + (this.nextId++); - angularCallbacks[requestId] = function(response) { - delete angular[requestId]; - callback(200, response); - }; - var payload = {u:url, m:method, p:request}; - payload = this.base64url(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, noop); +Server.prototype = { + base64url: function(txt) { + return Base64.encode(txt); + }, + + request: function(method, url, request, callback) { + var requestId = this.uuid + (this.nextId++); + angularCallbacks[requestId] = function(response) { + delete angular[requestId]; + callback(200, response); + }; + var payload = {u:url, m:method, p:request}; + payload = this.base64url(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, noop); + } } }; -FrameServer = function(frame) { +function FrameServer(frame) { this.frame = frame; }; FrameServer.PREFIX = "$DATASET:"; @@ -3059,7 +3086,7 @@ FrameServer.prototype = { }; -VisualServer = function(delegate, status, update) { +function VisualServer(delegate, status, update) { this.delegate = delegate; this.update = update; this.status = status; @@ -3080,14 +3107,13 @@ VisualServer.prototype = { }); } }; -// Copyright (C) 2008,2009 BRAT Tech LLC -Users = function(server, controlBar) { +function Users(server, controlBar) { this.server = server; this.controlBar = controlBar; }; Users.prototype = { - fetchCurrentUser:function(callback) { + 'fetchCurrentUser':function(callback) { var self = this; this.server.request("GET", "/account.json", {}, function(code, response){ self.current = response.user; @@ -3095,7 +3121,7 @@ Users.prototype = { }); }, - logout: function(callback) { + 'logout': function(callback) { var self = this; this.controlBar.logout(function(){ delete self.current; @@ -3103,7 +3129,7 @@ Users.prototype = { }); }, - login: function(callback) { + 'login': function(callback) { var self = this; this.controlBar.login(function(){ self.fetchCurrentUser(function(){ @@ -3112,12 +3138,10 @@ Users.prototype = { }); }, - notAuthorized: function(){ + 'notAuthorized': function(){ this.controlBar.notAuthorized(); } }; -// Copyright (C) 2009 BRAT Tech LLC - foreach({ 'regexp': function(value, regexp, msg) { if (!value.match(regexp)) { @@ -3198,10 +3222,7 @@ foreach({ } } }, function(v,k) {angularValidator[k] = v;}); -// Copyright (C) 2009 BRAT Tech LLC - - -WidgetFactory = function(serverUrl, database) { +function WidgetFactory(serverUrl, database) { this.nextUploadId = 0; this.serverUrl = serverUrl; this.database = database; @@ -3215,80 +3236,81 @@ WidgetFactory = function(serverUrl, database) { this.onChangeListener = function(){}; }; -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 ButtonController(input[0], exp); - event = "click"; - bubbleEvent = false; - } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { - controller = new TextController(input[0], exp); - event = "keyup change"; - } else if (type == 'checkbox') { - controller = new CheckboxController(input[0], exp); - event = "click"; - } else if (type == 'radio') { - controller = new RadioController(input[0], exp); - event="click"; - } else if (type == 'select-one') { - controller = new SelectController(input[0], exp); - } else if (type == 'select-multiple') { - controller = new 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); +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 ButtonController(input[0], exp); + event = "click"; + bubbleEvent = false; + } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { + controller = new TextController(input[0], exp); + event = "keyup change"; + } else if (type == 'checkbox') { + controller = new CheckboxController(input[0], exp); + event = "click"; + } else if (type == 'radio') { + controller = new RadioController(input[0], exp); + event="click"; + } else if (type == 'select-one') { + controller = new SelectController(input[0], exp); + } else if (type == 'select-multiple') { + controller = new 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; -}; - -WidgetFactory.prototype.createFileController = function(fileInput) { - var uploadId = '__uploadWidget_' + (this.nextUploadId++); - var view = 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 FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database); - jQuery(swfNode).data('controller', cntl); - return cntl; -}; - -WidgetFactory.prototype.createTextWidget = function(textInput) { - var controller = new TextController(textInput); - controller.onChange(this.onChangeListener); - return controller; + return bubbleEvent; + }; + jQuery(controller.view, ":input"). + bind(event, action); + return controller; + }, + + createFileController: function(fileInput) { + var uploadId = '__uploadWidget_' + (this.nextUploadId++); + var view = 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 FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database); + jQuery(swfNode).data('controller', cntl); + return cntl; + }, + + createTextWidget: function(textInput) { + var controller = new TextController(textInput); + controller.onChange(this.onChangeListener); + return controller; + } }; - ///////////////////// // FileController /////////////////////// -FileController = function(view, scopeName, uploader, databaseUrl) { +function FileController(view, scopeName, uploader, databaseUrl) { this.view = view; this.uploader = uploader; this.scopeName = scopeName; @@ -3312,99 +3334,89 @@ FileController.template = function(id) { '</span>'); }; -FileController.prototype._on_cancel = function() { -}; - -FileController.prototype._on_complete = function() { -}; - -FileController.prototype._on_httpStatus = function(status) { - alert("httpStatus:" + this.scopeName + " status:" + status); -}; - -FileController.prototype._on_ioError = function() { - alert("ioError:" + this.scopeName); -}; - -FileController.prototype._on_open = function() { - alert("open:" + this.scopeName); -}; - -FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) { -}; - -FileController.prototype._on_securityError = function() { - alert("securityError:" + this.scopeName); -}; - -FileController.prototype._on_uploadCompleteData = function(data) { - var value = 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(); -}; - -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(); -}; - -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; - } -}; - -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); -}; - -FileController.prototype.upload = function() { - if (this.name) { - this.uploader.uploadFile(this.attachmentsPath); +FileController.prototype = { + '_on_cancel': noop, + '_on_complete': noop, + '_on_httpStatus': function(status) { + alert("httpStatus:" + this.scopeName + " status:" + status); + }, + '_on_ioError': function() { + alert("ioError:" + this.scopeName); + }, + '_on_open': function() { + alert("open:" + this.scopeName); + }, + '_on_progress':noop, + '_on_securityError': function() { + alert("securityError:" + this.scopeName); + }, + '_on_uploadCompleteData': function(data) { + var value = 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(); + }, + '_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(); + }, + + 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; + } + }, + + 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); + }, + + upload: function() { + if (this.name) { + this.uploader.uploadFile(this.attachmentsPath); + } } }; - /////////////////////// // NullController /////////////////////// -NullController = function(view) {this.view = view;}; -NullController.prototype.updateModel = function() { return true; }; -NullController.prototype.updateView = function() { }; +function NullController(view) {this.view = view;}; +NullController.prototype = { + updateModel: function() { return true; }, + updateView: noop +}; NullController.instance = new NullController(); /////////////////////// // ButtonController /////////////////////// -ButtonController = function(view) {this.view = view;}; -ButtonController.prototype.updateModel = function(scope) { return true; }; -ButtonController.prototype.updateView = function(scope) {}; +var ButtonController = NullController; /////////////////////// // TextController /////////////////////// -TextController = function(view, exp) { +function TextController(view, exp) { this.view = view; this.exp = exp; this.validator = view.getAttribute('ng-validate'); @@ -3418,175 +3430,183 @@ TextController = function(view, exp) { } }; -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; - } -}; - -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); +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; + } + }, + + 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); } - jQuery(view).toggleClass('ng-validation-error', isValidationError); } }; /////////////////////// // CheckboxController /////////////////////// -CheckboxController = function(view, exp) { +function CheckboxController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = view.checked ? view.value : ""; }; -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; - } -}; - -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); +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; + } + }, + + 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); } - input.checked = input.value == (''+value); }; /////////////////////// // SelectController /////////////////////// -SelectController = function(view, exp) { +function SelectController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = view.value; }; -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; +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; + } + } + }, + + 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; - return true; } } }; -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 /////////////////////// -MultiSelectController = function(view, exp) { +function MultiSelectController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = this.selected(); }; -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; -}; - -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; - } -}; - -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; +MultiSelectController.prototype = { + selected: function () { + var value = []; + var options = this.view.options; for ( var i = 0; i < options.length; i++) { var option = options[i]; - option.selected = _.include(selected, option.value); + if (option.selected) { + value.push(option.value); + } + } + return value; + }, + + 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; + } + }, + + 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; } - this.lastValue = selected; } }; /////////////////////// // RadioController /////////////////////// -RadioController = function(view, exp) { +function RadioController(view, exp) { this.view = view; this.exp = exp; this.lastChecked = undefined; @@ -3595,35 +3615,37 @@ RadioController = function(view, exp) { this.initialValue = view.checked ? view.value : null; }; -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; - } -}; - -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; +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; + } + }, + + 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 /////////////////////// -BindUpdater = function(view, exp) { +function BindUpdater(view, exp) { this.view = view; this.exp = Binder.parseBindings(exp); this.hasError = false; @@ -3673,152 +3695,170 @@ BindUpdater.toText = function(obj) { } }; -BindUpdater.prototype.updateModel = function(scope) {}; -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 = Binder.binding(part); - if (binding) { - scope.evalWidget(this, binding, this.scopeSelf, function(value){ - html.push(BindUpdater.toText(value)); - }, function(e, text){ - setHtml(this.view, text); - }); - if (this.hasError) { - return; +BindUpdater.prototype = { + updateModel: noop, + 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 = Binder.binding(part); + if (binding) { + scope.evalWidget(this, binding, this.scopeSelf, function(value){ + html.push(BindUpdater.toText(value)); + }, function(e, text){ + setHtml(this.view, text); + }); + if (this.hasError) { + return; + } + } else { + html.push(escapeHtml(part)); } - } else { - html.push(escapeHtml(part)); } + setHtml(this.view, html.join('')); } - setHtml(this.view, html.join('')); }; -BindAttrUpdater = function(view, attrs) { +function BindAttrUpdater(view, attrs) { this.view = view; this.attrs = attrs; }; -BindAttrUpdater.prototype.updateModel = function(scope) {}; -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 = Binder.parseBindings(attributeTemplates[attrName]); - var attrValues = []; - for ( var i = 0; i < attributeTemplate.length; i++) { - var binding = Binder.binding(attributeTemplate[i]); - if (binding) { - try { - var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); - if (value && (value.constructor !== array || value.length !== 0)) - attrValues.push(value); - } catch (e) { - this.hasError = true; - error('BindAttrUpdater', e); - var jsonError = toJson(e, true); - attrValues.push('[' + jsonError + ']'); - jNode. - addClass('ng-exception'). - attr('ng-error', jsonError); +BindAttrUpdater.prototype = { + updateModel: noop, + 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 = Binder.parseBindings(attributeTemplates[attrName]); + var attrValues = []; + for ( var i = 0; i < attributeTemplate.length; i++) { + var binding = Binder.binding(attributeTemplate[i]); + if (binding) { + try { + var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); + if (value && (value.constructor !== array || value.length !== 0)) + attrValues.push(value); + } catch (e) { + this.hasError = true; + error('BindAttrUpdater', e); + var jsonError = toJson(e, true); + attrValues.push('[' + jsonError + ']'); + jNode. + addClass('ng-exception'). + attr('ng-error', jsonError); + } + } else { + attrValues.push(attributeTemplate[i]); } - } 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); + var attrValue = attrValues.length ? attrValues.join('') : null; + if(isImage && attrName == 'src' && !attrValue) + attrValue = scope.get('config.server') + '/images/blank.gif'; + jNode.attr(attrName, attrValue); + } } }; -EvalUpdater = function(view, exp) { +function EvalUpdater(view, exp) { this.view = view; this.exp = exp; this.hasError = false; }; -EvalUpdater.prototype.updateModel = function(scope) {}; -EvalUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp); +EvalUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp); + } }; -HideUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -HideUpdater.prototype.updateModel = function(scope) {}; -HideUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (toBoolean(hideValue)) { - view.hide(); - } else { - view.show(); - } - }); +function HideUpdater(view, exp) { this.view = view; this.exp = exp; }; +HideUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(hideValue){ + var view = jQuery(this.view); + if (toBoolean(hideValue)) { + view.hide(); + } else { + view.show(); + } + }); + } }; -ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ShowUpdater.prototype.updateModel = function(scope) {}; -ShowUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (toBoolean(hideValue)) { - view.show(); - } else { - view.hide(); - } - }); +function ShowUpdater(view, exp) { this.view = view; this.exp = exp; }; +ShowUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(hideValue){ + var view = jQuery(this.view); + if (toBoolean(hideValue)) { + view.show(); + } else { + view.hide(); + } + }); + } }; -ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ClassUpdater.prototype.updateModel = function(scope) {}; -ClassUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - if (classValue !== null && classValue !== undefined) { - this.view.className = classValue; - } - }); +function ClassUpdater(view, exp) { this.view = view; this.exp = exp; }; +ClassUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(classValue){ + if (classValue !== null && classValue !== undefined) { + this.view.className = classValue; + } + }); + } }; -ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ClassEvenUpdater.prototype.updateModel = function(scope) {}; -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); - }); +function ClassEvenUpdater(view, exp) { this.view = view; this.exp = exp; }; +ClassEvenUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(classValue){ + var index = scope.get('$index'); + jQuery(this.view).toggleClass(classValue, index % 2 === 1); + }); + } }; -ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ClassOddUpdater.prototype.updateModel = function(scope) {}; -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); - }); +function ClassOddUpdater(view, exp) { this.view = view; this.exp = exp; }; +ClassOddUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(classValue){ + var index = scope.get('$index'); + jQuery(this.view).toggleClass(classValue, index % 2 === 0); + }); + } }; -StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -StyleUpdater.prototype.updateModel = function(scope) {}; -StyleUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(styleValue){ - jQuery(this.view).attr('style', "").css(styleValue); - }); +function StyleUpdater(view, exp) { this.view = view; this.exp = exp; }; +StyleUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(styleValue){ + jQuery(this.view).attr('style', "").css(styleValue); + }); + } }; /////////////////////// // RepeaterUpdater /////////////////////// -RepeaterUpdater = function(view, repeaterExpression, template, prefix) { +function RepeaterUpdater(view, repeaterExpression, template, prefix) { this.view = view; this.template = template; this.prefix = prefix; @@ -3839,81 +3879,77 @@ RepeaterUpdater = function(view, repeaterExpression, template, prefix) { this.keyExp = match[2]; }; -RepeaterUpdater.prototype.updateModel = function(scope) {}; -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; - foreach(iterator, function(value, key){ - 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 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().element[0]; - unneeded.parentNode.removeChild(unneeded); - } - // 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); +RepeaterUpdater.prototype = { + updateModel: noop, + 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; + foreach(iterator, function(value, key){ + 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 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().element[0]; + unneeded.parentNode.removeChild(unneeded); + } + // 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 ////////////////////////////////// -PopUp = function(doc) { +function PopUp(doc) { this.doc = doc; }; PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; -PopUp.prototype.bind = function () { - var self = this; - this.doc.find('.ng-validation-error,.ng-exception'). - live("mouseover", PopUp.onOver); -}; - PopUp.onOver = function(e) { PopUp.onOut(); var jNode = jQuery(this); @@ -3953,29 +3989,39 @@ PopUp.onOut = function() { return true; }; +PopUp.prototype = { + bind: function () { + var self = this; + this.doc.find('.ng-validation-error,.ng-exception'). + live("mouseover", PopUp.onOver); + } +}; + ////////////////////////////////// // Status ////////////////////////////////// -Status = function(body) { +function Status(body) { this.loader = body.append(Status.DOM).find("#ng-loading"); this.requestCount = 0; }; Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>'; -Status.prototype.beginRequest = function () { - if (this.requestCount === 0) { - this.loader.show(); - } - this.requestCount++; -}; - -Status.prototype.endRequest = function () { - this.requestCount--; - if (this.requestCount === 0) { - this.loader.hide("fold"); +Status.prototype = { + beginRequest: function () { + if (this.requestCount === 0) { + this.loader.show(); + } + this.requestCount++; + }, + + endRequest: function () { + this.requestCount--; + if (this.requestCount === 0) { + this.loader.hide("fold"); + } } }; })(window, document);
\ No newline at end of file diff --git a/src/Binder.js b/src/Binder.js index 4c5299ed..36cb6ec3 100644 --- a/src/Binder.js +++ b/src/Binder.js @@ -1,12 +1,11 @@ -// Copyright (C) 2009 BRAT Tech LLC -Binder = function(doc, widgetFactory, urlWatcher, config) { +function Binder(doc, widgetFactory, urlWatcher, config) { this.doc = doc; this.urlWatcher = urlWatcher; this.anchor = {}; this.widgetFactory = widgetFactory; this.config = config || {}; this.updateListeners = []; -}; +} Binder.parseBindings = function(string) { var results = []; @@ -39,312 +38,314 @@ Binder.binding = function(string) { }; -Binder.prototype.parseQueryString = function(query) { - var params = {}; - query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, - function (match, left, right) { - if (left) params[decodeURIComponent(left)] = decodeURIComponent(right); - }); - return params; -}; - -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); - foreach(self.anchor, function(newValue, key) { - delete self.anchor[key]; - }); - foreach(anchorQuery, function(newValue, key) { - self.anchor[key] = newValue; - }); -}; - -Binder.prototype.onUrlChange = function (url) { - log("URL change detected", url); - this.parseAnchor(url); - this.updateView(); -}; - -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; -}; - -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();}); -}; - -Binder.prototype.docFindWithSelf = function(exp){ - var doc = jQuery(this.doc); - var selection = doc.find(exp); - if (doc.is(exp)){ - selection = selection.andSelf(); - } - return selection; -}; - -Binder.prototype.executeInit = function() { - this.docFindWithSelf("[ng-init]").each(function() { - var jThis = jQuery(this); - var scope = jThis.scope(); - try { - scope.eval(jThis.attr('ng-init')); - } catch (e) { - alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + toJson(e, true)); +Binder.prototype = { + parseQueryString: function(query) { + var params = {}; + query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, + function (match, left, right) { + if (left) params[decodeURIComponent(left)] = decodeURIComponent(right); + }); + return params; + }, + + 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); + foreach(self.anchor, function(newValue, key) { + delete self.anchor[key]; + }); + foreach(anchorQuery, function(newValue, key) { + self.anchor[key] = newValue; + }); + }, + + onUrlChange: function (url) { + log("URL change detected", url); + this.parseAnchor(url); + this.updateView(); + }, + + 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 = '&'; + } } - }); -}; - -Binder.prototype.entity = function (scope) { - this.docFindWithSelf("[ng-entity]").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) { - alert(e); + this.urlWatcher.setUrl(url); + return url; + }, + + 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();}); + }, + + docFindWithSelf: function(exp){ + var doc = jQuery(this.doc); + var selection = doc.find(exp); + if (doc.is(exp)){ + selection = selection.andSelf(); } - }); -}; - -Binder.prototype.compile = function() { - var jNode = jQuery(this.doc); - var self = this; - if (this.config.autoSubmit) { - var submits = this.docFindWithSelf(":submit").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(), ""); - this.docFindWithSelf("a[ng-action]").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', toJson(e, true)); + return selection; + }, + + executeInit: function() { + this.docFindWithSelf("[ng-init]").each(function() { + var jThis = jQuery(this); + var scope = jThis.scope(); + try { + scope.eval(jThis.attr('ng-init')); + } catch (e) { + alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + toJson(e, true)); + } + }); + }, + + entity: function (scope) { + this.docFindWithSelf("[ng-entity]").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) { + alert(e); + } + }); + }, + + compile: function() { + var jNode = jQuery(this.doc); + var self = this; + if (this.config.autoSubmit) { + var submits = this.docFindWithSelf(":submit").not("[ng-action]"); + submits.attr("ng-action", "$save()"); + submits.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr", '{disabled:"{{$invalidWidgets}}"}'); } - self.updateView(); - return false; - }); -}; - -Binder.prototype.translateBinding = function(node, parentPath, factories) { - var path = parentPath.concat(); - var offset = path.pop(); - var parts = Binder.parseBindings(node.nodeValue); - if (parts.length > 1 || Binder.binding(parts[0])) { - var parent = node.parentNode; - if (isLeafNode(parent)) { - parent.setAttribute('ng-bind-template', node.nodeValue); - factories.push({path:path, fn:function(node, scope, prefix) { - return new BindUpdater(node, node.getAttribute('ng-bind-template')); - }}); - } else { - for (var i = 0; i < parts.length; i++) { - var part = parts[i]; - var binding = 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:Binder.prototype.ng_bind}); + this.precompile(this.doc)(this.doc, jNode.scope(), ""); + this.docFindWithSelf("a[ng-action]").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', toJson(e, true)); + } + self.updateView(); + return false; + }); + }, + + translateBinding: function(node, parentPath, factories) { + var path = parentPath.concat(); + var offset = path.pop(); + var parts = Binder.parseBindings(node.nodeValue); + if (parts.length > 1 || Binder.binding(parts[0])) { + var parent = node.parentNode; + if (isLeafNode(parent)) { + parent.setAttribute('ng-bind-template', node.nodeValue); + factories.push({path:path, fn:function(node, scope, prefix) { + return new BindUpdater(node, node.getAttribute('ng-bind-template')); + }}); + } else { + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + var binding = 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:this.ng_bind}); + } + } else if (msie && part.charAt(0) == ' ') { + newNode = document.createElement("span"); + newNode.innerHTML = ' ' + part.substring(1); + } else { + newNode = document.createTextNode(part); } - } else if (msie && part.charAt(0) == ' ') { - newNode = document.createElement("span"); - newNode.innerHTML = ' ' + part.substring(1); - } else { - newNode = document.createTextNode(part); + parent.insertBefore(newNode, node); } - parent.insertBefore(newNode, node); } + parent.removeChild(node); } - parent.removeChild(node); - } -}; - -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]]; + }, + + 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) { + alert(e); + } } - try { - scope.addWidget(factory.fn(node, scope, prefix)); - } catch (e) { - alert(e); + }; + }, + + 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 ? 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 = msie && attrName == 'href' ? + decodeURI(node.getAttribute(attrName, 2)) : attr.value; + if (Binder.hasBinding(attrValue)) { + bindings[attrName] = attrValue; + } + } + var json = toJson(bindings); + if (json.length > 2) { + node.setAttribute("ng-bind-attr", json); } } - }; -}; - -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 ? 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 = msie && attrName == 'href' ? - decodeURI(node.getAttribute(attrName, 2)) : attr.value; - if (Binder.hasBinding(attrValue)) { - bindings[attrName] = attrValue; + + if (!node.getAttribute) 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); + function template(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 RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix); + }}); + return; } - var json = toJson(bindings); - if (json.length > 2) { - node.setAttribute("ng-bind-attr", json); + + 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 (!node.getAttribute) 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 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; + 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); + } + }, + + ng_eval: function(node) { + return new EvalUpdater(node, node.getAttribute('ng-eval')); + }, + + ng_bind: function(node) { + return new BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}"); + }, + + ng_bind_attr: function(node) { + return new BindAttrUpdater(node, fromJson(node.getAttribute('ng-bind-attr'))); + }, + + ng_hide: function(node) { + return new HideUpdater(node, node.getAttribute('ng-hide')); + }, + + ng_show: function(node) { + return new ShowUpdater(node, node.getAttribute('ng-show')); + }, + + ng_class: function(node) { + return new ClassUpdater(node, node.getAttribute('ng-class')); + }, + + ng_class_even: function(node) { + return new ClassEvenUpdater(node, node.getAttribute('ng-class-even')); + }, + + ng_class_odd: function(node) { + return new ClassOddUpdater(node, node.getAttribute('ng-class-odd')); + }, + + ng_style: function(node) { + return new StyleUpdater(node, node.getAttribute('ng-style')); + }, + + ng_watch: function(node, scope) { + scope.watch(node.getAttribute('ng-watch')); } - - var children = node.childNodes; - for (var k = 0; k < children.length; k++) { - this.precompileNode(children[k], path.concat(k), factories); - } -}; - -Binder.prototype.ng_eval = function(node) { - return new EvalUpdater(node, node.getAttribute('ng-eval')); -}; - -Binder.prototype.ng_bind = function(node) { - return new BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}"); -}; - -Binder.prototype.ng_bind_attr = function(node) { - return new BindAttrUpdater(node, fromJson(node.getAttribute('ng-bind-attr'))); -}; - -Binder.prototype.ng_hide = function(node) { - return new HideUpdater(node, node.getAttribute('ng-hide')); -}; - -Binder.prototype.ng_show = function(node) { - return new ShowUpdater(node, node.getAttribute('ng-show')); -}; - -Binder.prototype.ng_class = function(node) { - return new ClassUpdater(node, node.getAttribute('ng-class')); -}; - -Binder.prototype.ng_class_even = function(node) { - return new ClassEvenUpdater(node, node.getAttribute('ng-class-even')); -}; - -Binder.prototype.ng_class_odd = function(node) { - return new ClassOddUpdater(node, node.getAttribute('ng-class-odd')); -}; - -Binder.prototype.ng_style = function(node) { - return new StyleUpdater(node, node.getAttribute('ng-style')); -}; - -Binder.prototype.ng_watch = function(node, scope) { - scope.watch(node.getAttribute('ng-watch')); -}; +};
\ No newline at end of file diff --git a/src/ControlBar.js b/src/ControlBar.js index fb8147d5..53c87199 100644 --- a/src/ControlBar.js +++ b/src/ControlBar.js @@ -1,15 +1,10 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -ControlBar = function (document, serverUrl) { +function ControlBar(document, serverUrl) { this.document = document; this.serverUrl = serverUrl; this.window = window; this.callbacks = []; }; -ControlBar.prototype.bind = function () { -}; - ControlBar.HTML = '<div>' + '<div class="ui-widget-overlay"></div>' + @@ -18,54 +13,61 @@ ControlBar.HTML = '</div>' + '</div>'; -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())); - } -}; - -ControlBar.prototype.logout = function (loginSubmitFn) { - this.callbacks.push(loginSubmitFn); - if (this.callbacks.length == 1) { - this.doTemplate("/user_session/do_destroy.mini"); - } -}; - -ControlBar.prototype.urlWithoutAnchor = function (path) { - return this.window.location.href.split("#")[0]; -}; - -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>' - }); - callbacks["_iframe_notify_" + id] = function() { - loginView.dialog("destroy"); - loginView.remove(); - foreach(self.callbacks, function(callback){ - callback(); - }); - self.callbacks = []; - }; -}; - ControlBar.FORBIDEN = '<div ng-non-bindable="true" title="Permission Error:">' + 'Sorry, you do not have permission for this!'+ '</div>'; -ControlBar.prototype.notAuthorized = function () { - if (this.forbidenView) return; - this.forbidenView = jQuery(ControlBar.FORBIDEN); - this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); -}; + + +ControlBar.prototype = { + bind: function () { + }, + + login: function (loginSubmitFn) { + this.callbacks.push(loginSubmitFn); + if (this.callbacks.length == 1) { + this.doTemplate("/user_session/new.mini?return_url=" + encodeURIComponent(this.urlWithoutAnchor())); + } + }, + + logout: function (loginSubmitFn) { + this.callbacks.push(loginSubmitFn); + if (this.callbacks.length == 1) { + this.doTemplate("/user_session/do_destroy.mini"); + } + }, + + urlWithoutAnchor: function (path) { + return this.window.location.href.split("#")[0]; + }, + + 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>' + }); + callbacks["_iframe_notify_" + id] = function() { + loginView.dialog("destroy"); + loginView.remove(); + foreach(self.callbacks, function(callback){ + callback(); + }); + self.callbacks = []; + }; + }, + + notAuthorized: function () { + if (this.forbidenView) return; + this.forbidenView = jQuery(ControlBar.FORBIDEN); + this.forbidenView.dialog({bgiframe:true, height:70, modal:true}); + } +};
\ No newline at end of file diff --git a/src/DataStore.js b/src/DataStore.js index f99e5824..7952096f 100644 --- a/src/DataStore.js +++ b/src/DataStore.js @@ -1,6 +1,4 @@ -// Copyright (C) 2009 BRAT Tech LLC - -DataStore = function(post, users, anchor) { +function DataStore(post, users, anchor) { this.post = post; this.users = users; this._cache = {$collections:[]}; @@ -8,325 +6,329 @@ DataStore = function(post, users, anchor) { this.bulkRequest = []; }; -DataStore.prototype.cache = function(document) { - if (document.constructor != Model) { - throw "Parameter must be an instance of Entity! " + toJson(document); - } - var key = document.$entity + '/' + document.$id; - var cachedDocument = this._cache[key]; - if (cachedDocument) { - Model.copyDirectFields(document, cachedDocument); - } else { - this._cache[key] = document; - cachedDocument = document; - } - return cachedDocument; -}; - -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||noop)(instance); - }, failure); - } - return instance; -}; - -DataStore.prototype.loadMany = function(entity, ids, callback) { - var self=this; - var list = []; - var callbackCount = 0; - foreach(ids, function(id){ - list.push(self.load(entity(), id, function(){ - callbackCount++; - if (callbackCount == ids.length) { - (callback||noop)(list); - } - })); - }); - return list; -}; +DataStore.NullEntity = extend(function(){}, { + 'all': function(){return [];}, + 'query': function(){return [];}, + 'load': function(){return {};}, + 'title': undefined +}); -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||noop)(instance); +DataStore.prototype = { + cache: function(document) { + if (! document instanceof Model) { + throw "Parameter must be an instance of Entity! " + toJson(document); + } + var key = document.$entity + '/' + document.$id; + var cachedDocument = this._cache[key]; + if (cachedDocument) { + Model.copyDirectFields(document, cachedDocument); } else { - throw response; + this._cache[key] = document; + cachedDocument = document; } - }); -}; - -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)); + return cachedDocument; + }, + + 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||noop)(instance); + }, failure); } - (callback||noop)(list); - }); - return list; -}; - -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); + return instance; + }, + + loadMany: function(entity, ids, callback) { + var self=this; + var list = []; + var callbackCount = 0; + foreach(ids, function(id){ + list.push(self.load(entity(), id, function(){ + callbackCount++; + if (callbackCount == ids.length) { + (callback||noop)(list); + } + })); + }); + return list; + }, + + 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||noop)(instance); + } else { + throw response; } }); - if (document.$$anchor) { - self.anchor[document.$$anchor] = document.$id; - } - if (callback) - callback(document); - }); -}; - -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); + }, + + 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||noop)(list); + }); + return list; + }, + + 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); }); - (callback||noop)(response); - }); -}; - -DataStore.prototype._jsonRequest = function(request, callback, failure) { - request.$$callback = callback; - request.$$failure = failure||function(response){ - throw response; - }; - this.bulkRequest.push(request); -}; - -DataStore.prototype.flush = function() { - if (this.bulkRequest.length === 0) return; - var self = this; - var bulkRequest = this.bulkRequest; - this.bulkRequest = []; - log('REQUEST:', bulkRequest); - function callback(code, bulkResponse){ - log('RESPONSE[' + code + ']: ', bulkResponse); - if(bulkResponse.$status_code == 401) { - self.users.login(function(){ - self.post(bulkRequest, callback); + }, + + 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); + } + } }); - } else if(bulkResponse.$status_code) { - alert(toJson(bulkResponse)); - } else { - for ( var i = 0; i < bulkResponse.length; i++) { - var response = bulkResponse[i]; - var request = bulkRequest[i]; - var responseCode = response.$status_code; - if(responseCode) { - if(responseCode == 403) { - self.users.notAuthorized(); + (callback||noop)(response); + }); + }, + + _jsonRequest: function(request, callback, failure) { + request.$$callback = callback; + request.$$failure = failure||function(response){ + throw response; + }; + this.bulkRequest.push(request); + }, + + flush: function() { + if (this.bulkRequest.length === 0) return; + var self = this; + var bulkRequest = this.bulkRequest; + this.bulkRequest = []; + log('REQUEST:', bulkRequest); + function callback(code, bulkResponse){ + log('RESPONSE[' + code + ']: ', bulkResponse); + if(bulkResponse.$status_code == 401) { + self.users.login(function(){ + self.post(bulkRequest, callback); + }); + } else if(bulkResponse.$status_code) { + alert(toJson(bulkResponse)); + } else { + for ( var i = 0; i < bulkResponse.length; i++) { + var response = bulkResponse[i]; + var request = bulkRequest[i]; + var responseCode = response.$status_code; + if(responseCode) { + if(responseCode == 403) { + self.users.notAuthorized(); + } else { + request.$$failure(response); + } } else { - request.$$failure(response); + request.$$callback(response); } - } else { - request.$$callback(response); } } } - } - this.post(bulkRequest, callback); -}; - -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 == Model.prototype.$save) { - saveCounter++; - item.$save(onSaveDone); - } - } - onSaveDone(); -}; - -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)); + this.post(bulkRequest, callback); + }, + + saveScope: function(scope, callback) { + var saveCounter = 1; + function onSaveDone() { + saveCounter--; + if (saveCounter === 0 && callback) + callback(); } - if (callback) - callback(queryList); - }); - return queryList; -}; - -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)); + for(var key in scope) { + var item = scope[key]; + if (item && item.$save == Model.prototype.$save) { + saveCounter++; + item.$save(onSaveDone); + } } - entities.sort(function(a,b){return a.title > b.title ? 1 : -1;}); - if (callback) callback(entities); - }); - return entities; -}; - -DataStore.prototype.documentCountsByUser = function(){ - var counts = {}; - var self = this; - self.post([["GET", "$users"]], function(code, response){ - foreach(response[0], function(value, key){ - counts[key] = value; + onSaveDone(); + }, + + 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 counts; -}; - -DataStore.prototype.userDocumentIdsByEntity = function(user){ - var ids = {}; - var self = this; - self.post([["GET", "$users/" + user]], function(code, response){ - foreach(response[0], function(value, key){ - ids[key] = value; + return queryList; + }, + + 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 ids; -}; - -DataStore.NullEntity = function(){}; -DataStore.NullEntity.all = function(){return [];}; -DataStore.NullEntity.query = function(){return [];}; -DataStore.NullEntity.load = function(){return {};}; -DataStore.NullEntity.title = undefined; - -DataStore.prototype.entity = function(name, defaults){ - if (!name) { - return DataStore.NullEntity; - } - var self = this; - var entity = function(initialState){ - return new 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; -}; - -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 = Scope.getter(row, nextJoinOn); - joinIds[id] = id; + return entities; + }, + + documentCountsByUser: function(){ + var counts = {}; + var self = this; + self.post([["GET", "$users"]], function(code, response){ + foreach(response[0], function(value, key){ + counts[key] = value; }); - nextJoin.join.loadMany(_.toArray(joinIds), function(result){ - var byId = {}; + }); + return counts; + }, + + userDocumentIdsByEntity: function(user){ + var ids = {}; + var self = this; + self.post([["GET", "$users/" + user]], function(code, response){ + foreach(response[0], function(value, key){ + ids[key] = value; + }); + }); + return ids; + }, + + entity: function(name, defaults){ + if (!name) { + return DataStore.NullEntity; + } + var self = this; + var entity = extend(function(initialState){ + return new Model(entity, initialState); + }, { + // entity.name does not work as name seems to be reserved for functions + 'title': name, + '$$factory': true, + 'datastore': this, + 'defaults': defaults || {}, + 'load': function(id, callback){ + return self.load(entity(), id, callback); + }, + 'loadMany': function(ids, callback){ + return self.loadMany(entity, ids, callback); + }, + 'loadOrCreate': function(id, callback){ + return self.loadOrCreate(entity(), id, callback); + }, + 'all': function(callback){ + return self.loadAll(entity, callback); + }, + 'query': function(query, queryArgs, callback){ + return self.query(entity, query, queryArgs, callback); + }, + 'properties': function(callback) { + self._jsonRequest(["GET", name + "/$properties"], callback); + } + }); + return entity; + }, + + join: function(join){ + function fn(){ + 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){ - byId[doc.$id] = doc; - }); - _(joinedResult).each(function(row){ + var row = {}; + joinedResult.push(row); + row[baseName] = doc; var id = Scope.getter(row, nextJoinOn); - row[nextJoinName] = byId[id]; + 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 = Scope.getter(row, nextJoinOn); + row[nextJoinName] = byId[id]; + }); }); }); - }); - return joinedResult; - }; - return fn; -}; + return joinedResult; + }; + return fn; + } +};
\ No newline at end of file diff --git a/src/Filters.js b/src/Filters.js index 67fcffa1..666c9f30 100644 --- a/src/Filters.js +++ b/src/Filters.js @@ -1,5 +1,3 @@ -// Copyright (C) 2009 BRAT Tech LLC - angularFilter.Meta = function(obj){ if (obj) { for ( var key in obj) { diff --git a/src/JSON.js b/src/JSON.js index 14fce1fb..0c842865 100644 --- a/src/JSON.js +++ b/src/JSON.js @@ -1,16 +1,16 @@ array = [].constructor; -toJson = function(obj, pretty){ +function toJson(obj, pretty){ var buf = []; toJsonArray(buf, obj, pretty ? "\n " : null); return buf.join(''); }; -toPrettyJson = function(obj) { +function toPrettyJson(obj) { return toJson(obj, true); }; -fromJson = function(json) { +function fromJson(json) { try { var parser = new Parser(json, true); var expression = parser.primary(); @@ -22,8 +22,10 @@ fromJson = function(json) { } }; +angular['toJson'] = toJson; +angular['fromJson'] = fromJson; -toJsonArray = function(buf, obj, pretty){ +function toJsonArray(buf, obj, pretty){ var type = typeof obj; if (obj === null) { buf.push("null"); diff --git a/src/Loader.js b/src/Loader.js index 104dfec8..5207defb 100644 --- a/src/Loader.js +++ b/src/Loader.js @@ -1,7 +1,3 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -// IE compatibility - if (typeof document.getAttribute == 'undefined') document.getAttribute = function() {}; if (typeof Node == 'undefined') { diff --git a/src/Model.js b/src/Model.js index 35f6a1c1..4a3a1806 100644 --- a/src/Model.js +++ b/src/Model.js @@ -1,12 +1,10 @@ -// 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) -Model = function(entity, initial) { - this.$$entity = entity; +function Model(entity, initial) { + this['$$entity'] = entity; this.$loadFrom(initial||{}); - this.$entity = entity.title; + this.$entity = entity['title']; this.$migrate(); }; @@ -27,39 +25,41 @@ Model.copyDirectFields = function(src, dst) { } }; -Model.prototype.$migrate = function() { - merge(this.$$entity.defaults, this); - return this; -}; - -Model.prototype.$merge = function(other) { - merge(other, this); - return this; -}; - -Model.prototype.$save = function(callback) { - this.$$entity.datastore.save(this, callback === true ? undefined : callback); - if (callback === true) this.$$entity.datastore.flush(); - return this; -}; - -Model.prototype.$delete = function(callback) { - this.$$entity.datastore.remove(this, callback === true ? undefined : callback); - if (callback === true) this.$$entity.datastore.flush(); - return this; -}; - -Model.prototype.$loadById = function(id, callback) { - this.$$entity.datastore.load(this, id, callback); - return this; -}; - -Model.prototype.$loadFrom = function(other) { - Model.copyDirectFields(other, this); - return this; -}; - -Model.prototype.$saveTo = function(other) { - Model.copyDirectFields(this, other); - return this; -}; +Model.prototype = { + '$migrate': function() { + merge(this['$$entity'].defaults, this); + return this; + }, + + '$merge': function(other) { + merge(other, this); + return this; + }, + + '$save': function(callback) { + this['$$entity'].datastore.save(this, callback === true ? undefined : callback); + if (callback === true) this['$$entity'].datastore.flush(); + return this; + }, + + '$delete': function(callback) { + this['$$entity'].datastore.remove(this, callback === true ? undefined : callback); + if (callback === true) this['$$entity'].datastore.flush(); + return this; + }, + + '$loadById': function(id, callback) { + this['$$entity'].datastore.load(this, id, callback); + return this; + }, + + '$loadFrom': function(other) { + Model.copyDirectFields(other, this); + return this; + }, + + '$saveTo': function(other) { + Model.copyDirectFields(this, other); + return this; + } +};
\ No newline at end of file diff --git a/src/Parser.js b/src/Parser.js index cdece11e..333b8413 100644 --- a/src/Parser.js +++ b/src/Parser.js @@ -1,4 +1,4 @@ -Lexer = function(text, parsStrings){ +function Lexer(text, parsStrings){ this.text = text; // UTC dates have 20 characters, we send them through parser this.dateParseLength = parsStrings ? 20 : -1; @@ -30,210 +30,214 @@ Lexer.OPERATORS = { '|':function(self, a,b){return b(self, a);}, '!':function(self, a){return !a;} }; +Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; -Lexer.prototype.peek = function() { - if (this.index + 1 < this.text.length) { - return this.text.charAt(this.index + 1); - } else { - return false; - } -}; - -Lexer.prototype.parse = function() { - var tokens = this.tokens; - var OPERATORS = 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}); +Lexer.prototype = { + peek: function() { + if (this.index + 1 < this.text.length) { + return this.text.charAt(this.index + 1); + } else { + return false; + } + }, + + parse: function() { + var tokens = this.tokens; + var OPERATORS = 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 { + } 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; } - 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; + } + return tokens; + }, + + isNumber: function(ch) { + return '0' <= ch && ch <= '9'; + }, + + isWhitespace: function(ch) { + return ch == ' ' || ch == '\r' || ch == '\t' || + ch == '\n' || ch == '\v'; + }, + + isIdent: function(ch) { + return 'a' <= ch && ch <= 'z' || + 'A' <= ch && ch <= 'Z' || + '_' == ch || ch == '$'; + }, + + 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 { - throw "Lexer Error: Unexpected next character [" + - this.text.substring(this.index) + - "] in expression '" + this.text + - "' at column '" + (this.index+1) + "'."; + break; } - canStartRegExp = true; + this.index++; } - } - return tokens; -}; - -Lexer.prototype.isNumber = function(ch) { - return '0' <= ch && ch <= '9'; -}; - -Lexer.prototype.isWhitespace = function(ch) { - return ch == ' ' || ch == '\r' || ch == '\t' || - ch == '\n' || ch == '\v'; -}; - -Lexer.prototype.isIdent = function(ch) { - return 'a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' == ch || ch == '$'; -}; - -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; + number = 1 * number; + this.tokens.push({index:start, text:number, + fn:function(){return number;}}); + }, + + 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++; } - this.index++; - } - number = 1 * number; - this.tokens.push({index:start, text:number, - fn:function(){return number;}}); -}; - -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; + var fn = 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}); + }, + + readString: function(quote) { + var start = this.index; + var dateParseLength = this.dateParseLength; this.index++; - } - var fn = 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}); -}; -Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; -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 = Lexer.ESCAPE[ch]; - if (rep) { - string += rep; + 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 { - string += ch; + var rep = 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; } - 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; } + throw "Lexer Error: Unterminated quote [" + + this.text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + this.text + "'."; + }, + + readRegexp: function(quote) { + var start = this.index; this.index++; - } - throw "Lexer Error: Unterminated quote [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; -}; - -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 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; } - 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++; } - this.index++; + throw "Lexer Error: Unterminated RegExp [" + + this.text.substring(start) + "] starting at column '" + + (start+1) + "' in expression '" + this.text + "'."; } - throw "Lexer Error: Unterminated RegExp [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; }; +///////////////////////////////////////// -Parser = function(text, parseStrings){ +function Parser(text, parseStrings){ this.text = text; this.tokens = new Lexer(text, parseStrings).parse(); this.index = 0; @@ -243,499 +247,501 @@ Parser.ZERO = function(){ return 0; }; -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) + "'."; -}; - -Parser.prototype.peekToken = function() { - if (this.tokens.length === 0) - throw "Unexpected end of expression: " + this.text; - return this.tokens[0]; -}; - -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)) { +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) + "'."; + }, + + peekToken: function() { + if (this.tokens.length === 0) + throw "Unexpected end of expression: " + this.text; + return this.tokens[0]; + }, + + 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; + }, + + 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; -}; - -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; -}; - -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) + "'."; - } -}; - -Parser.prototype._unary = function(fn, parse) { - var right = parse.apply(this); - return function(self) { - return fn(self, right(self)); - }; -}; - -Parser.prototype._binary = function(left, fn, parse) { - var right = parse.apply(this); - return function(self) { - return fn(self, left(self), right(self)); - }; -}; - -Parser.prototype.hasTokens = function () { - return this.tokens.length > 0; -}; - -Parser.prototype.assertAllConsumed = function(){ - if (this.tokens.length !== 0) { - throw "Did not understand '" + this.text.substring(this.tokens[0].index) + - "' while evaluating '" + this.text + "'."; - } -}; - -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; - }; + return false; + }, + + 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) + "'."; } - } -}; - -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; + }, + + _unary: function(fn, parse) { + var right = parse.apply(this); + return function(self) { + return fn(self, right(self)); + }; + }, + + _binary: function(left, fn, parse) { + var right = parse.apply(this); + return function(self) { + return fn(self, left(self), right(self)); + }; + }, + + hasTokens: function () { + return this.tokens.length > 0; + }, + + assertAllConsumed: function(){ + if (this.tokens.length !== 0) { + throw "Did not understand '" + this.text.substring(this.tokens[0].index) + + "' while evaluating '" + this.text + "'."; } - } -}; - -Parser.prototype.filter = function(){ - return this._pipeFunction(angular['filter']); -}; - -Parser.prototype.validator = function(){ - return this._pipeFunction(angular['validator']); -}; - -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; - }; + }, + + 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; + }; + } } - } -}; - -Parser.prototype.expression = function(){ - return this.throwStmt(); -}; - -Parser.prototype.throwStmt = function(){ - if (this.expect('throw')) { - var throwExp = this.assignment(); - return function (self) { - throw throwExp(self); - }; - } else { - return this.assignment(); - } -}; - -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; - } -}; - -Parser.prototype.logicalOR = function(){ - var left = this.logicalAND(); - var token; - while(true) { - if ((token = this.expect('||'))) { - left = this._binary(left, token.fn, this.logicalAND); + }, + + 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; + } + } + }, + + filter: function(){ + return this._pipeFunction(angular['filter']); + }, + + validator: function(){ + return this._pipeFunction(angular['validator']); + }, + + _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; + }; + } + } + }, + + expression: function(){ + return this.throwStmt(); + }, + + throwStmt: function(){ + if (this.expect('throw')) { + var throwExp = this.assignment(); + return function (self) { + throw throwExp(self); + }; } else { - return left; + return this.assignment(); } - } -}; - -Parser.prototype.logicalAND = function(){ - var left = this.negated(); - var token; - while(true) { - if ((token = this.expect('&&'))) { - left = this._binary(left, token.fn, this.negated); + }, + + 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; + return left; } - } -}; - -Parser.prototype.negated = function(){ - var token; - if (token = this.expect('!')) { - return this._unary(token.fn, this.equality); - } else { - return this.equality(); - } -}; - -Parser.prototype.equality = function(){ - var left = this.relational(); - var token; - while(true) { - if ((token = this.expect('==','!='))) { - left = this._binary(left, token.fn, this.relational); + }, + + 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; + } + } + }, + + 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; + } + } + }, + + negated: function(){ + var token; + if (token = this.expect('!')) { + return this._unary(token.fn, this.equality); } else { - return left; + return this.equality(); } - } -}; - -Parser.prototype.relational = function(){ - var left = this.additive(); - var token; - while(true) { - if ((token = this.expect('<', '>', '<=', '>='))) { - left = this._binary(left, token.fn, this.additive); + }, + + 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; + } + } + }, + + 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; + } + } + }, + + additive: function(){ + var left = this.multiplicative(); + var token; + while(token = this.expect('+','-')) { + left = this._binary(left, token.fn, this.multiplicative); + } + return left; + }, + + multiplicative: function(){ + var left = this.unary(); + var token; + while(token = this.expect('*','/','%')) { + left = this._binary(left, token.fn, this.unary); + } + return left; + }, + + unary: function(){ + var token; + if (this.expect('+')) { + return this.primary(); + } else if (token = this.expect('-')) { + return this._binary(Parser.ZERO, token.fn, this.multiplicative); } else { - return left; + return this.primary(); } - } -}; - -Parser.prototype.additive = function(){ - var left = this.multiplicative(); - var token; - while(token = this.expect('+','-')) { - left = this._binary(left, token.fn, this.multiplicative); - } - return left; -}; - -Parser.prototype.multiplicative = function(){ - var left = this.unary(); - var token; - while(token = this.expect('*','/','%')) { - left = this._binary(left, token.fn, this.unary); - } - return left; -}; - -Parser.prototype.unary = function(){ - var token; - if (this.expect('+')) { - return this.primary(); - } else if (token = this.expect('-')) { - return this._binary(Parser.ZERO, token.fn, this.multiplicative); - } else { - return this.primary(); - } -}; - -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; -}; - -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 { + }, + + functionIdent: function(fnScope) { var token = this.expect(); - primary = token.fn; - if (!primary) { - this.error("not a primary expression", token); + 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]; } - } - 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); + if (typeof instance != 'function') { + throw "Function '" + token.text + "' at column '" + + (token.index+1) + "' in '" + this.text + "' is not defined."; + } + return instance; + }, + + 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 { - throw "IMPOSSIBLE"; + var token = this.expect(); + primary = token.fn; + if (!primary) { + this.error("not a primary expression", token); + } } - } - return primary; -}; - -Parser.prototype.closure = function(hasArgs) { - var args = []; - if (hasArgs) { - if (!this.expect(')')) { - args.push(this.expect().text); - while(this.expect(',')) { + 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; + }, + + 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(')'); + this.consume(":"); } - this.consume(":"); - } - var statements = this.statements(); - this.consume("}"); - return function(self){ - return function($){ - var scope = new Scope(self.scope.state); - scope.set('$', $); - for ( var i = 0; i < args.length; i++) { - scope.set(args[i], arguments[i]); + var statements = this.statements(); + this.consume("}"); + return function(self){ + return function($){ + var scope = new Scope(self.scope.state); + scope.set('$', $); + for ( var i = 0; i < args.length; i++) { + scope.set(args[i], arguments[i]); + } + return statements({scope:scope}); + }; + }; + }, + + fieldAccess: function(object) { + var field = this.expect().text; + var fn = function (self){ + return Scope.getter(object(self), field); + }; + fn.isAssignable = field; + return fn; + }, + + 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; + }; + } + }, + + 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."; } - return statements({scope:scope}); }; - }; -}; - -Parser.prototype.fieldAccess = function(object) { - var field = this.expect().text; - var fn = function (self){ - return Scope.getter(object(self), field); - }; - fn.isAssignable = field; - return fn; -}; - -Parser.prototype.objectIndex = function(obj) { - var indexFn = this.expression(); - this.consume(']'); - if (this.expect('=')) { - var rhs = this.expression(); + }, + + // This is used with json array declaration + arrayDeclaration: function () { + var elementFns = []; + if (this.peekToken().text != ']') { + do { + elementFns.push(this.expression()); + } while (this.expect(',')); + } + this.consume(']'); return function (self){ - return obj(self)[indexFn(self)] = rhs(self); + var array = []; + for ( var i = 0; i < elementFns.length; i++) { + array.push(elementFns[i](self)); + } + return array; }; - } else { + }, + + 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 o = obj(self); - var i = indexFn(self); - return (o) ? o[i] : undefined; + 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; }; - } -}; - -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)); + }, + + entityDeclaration: function () { + var decl = []; + while(this.hasTokens()) { + decl.push(this.entityDecl()); + if (!this.expect(';')) { + this.assertAllConsumed(); + } } - var fnPtr = fn(self); - if (typeof fnPtr === 'function') { - return fnPtr.apply(self, args); - } else { - throw "Expression '" + fn.isAssignable + "' is not a function."; + return function (self){ + var code = ""; + for ( var i = 0; i < decl.length; i++) { + code += decl[i](self); + } + return code; + }; + }, + + entityDecl: function () { + var entity = this.expect().text; + var instance; + var defaults; + if (this.expect('=')) { + instance = entity; + entity = this.expect().text; } - }; -}; - -// This is used with json array declaration -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; - }; -}; - -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; - }; -}; - -Parser.prototype.entityDeclaration = function () { - var decl = []; - while(this.hasTokens()) { - decl.push(this.entityDecl()); - if (!this.expect(';')) { - this.assertAllConsumed(); + if (this.expect(':')) { + defaults = this.primary()(null); } - } - return function (self){ - var code = ""; - for ( var i = 0; i < decl.length; i++) { - code += decl[i](self); + 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 ""; + } + }; + }, + + watch: function () { + var decl = []; + while(this.hasTokens()) { + decl.push(this.watchDecl()); + if (!this.expect(';')) { + this.assertAllConsumed(); + } } - return code; - }; -}; - -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) + ";" + - "};"; + this.assertAllConsumed(); + return function (self){ + for ( var i = 0; i < decl.length; i++) { + var d = decl[i](self); + self.addListener(d.name, d.fn); + } + }; + }, + + watchDecl: function () { + var anchorName = this.expect().text; + this.consume(":"); + var expression; + if (this.peekToken().text == '{') { + this.consume("{"); + expression = this.statements(); + this.consume("}"); } else { - return ""; - } - }; -}; - -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); + expression = this.expression(); } - }; -}; - -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}; + }; } - return function(self) { - return {name:anchorName, fn:expression}; - }; }; diff --git a/src/Scope.js b/src/Scope.js index f4b34c3c..dcc50007 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -1,6 +1,4 @@ -// Copyright (C) 2009 BRAT Tech LLC - -Scope = function(initialState, name) { +function Scope(initialState, name) { this.widgets = []; this.watchListeners = {}; this.name = name; @@ -15,31 +13,6 @@ Scope = function(initialState, name) { }; Scope.expressionCache = {}; - -Scope.prototype.updateView = function() { - var self = this; - this.fireWatchers(); - _.each(this.widgets, function(widget){ - self.evalWidget(widget, "", {}, function(){ - this.updateView(self); - }); - }); -}; - -Scope.prototype.addWidget = function(controller) { - if (controller) this.widgets.push(controller); -}; - -Scope.prototype.isProperty = function(exp) { - for ( var i = 0; i < exp.length; i++) { - var ch = exp.charAt(i); - if (ch!='.' && !Lexer.prototype.isIdent(ch)) { - return false; - } - } - return true; -}; - Scope.getter = function(instance, path) { if (!path) return instance; var element = path.split('.'); @@ -70,129 +43,155 @@ Scope.getter = function(instance, path) { return instance; }; -Scope.prototype.get = function(path) { - return Scope.getter(this.state, path); -}; - -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; +Scope.prototype = { + updateView: function() { + var self = this; + this.fireWatchers(); + _.each(this.widgets, function(widget){ + self.evalWidget(widget, "", {}, function(){ + this.updateView(self); + }); + }); + }, + + addWidget: function(controller) { + if (controller) this.widgets.push(controller); + }, + + isProperty: function(exp) { + for ( var i = 0; i < exp.length; i++) { + var ch = exp.charAt(i); + if (ch!='.' && !Lexer.prototype.isIdent(ch)) { + return false; + } } - instance = newInstance; - } - instance[element.shift()] = value; - return value; -}; - -Scope.prototype.setEval = function(expressionText, value) { - this.eval(expressionText + "=" + toJson(value)); -}; - -Scope.prototype.eval = function(expressionText, context) { - var expression = Scope.expressionCache[expressionText]; - if (!expression) { - var parser = new Parser(expressionText); - expression = parser.statements(); - parser.assertAllConsumed(); - 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. -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'); + return true; + }, + + get: function(path) { + return Scope.getter(this.state, path); + }, + + 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; } - if (onSuccess) { - value = onSuccess.apply(widget, [value]); + instance[element.shift()] = value; + return value; + }, + + setEval: function(expressionText, value) { + this.eval(expressionText + "=" + toJson(value)); + }, + + eval: function(expressionText, context) { + var expression = Scope.expressionCache[expressionText]; + if (!expression) { + var parser = new Parser(expressionText); + expression = parser.statements(); + parser.assertAllConsumed(); + Scope.expressionCache[expressionText] = expression; } - return true; - } catch (e){ - error('Eval Widget Error:', e); - var jsonError = toJson(e, true); - widget.hasError = true; - jQuery(widget.view). - addClass('ng-exception'). - attr('ng-error', jsonError); - if (onFailure) { - onFailure.apply(widget, [e, jsonError]); + 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. + 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){ + error('Eval Widget Error:', e); + var jsonError = 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; } - return false; - } -}; - -Scope.prototype.validate = function(expressionText, value) { - var expression = Scope.expressionCache[expressionText]; - if (!expression) { - expression = new Parser(expressionText).validator(); - Scope.expressionCache[expressionText] = expression; - } - var self = {scope:this}; - return expression(self)(self, value); -}; - -Scope.prototype.entity = function(entityDeclaration) { - var expression = new Parser(entityDeclaration).entityDeclaration(); - return expression({scope:this}); -}; - -Scope.prototype.markInvalid = function(widget) { - this.state.$invalidWidgets.push(widget); -}; - -Scope.prototype.watch = function(declaration) { - var self = this; - new Parser(declaration).watch()({ - scope:this, - addListener:function(watch, exp){ - self.addWatchListener(watch, function(n,o){ - try { - return exp({scope:self}, n, o); - } catch(e) { - alert(e); - } - }); + }, + + validate: function(expressionText, value) { + var expression = Scope.expressionCache[expressionText]; + if (!expression) { + expression = new Parser(expressionText).validator(); + Scope.expressionCache[expressionText] = expression; } - }); -}; - -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); -}; - -Scope.prototype.fireWatchers = function() { - var self = this; - var fired = false; - foreach(this.watchListeners, function(watcher) { - var value = self.eval(watcher.expression); - if (value !== watcher.lastValue) { - foreach(watcher.listeners, function(listener){ - listener(value, watcher.lastValue); - fired = true; - }); - watcher.lastValue = value; + var self = {scope:this}; + return expression(self)(self, value); + }, + + entity: function(entityDeclaration) { + var expression = new Parser(entityDeclaration).entityDeclaration(); + return expression({scope:this}); + }, + + markInvalid: function(widget) { + this.state.$invalidWidgets.push(widget); + }, + + watch: function(declaration) { + var self = this; + new Parser(declaration).watch()({ + scope:this, + addListener:function(watch, exp){ + self.addWatchListener(watch, function(n,o){ + try { + return exp({scope:self}, n, o); + } catch(e) { + alert(e); + } + }); + } + }); + }, + + addWatchListener: function(watchExpression, listener) { + var watcher = this.watchListeners[watchExpression]; + if (!watcher) { + watcher = {listeners:[], expression:watchExpression}; + this.watchListeners[watchExpression] = watcher; } - }); - return fired; -}; + watcher.listeners.push(listener); + }, + + fireWatchers: function() { + var self = this; + var fired = false; + foreach(this.watchListeners, function(watcher) { + var value = self.eval(watcher.expression); + if (value !== watcher.lastValue) { + foreach(watcher.listeners, function(listener){ + listener(value, watcher.lastValue); + fired = true; + }); + watcher.lastValue = value; + } + }); + return fired; + } +};
\ No newline at end of file diff --git a/src/Server.js b/src/Server.js index 8f682038..f351e84c 100644 --- a/src/Server.js +++ b/src/Server.js @@ -1,6 +1,4 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - -Server = function(url, getScript) { +function Server(url, getScript) { this.url = url; this.nextId = 0; this.getScript = getScript; @@ -8,27 +6,29 @@ Server = function(url, getScript) { this.maxSize = 1800; }; -Server.prototype.base64url = function(txt) { - return Base64.encode(txt); -}; - -Server.prototype.request = function(method, url, request, callback) { - var requestId = this.uuid + (this.nextId++); - angularCallbacks[requestId] = function(response) { - delete angular[requestId]; - callback(200, response); - }; - var payload = {u:url, m:method, p:request}; - payload = this.base64url(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, noop); +Server.prototype = { + base64url: function(txt) { + return Base64.encode(txt); + }, + + request: function(method, url, request, callback) { + var requestId = this.uuid + (this.nextId++); + angularCallbacks[requestId] = function(response) { + delete angular[requestId]; + callback(200, response); + }; + var payload = {u:url, m:method, p:request}; + payload = this.base64url(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, noop); + } } }; -FrameServer = function(frame) { +function FrameServer(frame) { this.frame = frame; }; FrameServer.PREFIX = "$DATASET:"; @@ -46,7 +46,7 @@ FrameServer.prototype = { }; -VisualServer = function(delegate, status, update) { +function VisualServer(delegate, status, update) { this.delegate = delegate; this.update = update; this.status = status; diff --git a/src/Users.js b/src/Users.js index d10b96df..47da4f73 100644 --- a/src/Users.js +++ b/src/Users.js @@ -1,11 +1,10 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC -Users = function(server, controlBar) { +function Users(server, controlBar) { this.server = server; this.controlBar = controlBar; }; Users.prototype = { - fetchCurrentUser:function(callback) { + 'fetchCurrentUser':function(callback) { var self = this; this.server.request("GET", "/account.json", {}, function(code, response){ self.current = response.user; @@ -13,7 +12,7 @@ Users.prototype = { }); }, - logout: function(callback) { + 'logout': function(callback) { var self = this; this.controlBar.logout(function(){ delete self.current; @@ -21,7 +20,7 @@ Users.prototype = { }); }, - login: function(callback) { + 'login': function(callback) { var self = this; this.controlBar.login(function(){ self.fetchCurrentUser(function(){ @@ -30,7 +29,7 @@ Users.prototype = { }); }, - notAuthorized: function(){ + 'notAuthorized': function(){ this.controlBar.notAuthorized(); } }; diff --git a/src/Validators.js b/src/Validators.js index eb8cff59..5549ee39 100644 --- a/src/Validators.js +++ b/src/Validators.js @@ -1,5 +1,3 @@ -// Copyright (C) 2009 BRAT Tech LLC - foreach({ 'regexp': function(value, regexp, msg) { if (!value.match(regexp)) { diff --git a/src/Widgets.js b/src/Widgets.js index 5dcb84c4..f93f2476 100644 --- a/src/Widgets.js +++ b/src/Widgets.js @@ -1,7 +1,4 @@ -// Copyright (C) 2009 BRAT Tech LLC - - -WidgetFactory = function(serverUrl, database) { +function WidgetFactory(serverUrl, database) { this.nextUploadId = 0; this.serverUrl = serverUrl; this.database = database; @@ -15,80 +12,81 @@ WidgetFactory = function(serverUrl, database) { this.onChangeListener = function(){}; }; -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 ButtonController(input[0], exp); - event = "click"; - bubbleEvent = false; - } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { - controller = new TextController(input[0], exp); - event = "keyup change"; - } else if (type == 'checkbox') { - controller = new CheckboxController(input[0], exp); - event = "click"; - } else if (type == 'radio') { - controller = new RadioController(input[0], exp); - event="click"; - } else if (type == 'select-one') { - controller = new SelectController(input[0], exp); - } else if (type == 'select-multiple') { - controller = new 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); - } +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 ButtonController(input[0], exp); + event = "click"; + bubbleEvent = false; + } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { + controller = new TextController(input[0], exp); + event = "keyup change"; + } else if (type == 'checkbox') { + controller = new CheckboxController(input[0], exp); + event = "click"; + } else if (type == 'radio') { + controller = new RadioController(input[0], exp); + event="click"; + } else if (type == 'select-one') { + controller = new SelectController(input[0], exp); + } else if (type == 'select-multiple') { + controller = new MultiSelectController(input[0], exp); + } else if (type == 'file') { + controller = this.createFileController(input, exp); + } else { + throw 'Unknown type: ' + type; } - return bubbleEvent; - }; - jQuery(controller.view, ":input"). - bind(event, action); - return controller; -}; - -WidgetFactory.prototype.createFileController = function(fileInput) { - var uploadId = '__uploadWidget_' + (this.nextUploadId++); - var view = 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 FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database); - jQuery(swfNode).data('controller', cntl); - return cntl; -}; - -WidgetFactory.prototype.createTextWidget = function(textInput) { - var controller = new TextController(textInput); - controller.onChange(this.onChangeListener); - return controller; + 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; + }, + + createFileController: function(fileInput) { + var uploadId = '__uploadWidget_' + (this.nextUploadId++); + var view = 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 FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database); + jQuery(swfNode).data('controller', cntl); + return cntl; + }, + + createTextWidget: function(textInput) { + var controller = new TextController(textInput); + controller.onChange(this.onChangeListener); + return controller; + } }; - ///////////////////// // FileController /////////////////////// -FileController = function(view, scopeName, uploader, databaseUrl) { +function FileController(view, scopeName, uploader, databaseUrl) { this.view = view; this.uploader = uploader; this.scopeName = scopeName; @@ -112,99 +110,89 @@ FileController.template = function(id) { '</span>'); }; -FileController.prototype._on_cancel = function() { -}; - -FileController.prototype._on_complete = function() { -}; - -FileController.prototype._on_httpStatus = function(status) { - alert("httpStatus:" + this.scopeName + " status:" + status); -}; - -FileController.prototype._on_ioError = function() { - alert("ioError:" + this.scopeName); -}; - -FileController.prototype._on_open = function() { - alert("open:" + this.scopeName); -}; - -FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) { -}; - -FileController.prototype._on_securityError = function() { - alert("securityError:" + this.scopeName); -}; - -FileController.prototype._on_uploadCompleteData = function(data) { - var value = 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(); -}; - -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(); -}; - -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; - } -}; - -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); -}; - -FileController.prototype.upload = function() { - if (this.name) { - this.uploader.uploadFile(this.attachmentsPath); +FileController.prototype = { + '_on_cancel': noop, + '_on_complete': noop, + '_on_httpStatus': function(status) { + alert("httpStatus:" + this.scopeName + " status:" + status); + }, + '_on_ioError': function() { + alert("ioError:" + this.scopeName); + }, + '_on_open': function() { + alert("open:" + this.scopeName); + }, + '_on_progress':noop, + '_on_securityError': function() { + alert("securityError:" + this.scopeName); + }, + '_on_uploadCompleteData': function(data) { + var value = 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(); + }, + '_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(); + }, + + 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; + } + }, + + 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); + }, + + upload: function() { + if (this.name) { + this.uploader.uploadFile(this.attachmentsPath); + } } }; - /////////////////////// // NullController /////////////////////// -NullController = function(view) {this.view = view;}; -NullController.prototype.updateModel = function() { return true; }; -NullController.prototype.updateView = function() { }; +function NullController(view) {this.view = view;}; +NullController.prototype = { + updateModel: function() { return true; }, + updateView: noop +}; NullController.instance = new NullController(); /////////////////////// // ButtonController /////////////////////// -ButtonController = function(view) {this.view = view;}; -ButtonController.prototype.updateModel = function(scope) { return true; }; -ButtonController.prototype.updateView = function(scope) {}; +var ButtonController = NullController; /////////////////////// // TextController /////////////////////// -TextController = function(view, exp) { +function TextController(view, exp) { this.view = view; this.exp = exp; this.validator = view.getAttribute('ng-validate'); @@ -218,175 +206,183 @@ TextController = function(view, exp) { } }; -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; - } -}; - -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); +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; + } + }, + + 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); } - jQuery(view).toggleClass('ng-validation-error', isValidationError); } }; /////////////////////// // CheckboxController /////////////////////// -CheckboxController = function(view, exp) { +function CheckboxController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = view.checked ? view.value : ""; }; -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; - } -}; - -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); +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; + } + }, + + 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); } - input.checked = input.value == (''+value); }; /////////////////////// // SelectController /////////////////////// -SelectController = function(view, exp) { +function SelectController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = view.value; }; -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; +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; + } + } + }, + + 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; - return true; } } }; -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 /////////////////////// -MultiSelectController = function(view, exp) { +function MultiSelectController(view, exp) { this.view = view; this.exp = exp; this.lastValue = undefined; this.initialValue = this.selected(); }; -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; -}; - -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; - } -}; - -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; +MultiSelectController.prototype = { + selected: function () { + var value = []; + var options = this.view.options; for ( var i = 0; i < options.length; i++) { var option = options[i]; - option.selected = _.include(selected, option.value); + if (option.selected) { + value.push(option.value); + } + } + return value; + }, + + 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; + } + }, + + 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; } - this.lastValue = selected; } }; /////////////////////// // RadioController /////////////////////// -RadioController = function(view, exp) { +function RadioController(view, exp) { this.view = view; this.exp = exp; this.lastChecked = undefined; @@ -395,35 +391,37 @@ RadioController = function(view, exp) { this.initialValue = view.checked ? view.value : null; }; -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; - } -}; - -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; +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; + } + }, + + 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 /////////////////////// -BindUpdater = function(view, exp) { +function BindUpdater(view, exp) { this.view = view; this.exp = Binder.parseBindings(exp); this.hasError = false; @@ -473,152 +471,170 @@ BindUpdater.toText = function(obj) { } }; -BindUpdater.prototype.updateModel = function(scope) {}; -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 = Binder.binding(part); - if (binding) { - scope.evalWidget(this, binding, this.scopeSelf, function(value){ - html.push(BindUpdater.toText(value)); - }, function(e, text){ - setHtml(this.view, text); - }); - if (this.hasError) { - return; +BindUpdater.prototype = { + updateModel: noop, + 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 = Binder.binding(part); + if (binding) { + scope.evalWidget(this, binding, this.scopeSelf, function(value){ + html.push(BindUpdater.toText(value)); + }, function(e, text){ + setHtml(this.view, text); + }); + if (this.hasError) { + return; + } + } else { + html.push(escapeHtml(part)); } - } else { - html.push(escapeHtml(part)); } + setHtml(this.view, html.join('')); } - setHtml(this.view, html.join('')); }; -BindAttrUpdater = function(view, attrs) { +function BindAttrUpdater(view, attrs) { this.view = view; this.attrs = attrs; }; -BindAttrUpdater.prototype.updateModel = function(scope) {}; -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 = Binder.parseBindings(attributeTemplates[attrName]); - var attrValues = []; - for ( var i = 0; i < attributeTemplate.length; i++) { - var binding = Binder.binding(attributeTemplate[i]); - if (binding) { - try { - var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); - if (value && (value.constructor !== array || value.length !== 0)) - attrValues.push(value); - } catch (e) { - this.hasError = true; - error('BindAttrUpdater', e); - var jsonError = toJson(e, true); - attrValues.push('[' + jsonError + ']'); - jNode. - addClass('ng-exception'). - attr('ng-error', jsonError); +BindAttrUpdater.prototype = { + updateModel: noop, + 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 = Binder.parseBindings(attributeTemplates[attrName]); + var attrValues = []; + for ( var i = 0; i < attributeTemplate.length; i++) { + var binding = Binder.binding(attributeTemplate[i]); + if (binding) { + try { + var value = scope.eval(binding, {element:jNode[0], attrName:attrName}); + if (value && (value.constructor !== array || value.length !== 0)) + attrValues.push(value); + } catch (e) { + this.hasError = true; + error('BindAttrUpdater', e); + var jsonError = toJson(e, true); + attrValues.push('[' + jsonError + ']'); + jNode. + addClass('ng-exception'). + attr('ng-error', jsonError); + } + } else { + attrValues.push(attributeTemplate[i]); } - } 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); + var attrValue = attrValues.length ? attrValues.join('') : null; + if(isImage && attrName == 'src' && !attrValue) + attrValue = scope.get('config.server') + '/images/blank.gif'; + jNode.attr(attrName, attrValue); + } } }; -EvalUpdater = function(view, exp) { +function EvalUpdater(view, exp) { this.view = view; this.exp = exp; this.hasError = false; }; -EvalUpdater.prototype.updateModel = function(scope) {}; -EvalUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp); +EvalUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp); + } }; -HideUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -HideUpdater.prototype.updateModel = function(scope) {}; -HideUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (toBoolean(hideValue)) { - view.hide(); - } else { - view.show(); - } - }); +function HideUpdater(view, exp) { this.view = view; this.exp = exp; }; +HideUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(hideValue){ + var view = jQuery(this.view); + if (toBoolean(hideValue)) { + view.hide(); + } else { + view.show(); + } + }); + } }; -ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ShowUpdater.prototype.updateModel = function(scope) {}; -ShowUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(hideValue){ - var view = jQuery(this.view); - if (toBoolean(hideValue)) { - view.show(); - } else { - view.hide(); - } - }); +function ShowUpdater(view, exp) { this.view = view; this.exp = exp; }; +ShowUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(hideValue){ + var view = jQuery(this.view); + if (toBoolean(hideValue)) { + view.show(); + } else { + view.hide(); + } + }); + } }; -ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ClassUpdater.prototype.updateModel = function(scope) {}; -ClassUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(classValue){ - if (classValue !== null && classValue !== undefined) { - this.view.className = classValue; - } - }); +function ClassUpdater(view, exp) { this.view = view; this.exp = exp; }; +ClassUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(classValue){ + if (classValue !== null && classValue !== undefined) { + this.view.className = classValue; + } + }); + } }; -ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ClassEvenUpdater.prototype.updateModel = function(scope) {}; -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); - }); +function ClassEvenUpdater(view, exp) { this.view = view; this.exp = exp; }; +ClassEvenUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(classValue){ + var index = scope.get('$index'); + jQuery(this.view).toggleClass(classValue, index % 2 === 1); + }); + } }; -ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -ClassOddUpdater.prototype.updateModel = function(scope) {}; -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); - }); +function ClassOddUpdater(view, exp) { this.view = view; this.exp = exp; }; +ClassOddUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(classValue){ + var index = scope.get('$index'); + jQuery(this.view).toggleClass(classValue, index % 2 === 0); + }); + } }; -StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; }; -StyleUpdater.prototype.updateModel = function(scope) {}; -StyleUpdater.prototype.updateView = function(scope) { - scope.evalWidget(this, this.exp, {}, function(styleValue){ - jQuery(this.view).attr('style', "").css(styleValue); - }); +function StyleUpdater(view, exp) { this.view = view; this.exp = exp; }; +StyleUpdater.prototype = { + updateModel: noop, + updateView: function(scope) { + scope.evalWidget(this, this.exp, {}, function(styleValue){ + jQuery(this.view).attr('style', "").css(styleValue); + }); + } }; /////////////////////// // RepeaterUpdater /////////////////////// -RepeaterUpdater = function(view, repeaterExpression, template, prefix) { +function RepeaterUpdater(view, repeaterExpression, template, prefix) { this.view = view; this.template = template; this.prefix = prefix; @@ -639,81 +655,77 @@ RepeaterUpdater = function(view, repeaterExpression, template, prefix) { this.keyExp = match[2]; }; -RepeaterUpdater.prototype.updateModel = function(scope) {}; -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); +RepeaterUpdater.prototype = { + updateModel: noop, + 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; - foreach(iterator, function(value, key){ - 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 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); + 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; + foreach(iterator, function(value, key){ + 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 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().element[0]; + unneeded.parentNode.removeChild(unneeded); } - 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().element[0]; - unneeded.parentNode.removeChild(unneeded); - } - // 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); + // 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 ////////////////////////////////// -PopUp = function(doc) { +function PopUp(doc) { this.doc = doc; }; PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; -PopUp.prototype.bind = function () { - var self = this; - this.doc.find('.ng-validation-error,.ng-exception'). - live("mouseover", PopUp.onOver); -}; - PopUp.onOver = function(e) { PopUp.onOut(); var jNode = jQuery(this); @@ -753,28 +765,38 @@ PopUp.onOut = function() { return true; }; +PopUp.prototype = { + bind: function () { + var self = this; + this.doc.find('.ng-validation-error,.ng-exception'). + live("mouseover", PopUp.onOver); + } +}; + ////////////////////////////////// // Status ////////////////////////////////// -Status = function(body) { +function Status(body) { this.loader = body.append(Status.DOM).find("#ng-loading"); this.requestCount = 0; }; Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>'; -Status.prototype.beginRequest = function () { - if (this.requestCount === 0) { - this.loader.show(); - } - this.requestCount++; -}; - -Status.prototype.endRequest = function () { - this.requestCount--; - if (this.requestCount === 0) { - this.loader.hide("fold"); +Status.prototype = { + beginRequest: function () { + if (this.requestCount === 0) { + this.loader.show(); + } + this.requestCount++; + }, + + endRequest: function () { + this.requestCount--; + if (this.requestCount === 0) { + this.loader.hide("fold"); + } } }; diff --git a/src/angular-bootstrap.js b/src/angular-bootstrap.js index b13bbf34..50c78f81 100644 --- a/src/angular-bootstrap.js +++ b/src/angular-bootstrap.js @@ -1,5 +1,26 @@ -// Copyright (C) 2008,2009 BRAT Tech LLC - +/** + * The MIT License + * + * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ (function(previousOnLoad){ var filename = /(.*)\/angular-(.*).js(#(.*))?/; var scripts = document.getElementsByTagName("script"); diff --git a/src/angular.prefix b/src/angular.prefix index dbd4959a..26a8429f 100644 --- a/src/angular.prefix +++ b/src/angular.prefix @@ -1 +1,24 @@ +/** + * The MIT License + * + * Copyright (c) 2010 Adam Abrons and Misko Hevery http://getangular.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ (function(window, document){ diff --git a/test/DataStoreTest.js b/test/DataStoreTest.js index 2dd4a582..87c5be2e 100644 --- a/test/DataStoreTest.js +++ b/test/DataStoreTest.js @@ -17,9 +17,8 @@ DataStoreTest.prototype.testSavePostsToServer = function(){ response.$version = "2"; callback(200, [response]); }; - var model; var datastore = new DataStore(post); - model = datastore.entity('abc', {name: "value"})(); + var model = datastore.entity('abc', {name: "value"})(); model.$id = "123"; model.$version = "1"; |
