diff options
| -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";  | 
