diff options
| author | Misko Hevery | 2010-03-15 14:36:50 -0700 |
|---|---|---|
| committer | Misko Hevery | 2010-03-15 14:36:50 -0700 |
| commit | cc71b745c3c821f5e012a363ae3267252a81fddb (patch) | |
| tree | b86a76a131aa222b8bdf032480dc2ef0dca51a97 | |
| parent | bf838aab5d65a1019a4867b496e971c488589512 (diff) | |
| download | angular.js-cc71b745c3c821f5e012a363ae3267252a81fddb.tar.bz2 | |
added resources; removed compiled code
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | angular-minified.js | 104 | ||||
| -rw-r--r-- | angular.js | 4087 | ||||
| -rw-r--r-- | example/memoryLeak.html | 47 | ||||
| -rw-r--r-- | jsTestDriver.conf | 2 | ||||
| -rw-r--r-- | lib/jasmine/jasmine-0.10.1.js (renamed from lib/jasmine/jasmine-0.10.0.js) | 57 | ||||
| -rw-r--r-- | src/Angular.js | 54 | ||||
| -rw-r--r-- | src/Resource.js | 117 | ||||
| -rw-r--r-- | src/Scope.js | 32 | ||||
| -rw-r--r-- | test/AngularSpec.js | 36 | ||||
| -rw-r--r-- | test/ResourceSpec.js | 159 | ||||
| -rw-r--r-- | test/testabilityPatch.js | 7 |
12 files changed, 451 insertions, 4253 deletions
@@ -1,2 +1,4 @@ angular-minified.map externs.js +angular.js +angular-minified.js diff --git a/angular-minified.js b/angular-minified.js deleted file mode 100644 index 2056d81f..00000000 --- a/angular-minified.js +++ /dev/null @@ -1,104 +0,0 @@ -function G(){return function(){}}function L(p){return function(){return p}} -(function(p,x){function n(){}function M(a,b,c){var d=p.console;switch(arguments.length){case 1:d.log(a);break;case 2:d.log(a,b);break;default:d.log(a,b,c);break}}function aa(a,b,c){var d=p.console;switch(arguments.length){case 1:d.error(a);break;case 2:d.error(a,b);break;default:d.error(a,b,c);break}}function ia(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:v(f));d=" "}c.appendChild(x.createTextNode(a));Q.appendChild(c)}function ja(a){switch(a.nodeName){case "OPTION":case "PRE":case "TITLE":return true; -default:return false}}function ka(a,b){if(ja(a))if(V)a.innerText=b;else a.textContent=b;else a.innerHTML=b}function D(a){if(!a||!a.replace)return a;return a.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">")}function La(a){if(!a||!a.replace)return a;return a.replace(/</g,"<").replace(/>/g,">").replace(/\"/g,""")}function Ma(a,b){if(!a)throw"Missing this";if(!_.isFunction(b))throw"Missing function";return function(){return b.apply(a,arguments)}}function la(a){var b=x.createElement("div"); -b.appendChild(a);var c=b.innerHTML;b.removeChild(a);return c}function W(a){var b=(""+a).toLowerCase();if(b=="f"||b=="0"||b=="false"||b=="no")a=false;return!!a}function X(a,b){for(var c in a){var d=b[c],e=typeof d;if(e=="undefined")b[c]=N(v(a[c]));else e=="object"&&d.constructor!=array&&c.substring(0,1)!="$"&&X(a[c],d)}}function ma(a){this.location=a;this.delay=25;this.setTimeout=function(b,c){p.setTimeout(b,c)};this.Ka=function(b){return b};this.N=a.href}function Na(){var a=k.fn;a.scope=function(){for(var b= -this;b&&b.get(0);){var c=b.data("scope");if(c)return c;b=b.parent()}return null};a.controller=function(){return this.data("controller")||R.Kb}}function Oa(a){if(a.Gc=="console"&&!Q){Q=x.createElement("div");Q.id="ng-console";x.getElementsByTagName("body")[0].appendChild(Q);M=function(){ia("ng-console-info",arguments)};console.error=function(){ia("ng-console-error",arguments)}}}function ba(a,b){var c={};o(b,function(d,e){c[e]=_(d).bind(a)});return c}function Pa(a,b){var c=new na(b.server,b.database), -d=new t(a[0],c,h,b.location,b);d.sa.push(b.Nc);c=new S(a.find("body"),b.server,b.database);var e=b.database=="$MEMORY"?new ca(p):new oa(b.server,k.getScript);e=new pa(e,new qa(a.find("body")),function(){d.d()});var f=new ra(e,c),g="/data/"+b.database,h=new Y(function(l,q){e.P("POST",g,l,q)},f,d.anchor);d.l=h;d.sa.push(function(){h.W()});var i=new u({$anchor:d.anchor,$updateView:_(d.d).bind(d),$config:b,$invalidWidgets:[],$console:p.console,$datastore:ba(h,{load:h.load,loadMany:h.ma,loadOrCreate:h.Na, -loadAll:h.Ma,save:h.save,remove:h.remove,flush:h.W,query:h.Va,entity:h.G,entities:h.entities,documentCountsByUser:h.Bb,userDocumentIdsByEntity:h.sc,join:h.join}),$save:function(l){h.mc(i.j,l,d.anchor)},$window:p,$uid:function(){return""+(new Date).getTime()},$users:f},"ROOT");a.data("scope",i);d.G(i);d.compile();c.bind();(new z(a)).bind();var j=_(ba(i,{set:i.i,get:i.get,eval:i.eval})).extend({init:function(){b.location.listen(_(d.gc).bind(d));d.Sa();d.Fb();d.d();return j},element:a[0],updateView:_(d.d).bind(d), -config:b});return j}function H(a,b,c){var d=_.last(b);o(c,function(e){d[e]=_[e]});m[a]=m[a]||{};o(b,function(e){B(m[a],e)})}function t(a,b,c,d,e){this.F=a;this.location=d;this.l=c;this.anchor={};this.xc=b;this.vb=e||{};this.sa=[]}function S(a,b,c){this.hb=a;this.fa=b;this.ka=c;this.lb=p;this.H=[]}function Y(a,b,c){this.post=a;this.$a=b;this.K=[];this.ha={$collections:this.K};this.anchor=c;this.S=[]}function T(a,b){return{format:a,parse:b||a}}function sa(a){return""+a}function v(a,b){var c=[];da(c, -a,b?"\n ":null,_([]));return c.join("")}function N(a){try{var b=new E(a,true),c=b.da();b.L();return c()}catch(d){aa("fromJson error: ",a,d);throw d;}}function da(a,b,c,d){if(typeof b=="object"){if(d.include(b)){a.push("RECURSION");return}d.push(b)}var e=typeof b;if(b===null)a.push("null");else if(e==="function")return;else if(e==="boolean")a.push(""+b);else if(e==="number")isNaN(b)?a.push("null"):a.push(""+b);else if(e==="string")return a.push(m.String.quoteUnicode(b));else if(e==="object")if(b instanceof -Array){a.push("[");var f=b.length;e=false;for(var g=0;g<f;g++){var h=b[g];e&&a.push(",");typeof h=="function"||typeof h=="undefined"?a.push("null"):da(a,h,c,d);e=true}a.push("]")}else if(b instanceof Date)a.push(m.String.quoteUnicode(m.Date.toString(b)));else{a.push("{");c&&a.push(c);e=false;g=c?c+" ":false;h=[];for(var i in b)i.indexOf("$$")!==0&&h.push(i);h.sort();for(i=0;i<h.length;i++){var j=h[i];try{f=b[j];if(typeof f!="function"){if(e){a.push(",");c&&a.push(c)}a.push(m.String.quote(j));a.push(":"); -da(a,f,g,d);e=true}}catch(l){}}a.push("}")}typeof b=="object"&&d.pop()}function I(a,b){this.$$entity=a;this.$loadFrom(b||{});this.$entity=a.title;this.$migrate()}function F(a,b){this.text=a;this.zb=b?20:-1;this.h=[];this.index=0}function E(a,b){this.text=a;this.h=(new F(a,b)).parse();this.index=0}function u(a,b){this.ab=[];this.ta={};this.name=b;a=a||{};function c(){}c.prototype=a;this.j=new c;this.j.$parent=a;if(b=="ROOT")this.j.$root=this.j}function oa(a,b){this.url=a;this.Tb=0;this.getScript=b; -this.tc="_"+(""+Math.random()).substr(2)+"_";this.oa=1800}function ca(a){this.frame=a}function pa(a,b,c){this.Ab=a;this.update=c;this.status=b}function ra(a,b){this.nc=a;this.ia=b}function na(a,b){this.Ub=0;this.fa=a;this.ka=b;this.yb=p.swfobject?p.swfobject.createSWF:function(){alert("ERROR: swfobject not loaded!")}}function U(a,b,c,d){this.view=a;this.rc=c;this.J=b;this.ya=d+"/_attachments";this.value=null;this.c=undefined}function R(a){this.view=a}function ta(a,b,c){this.view=a;this.A=c;this.exp= -b;this.Q=a.getAttribute("ng-validate");this.lc=typeof a.attributes["ng-required"]!="undefined";this.Ja=null;this.c=undefined;this.w=this.A.parse(a.value);a.getAttribute("ng-widget")==="datepicker"&&k(a).Fc()}function ua(a,b,c){this.view=a;this.exp=b;this.c=undefined;this.A=c;this.w=this.A.parse(a.checked?a.value:"")}function va(a,b){this.view=a;this.exp=b;this.c=undefined;this.w=a.value}function wa(a,b){this.view=a;this.exp=b;this.c=undefined;this.w=this.selected()}function xa(a,b){this.view=a;this.exp= -b;this.c=this.la=undefined;this.Ia=a.value;this.w=a.checked?a.value:null}function O(a,b){this.view=a;this.exp=t.ba(b);this.C=false}function ya(a,b){this.view=a;this.rb=b}function za(a,b){this.view=a;this.exp=b;this.C=false}function Aa(a,b){this.view=a;this.exp=b}function Ba(a,b){this.view=a;this.exp=b}function Ca(a,b){this.view=a;this.exp=b}function Da(a,b){this.view=a;this.exp=b}function Ea(a,b){this.view=a;this.exp=b}function Fa(a,b){this.view=a;this.exp=b}function Ga(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.Z=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.vc=a[3]||a[1];this.Ob=a[2]}function z(a){this.F=a}function qa(){}function ea(a){this.ea=0;this.body=a}var fa={B:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=", -Cb:function(a){var b="",c,d,e,f,g,h,i=0;for(a=fa.kb(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.B.charAt(f)+this.B.charAt(c)+this.B.charAt(g)+this.B.charAt(h)}return b},Hc: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.B.indexOf(a.charAt(h++));d=this.B.indexOf(a.charAt(h++));f=this.B.indexOf(a.charAt(h++));g=this.B.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=fa.jb(b)},kb: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},jb: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=G();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};p.console||(p.console={log:n,error:n});var Q,V,k=p.jQuery||p.$,o=_.each,B=_.extend,Ha=_.identity,m=p.angular||(p.angular={}),ga=m.validator||(m.validator={}),r=m.filter||(m.filter={}),ha=m.formatter||(m.formatter={}),P=m.callbacks||(m.callbacks={});m.alert||(m.alert=function(){M(arguments);p.alert.apply(p,arguments)});var Ia=Ia||function(a){return k(a).is(":visible")};ma.prototype={Pb:function(a){this.Ka= -a},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=P[c];delete P[c];try{(d||n)()}catch(e){alert(e)}}else{a.Ka(a.location.href);a.N=a.location.href}}a.setTimeout(b,a.delay)}b()},i:function(a){var b=this.location.href;b.match(/#/)||(b+="#");if(b!=a)this.location.href=a;this.Kc=a},get:function(){return p.location.href}};m.startUrlWatcher=function(){var a= -new ma(p.location);a.watch();return ba(a,{listen:a.Pb,set:a.i,get:a.get})};m.compile=function(a,b){k=p.jQuery;V=k.browser.msie;b=_({onUpdateView:n,server:"",location:{get:n,set:n,listen:n}}).extend(b||{});Oa(b);Na();return Pa(k(a),b)};var J={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}},Z={},Ja={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.zc.find(a,function(c){return c.bb==b},null)},filter:function(a,b){var c=[];c.tb=function(j){for(var l=0;l<c.length;l++)if(!c[l](j))return false; -return true};var d=u.O;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 q in j)if(q.charAt(0)!=="$"&&e(j[q],l))return true;return false;case "array":for(q=0;q<j.length;q++)if(e(j[q],l))return true;return false;default:return false}}switch(typeof b){case "boolean":case "number":case "string":b={yc: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(q){return e(d(q,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.tb(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 W(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}X(c,d);return a}},Qa={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}},Ka={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"}},Ra={compile:function(a){if(_.isFunction(a))return a;else if(a){var b=new u;return function(c){b.j=c;return b.eval(a)}}else return function(c){return c}}}; -H("Global",[J],["extend","clone","isEqual","isElement","isArray","isFunction","isUndefined"]);H("Collection",[J,Z],["each","map","reduce","reduceRight","detect","select","reject","all","any","include","invoke","pluck","max","min","sortBy","sortedIndex","toArray","size"]);H("Array",[J,Z,Ja],["first","last","compact","flatten","without","uniq","intersect","zip","indexOf","lastIndexOf"]);H("Object",[J,Z,{}],["keys","values"]);H("String",[J,Qa],[]);H("Date",[J,Ka],[]);m.Date.toString=Ka.toString;H("Function", -[J,Z,Ra],["bind","bindAll","delay","defer","wrap","compose"]);t.ba=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};t.Ga=function(a){a=t.ba(a);return a.length>1||t.M(a[0])!==null};t.M=function(a){return(a=a.replace(/\n/gm," ").match(/^\{\{(.*)\}\}$/))?a[1]:null};t.prototype={hc:function(a){var b={};a.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, -function(c,d,e){if(d)b[decodeURIComponent(d)]=decodeURIComponent(e)});return b},Sa:function(){var a=this,b=this.location.get()||"",c=b.indexOf("#");if(!(c<0)){b=this.hc(b.substring(c+1));o(a.anchor,function(d,e){delete a.anchor[e]});o(b,function(d,e){a.anchor[e]=d})}},gc:function(){this.Sa();this.d()},qc:function(){var a=this.location.get()||"",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.location.set(a);return a},d:function(){(new Date).getTime();var a=k(this.F).scope();a.ub();a.d();(new Date).getTime();this.qc();o(this.sa,function(b){b()})},U:function(a){var b=k(this.F),c=b.find(a);if(b.is(a))c=c.andSelf();return c},Fb: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"+v(c,true))}})},G:function(a){var b= -this;this.U("[ng-entity]").attr("ng-watch",function(){try{var c=k(this);return a.G(c.attr("ng-entity"),b.l)+(c.attr("ng-watch")||"")}catch(d){M(d);alert(d)}})},compile:function(){var a=k(this.F);if(this.vb.autoSubmit){var b=this.U(":submit").not("[ng-action]");b.attr("ng-action","$save()");b.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr",'{disabled:"{{$invalidWidgets}}"}')}this.Ta(this.F)(this.F,a.scope(),"");this.U("a[ng-action]").live("click",function(){var c=k(this),d=c.scope();try{d.eval(c.attr("ng-action")); -c.removeAttr("ng-error");c.removeClass("ng-exception")}catch(e){c.addClass("ng-exception");c.attr("ng-error",v(e,true))}d.get("$updateView")();return false})},pc:function(a,b,c){b=b.concat();var d=b.pop(),e=t.ba(a.nodeValue);if(e.length>1||t.M(e[0])){var f=a.parentNode;if(ja(f)){f.setAttribute("ng-bind-template",a.nodeValue);c.push({path:b,b:function(l){return new O(l,l.getAttribute("ng-bind-template"))}})}else for(var g=0;g<e.length;g++){var h=e[g],i=t.M(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.Ra})}else if(V&&h.charAt(0)==" "){j=x.createElement("span");j.innerHTML=" "+h.substring(1)}else j=x.createTextNode(h);f.insertBefore(j,a)}f.removeChild(a)}},Ta:function(a){var b=[];this.Ua(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.ob(h.b(i,d,e))}catch(q){alert(q)}}}},Ua:function(a,b,c){var d=a.nodeType;if(d==Node.TEXT_NODE)this.pc(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?N(e):{};for(var f=d.length,g=0;g<f;g++){var h=d[g],i=h.name;h=V&&i=="href"?decodeURI(a.getAttribute(i,2)):h.value;if(t.Ga(h))e[i]=h}d=v(e);d.length>2&&a.setAttribute("ng-bind-attr",d)}a.getAttribute||M(a);var j=a.getAttribute("ng-repeat");if(j){a.removeAttribute("ng-repeat");var l=this.Ta(a);d=x.createComment("ng-repeat: "+ -j);e=a.parentNode;e.insertBefore(d,a);e.removeChild(a);function q(w,A,y){var K=k(a).clone();K.css("display","");K.attr("ng-repeat-index",""+y);K.data("scope",w);l(K[0],w,A+y+":");return K}c.push({path:b,b:function(w,A,y){return new Ga(k(w),j,q,y)}})}else{a.getAttribute("ng-eval")&&c.push({path:b,b:this.Zb});a.getAttribute("ng-bind")&&c.push({path:b,b:this.Ra});a.getAttribute("ng-bind-attr")&&c.push({path:b,b:this.Vb});a.getAttribute("ng-hide")&&c.push({path:b,b:this.$b});a.getAttribute("ng-show")&& -c.push({path:b,b:this.ac});a.getAttribute("ng-class")&&c.push({path:b,b:this.Wb});a.getAttribute("ng-class-odd")&&c.push({path:b,b:this.Yb});a.getAttribute("ng-class-even")&&c.push({path:b,b:this.Xb});a.getAttribute("ng-style")&&c.push({path:b,b:this.bc});a.getAttribute("ng-watch")&&c.push({path:b,b:this.cc});d=a.nodeName;if(d=="INPUT"||d=="TEXTAREA"||d=="SELECT"||d=="BUTTON"){var C=this;c.push({path:b,b:function(w,A,y){w.name=y+w.name.split(":").pop();return C.xc.wb(k(w),A)}})}if(d=="OPTION")if(!k("<select/>").append(k(a).clone()).html().match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi))if(t.Ga(a.text))k(a).attr("ng-bind-attr", -m.Oc({value:a.text}));else a.value=a.text;d=a.childNodes;for(e=0;e<d.length;e++)this.Ua(d[e],b.concat(e),c)}}}},Zb:function(a){return new za(a,a.getAttribute("ng-eval"))},Ra:function(a){return new O(a,"{{"+a.getAttribute("ng-bind")+"}}")},Vb:function(a){return new ya(a,N(a.getAttribute("ng-bind-attr")))},$b:function(a){return new Aa(a,a.getAttribute("ng-hide"))},ac:function(a){return new Ba(a,a.getAttribute("ng-show"))},Wb:function(a){return new Ca(a,a.getAttribute("ng-class"))},Xb:function(a){return new Da(a, -a.getAttribute("ng-class-even"))},Yb:function(a){return new Ea(a,a.getAttribute("ng-class-odd"))},bc:function(a){return new Fa(a,a.getAttribute("ng-style"))},cc:function(a,b){b.watch(a.getAttribute("ng-watch"))}};S.Ac='<div><div class="ui-widget-overlay"></div><div id="ng-login" ng-non-bindable="true"><div class="ng-login-container"></div></div></div>';S.eb='<div ng-non-bindable="true" title="Permission Error:">Sorry, you do not have permission for this!</div>';S.prototype={bind:G(),Qb:function(a){this.H.push(a); -this.H.length==1&&this.Ba("/user_session/new.mini?database="+encodeURIComponent(this.ka)+"&return_url="+encodeURIComponent(this.Za()))},Rb:function(a){this.H.push(a);this.H.length==1&&this.Ba("/user_session/do_destroy.mini")},Za:function(){return this.lb.location.href.split("#")[0]},Ba:function(a){var b=this,c=(new Date).getTime(),d=this.Za()+"#$iframe_notify="+c,e=k('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+d+'" src="'+this.fa+a+'" width="500" height="330"/></div>');this.hb.append(e); -e.dialog({height:363,width:500,resizable:false,modal:true,title:'Authentication: <a href="http://www.getangular.com"><tt><angular/></tt></a>'});P["_iframe_notify_"+c]=function(){e.dialog("destroy");e.remove();o(b.H,function(f){f()});b.H=[]}},dc:function(){if(!this.Fa){this.Fa=k(S.eb);this.Fa.Ic({Cc:true,height:70,Mc:true})}}};Y.fb=B(G(),{all:function(){return[]},query:function(){return[]},load:function(){return{}},title:undefined});Y.prototype={T:function(a){if(!a.l===this)throw"Parameter must be an instance of Entity! "+ -v(a);var b=a.$entity+"/"+a.$id,c=this.ha[b];if(c)I.ja(a,c);else c=this.ha[b]=a;return c},load:function(a,b,c,d){if(b&&b!=="*"){var e=this;this.D(["GET",a.$entity+"/"+b],function(f){a.$loadFrom(f);a.$migrate();f=a.$$entity(a);e.T(f);(c||n)(a)},d)}return a},ma: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||n)(e)}))});return e},Na:function(a,b,c){return this.load(a,b,c,function(d){if(d.$status_code==404){a.$id=b;(c||n)(a)}else throw d;})},Ma:function(a, -b){var c=this,d=[];d.$$accept=function(e){return e.$entity==a.title};this.K.push(d);this.D(["GET",a.title],function(e){for(var f=0;f<e.length;f++){var g=a();g.$loadFrom(e[f]);d.push(c.T(g))}(b||n)(d)});return d},save:function(a,b){var c=this,d={};a.$saveTo(d);this.D(["POST","",d],function(e){a.$loadFrom(e);var f=c.T(a);_.each(c.K,function(g){g.$$accept(a)&&Ja.includeIf(g,f,true)});if(a.$$anchor)c.anchor[a.$$anchor]=a.$id;b&&b(a)})},remove:function(a,b){var c=this,d={};a.$saveTo(d);this.D(["DELETE", -"",d],function(e){delete c.ha[a.$entity+"/"+a.$id];_.each(c.K,function(f){for(var g=0;g<f.length;g++)f[g].$id==a.$id&&f.splice(g,1)});(b||n)(e)})},D:function(a,b,c){a.$$callback=b;a.$$failure=c||function(d){throw d;};this.S.push(a)},W:function(){function a(d,e){M("RESPONSE["+d+"]: ",e);if(e.$status_code==401)b.$a.login(function(){b.post(c,a)});else if(e.$status_code)alert(v(e));else for(d=0;d<e.length;d++){var f=e[d],g=c[d],h=f.$status_code;if(h)h==403?b.$a.notAuthorized():g.$$failure(f);else g.$$callback(f)}} -if(this.S.length!==0){var b=this,c=this.S;this.S=[];M("REQUEST:",c);this.post(c,a)}},mc: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.$save==I.prototype.$save){d++;f.$save(c)}}c()},Va:function(a,b,c,d){var e=this,f=[];f.$$accept=L(false);this.K.push(f);this.D(["GET",a.title+"/"+b+"="+c],function(g){o(g,function(h){h=a().$loadFrom(h);f.push(e.T(h))});(d||n)(f)});return f},entities:function(a){var b=[],c=this;this.D(["GET","$entities"],function(d){o(d,function(e, -f){b.push(c.G(f))});b.sort(function(e,f){return e.title>f.title?1:-1});(a||n)(b)});return b},Bb:function(){var a={};this.post([["GET","$users"]],function(b,c){B(a,c[0])});return a},sc:function(a){var b={};this.post([["GET","$users/"+a]],function(c,d){B(b,d[0])});return b},G:function(a,b){if(!a)return Y.fb;var c=this,d=B(function(e){return new I(d,e)},{title:a,$$factory:true,l:this,defaults:b||{},load:function(e,f){return c.load(d(),e,f)},loadMany:function(e,f){return c.ma(d,e,f)},loadOrCreate:function(e, -f){return c.Na(d(),e,f)},all:function(e){return c.Ma(d,e)},query:function(e,f,g){return c.Va(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).Dc().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].aa? -a[d].aa.substring(0,a[d].aa.indexOf(".")):undefined}while(d);return e.length}).value();if(_(c).select(function(d){return a[d].aa}).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],q=l.aa,C={};_(i).each(function(w){var A={};f.push(A); -A[g]=w;w=u.O(A,q);C[w]=w});l.join.ma(_.toArray(C),function(w){var A={};_(w).each(function(y){A[y.bb]=y});_(f).each(function(y){var K=u.O(y,q);y[j]=A[K]})})});return f};return b}};r.g=function(a){if(a)for(var b in a)this[b]=a[b]};r.g.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 s;o({currency:function(a){k(this.R).toggleClass("ng-format-negative",a<0);return"$"+r.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:G(),json:function(a){k(this.R).addClass("ng-monospace");return v(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=",qa:[/^1Z[0-9A-Z]{16}$/i]},{name:"FedEx",url:"http://www.fedex.com/Tracking?tracknumbers=",qa:[/^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=",qa:[/^(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.qa,function(g){if(g.test(d)){g=f.name+": "+b;var h=f.url+b;e=new r.g({text:g,url:h,html:'<a href="'+La(h)+'">'+g+"</a>",Pc:b});_.breakLoop()}});e&&_.breakLoop()});return e?e:b?c||new r.g({text:b+" is not recognized"}):null}}(),link:function(a,b){b=b||r.g.get(a);var c=r.g.get(a,"url")||r.g.get(a);if(c){if(m.Q.Jc(c)===null)c="mailto:"+c;a='<a href="'+D(c)+'">'+b+"</a>";return new r.g({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 r.g({url:a.url,text:a.url,html:'<img src="'+a.url+'"'+d+"/>"})}return null},lowercase:function(a){return(a=r.g.get(a))?(""+a).toLowerCase():a},uppercase:function(a){return(a=r.g.get(a))? -(""+a).toUpperCase():a},linecount:function(a){a=r.g.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:B(function(a,b,c,d){b=b||{};a={cht:a,chco:s.collect(b,"color"),chtt:s.title(b),chdl:s.collect(b,"label"),chd:s.values(b),chf:"bg,s,FFFFFF00"};if(_.isArray(b.xLabels)){a.chxt="x";a.chxl="0:|"+b.Qc.join("|")}return s.encode(a,c,d)},{values:function(a){var b=[];o(a.series||[],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.series||[],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.chs=b+"x"+c;o(a,function(f,g){f&&e.push(g+"="+f)});e.sort();d+=e.join("&");return new r.g({url:d,html:'<img width="'+b+'" height="'+c+'" src="'+d+'"/>'})}}),qrcode:function(a,b,c){return s.encode({cht:"qr",chl:encodeURIComponent(a)},b,c)},chart:{pie:function(a,b,c){return s("p",a,b,c)},pie3d:function(a,b,c){return s("p3",a,b,c)},pieConcentric:function(a,b,c){return s("pc",a,b,c)},barHorizontalStacked:function(a,b,c){return s("bhs",a,b,c)},barHorizontalGrouped:function(a,b,c){return s("bhg", -a,b,c)},barVerticalStacked:function(a,b,c){return s("bvs",a,b,c)},barVerticalGrouped:function(a,b,c){return s("bvg",a,b,c)},line:function(a,b,c){return s("lc",a,b,c)},sparkline:function(a,b,c){return s("ls",a,b,c)},scatter:function(a,b,c){return s("s",a,b,c)}},html:function(a){return new r.g({html:a})},linky:function(a){if(!a)return a;for(var b=/(ftp|http|https|mailto):\/\/([^\(\)|\s]+)/,c,d=a,e=[];c=d.match(b);){c=c[0].replace(/[\.\;\,\(\)\{\}\<\>]$/,"");var f=d.indexOf(c);e.push(D(d.substr(0,f))); -e.push('<a href="'+c+'">');e.push(c);e.push("</a>");d=d.substring(f+c.length)}e.push(D(d));return new r.g({text:a,html:e.join("")})}},function(a,b){r[b]=a});s=r.googleChartApi;B(ha,{noop:T(Ha,Ha),"boolean":T(sa,W),number:T(sa,function(a){return 1*a}),list:T(function(a){return a?a.join(", "):a},function(a){return a?_(_(a.split(",")).map(k.trim)).select(_.identity):[]}),trim:T(function(a){return a?$.trim(""+a):""})});array=[].constructor;m.toJson=v;m.fromJson=N;I.ja=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]}};B(I.prototype,{$migrate:function(){X(this.$$entity.defaults,this);return this},$merge:function(a){X(a,this);return this},$save:function(a){this.$$entity.l.save(this,a===true?undefined:a);a===true&&this.$$entity.l.W();return this},$delete:function(a){this.$$entity.l.remove(this,a===true?undefined:a);a===true&&this.$$entity.l.W(); -return this},$loadById:function(a,b){this.$$entity.l.load(this,a,b);return this},$loadFrom:function(a){I.ja(a,this);return this},$saveTo:function(a){I.ja(this,a);return this}});F.ua={"null":L(null),"true":L(true),"false":L(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.i(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.db={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'};F.prototype={I:function(){return this.index+1<this.text.length?this.text.charAt(this.index+ -1):false},parse:function(){for(var a=this.h,b=F.ua,c=true;this.index<this.text.length;){var d=this.text.charAt(this.index);if(d=='"'||d=="'"){this.kc(d);c=true}else if(d=="("||d=="["){a.push({index:this.index,text:d});this.index++}else if(d=="{"){c=this.I();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.jc();c=false}else if(this.isNumber(d)){this.ic();c=false}else if(this.Y(d)){this.Wa();c=false}else if(this.Mb(d))this.index++;else{c=d+this.I();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},isNumber:function(a){return"0"<=a&&a<="9"},Mb:function(a){return a==" "||a=="\r"||a=="\t"||a=="\n"||a=="\u000b"},Y:function(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"},ic:function(){for(var a="",b=this.index;this.index<this.text.length;){var c=this.text.charAt(this.index);if(c=="."||this.isNumber(c))a+=c;else break;this.index++}a=1*a;this.h.push({index:b,text:a,b:function(){return a}})},Wa:function(){for(var a="",b=this.index;this.index<this.text.length;){var c= -this.text.charAt(this.index);if(c=="."||this.Y(c)||this.isNumber(c))a+=c;else break;this.index++}c=F.ua[a];if(!c){c=function(d){return d.scope.get(a)};c.X=a}this.h.push({index:b,text:a,b:c})},kc:function(a){var b=this.index,c=this.zb;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.db[f];d+=e?e:f}e=false}else if(f=="\\")e= -true;else if(f==a){this.index++;this.h.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+"'.";},jc: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.Y(this.text.charAt(this.index))){this.Wa(); -c=this.h.pop().text}var e=new RegExp(b,c);this.h.push({index:a,text:b,Lc: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+"'.";}};E.gb=L(0);E.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)+"'.";},ca:function(){if(this.h.length===0)throw"Unexpected end of expression: "+ -this.text;return this.h[0]},I:function(a,b,c,d){var e=this.h;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.I(a,b,c,d)){this.h.shift();return this.Ec=a}return false},k:function(a){if(!this.a(a)){var b=this.I();throw"Expecting '"+a+"' at column '"+(b.index+1)+"' in '"+this.text+"' got '"+this.text.substring(b.index)+"'.";}},ib:function(a,b){return function(c){return a(c,b(c))}},z:function(a,b,c){return function(d){return b(d, -a(d),c(d))}},Ha:function(){return this.h.length>0},L:function(){if(this.h.length!==0)throw"Did not understand '"+this.text.substring(this.h[0].index)+"' while evaluating '"+this.text+"'.";},ra:function(){for(var a=[];;){this.h.length>0&&!this.I("}",")",";","]")&&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.s(),b;;)if(b=this.a("|"))a=this.z(a,b.b,this.filter());else return a},filter:function(){return this.wa(r)}, -Q:function(){return this.wa(ga)},wa:function(a){for(var b=this.Jb(a),c=[];;)if(this.a(":"))c.push(this.s());else{var d=function(e,f){f=[f];for(var g=0;g<c.length;g++)f.push(c[g](e));function h(){var i=this;o(e,function(j,l){if(l.charAt(0)=="$")i[l]=j})}h.prototype=e.self;return b.apply(new h,f)};return function(){return d}}},s:function(){return this.oc()},oc:function(){if(this.a("throw")){var a=this.xa();return function(b){throw a(b);}}else return this.xa()},xa:function(){var a=this.Pa(),b;if(b=this.a("=")){if(!a.X)throw"Left hand side '"+ -this.text.substring(0,b.index)+"' of assignment '"+this.text.substring(b.index)+"' is not assignable.";return this.z(function(){return a.X},b.b,this.Pa())}else return a},Pa:function(){for(var a=this.na(),b;;)if(b=this.a("||"))a=this.z(a,b.b,this.na());else return a},na:function(){var a=this.Da(),b;if(b=this.a("&&"))a=this.z(a,b.b,this.na());return a},Da:function(){var a=this.Xa(),b;if(b=this.a("==","!="))a=this.z(a,b.b,this.Da());return a},Xa:function(){var a=this.pb(),b;if(b=this.a("<",">","<=", -">="))a=this.z(a,b.b,this.Xa());return a},pb:function(){for(var a=this.Qa(),b;b=this.a("+","-");)a=this.z(a,b.b,this.Qa());return a},Qa:function(){for(var a=this.ga(),b;b=this.a("*","/","%");)a=this.z(a,b.b,this.ga());return a},ga:function(){var a;return this.a("+")?this.da():(a=this.a("-"))?this.z(E.gb,a.b,this.ga()):(a=this.a("!"))?this.ib(a.b,this.ga()):this.da()},Jb: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},da:function(){var a;if(this.a("(")){a=this.Ea();this.k(")");a=a}else if(this.a("["))a=this.qb();else if(this.a("{"))a=this.object();else if(this.a("{:"))a=this.Aa(false);else if(this.a("{("))a=this.Aa(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.Ib(a);else if(b.text==="[")a=this.ec(a);else if(b.text===".")a=this.Gb(a);else throw"IMPOSSIBLE"; -return a},Aa: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.ra();this.k("}");return function(d){return function(e){var f=new u(d.scope.j);f.i("$",e);for(var g=0;g<b.length;g++)f.i(b[g],arguments[g]);return c({scope:f})}}},Gb:function(a){var b=this.a().text;function c(d){return u.O(a(d),b)}c.X=b;return c},ec:function(a){var b=this.s();this.k("]");if(this.a("=")){var c=this.s();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}},Ib:function(a){var b=[];if(this.ca().text!=")"){do b.push(this.s());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.X+"' is not a function.";}},qb:function(){var a=[];if(this.ca().text!="]"){do a.push(this.s());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.ca().text!="}"){do{var b=this.a().text;this.k(":");var c=this.s();a.push({Nb: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.Nb]=h}return e}},Eb:function(){for(var a=[];this.Ha();){a.push(this.Db());this.a(";")||this.L()}return function(b){for(var c="",d=0;d<a.length;d++)c+=a[d](b);return c}},Db:function(){var a=this.a().text,b,c;if(this.a("=")){b=a;a=this.a().text}if(this.a(":"))c= -this.da()(null);return function(d){var e=d.l.G(a,c);d.scope.i(a,e);if(b){e=e();e.$$anchor=b;d.scope.i(b,e);return"$anchor."+b+":{"+b+"="+a+".load($anchor."+b+");"+b+".$$anchor="+m.String.quote(b)+";};"}else return""}},watch:function(){for(var a=[];this.Ha();){a.push(this.wc());this.a(";")||this.L()}this.L();return function(b){for(var c=0;c<a.length;c++){var d=a[c](b);b.mb(d.name,d.b)}}},wc:function(){var a=this.a().text;this.k(":");var b;if(this.ca().text=="{"){this.k("{");b=this.ra();this.k("}")}else b= -this.s();return function(){return{name:a,b:b}}}};u.V={};u.O=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.$$factory)return Ma(e,a);return a}; -u.prototype={d:function(){var a=this;this.Hb();_.each(this.ab,function(b){a.o(b,"",{},function(){this.d(a)})})},ob:function(a){a&&this.ab.push(a)},Lb:function(a){for(var b=0;b<a.length;b++){var c=a.charAt(b);if(c!="."&&!F.prototype.Y(c))return false}return true},get:function(a){return u.O(this.j,a)},i:function(a,b){a=a.split(".");for(var c=this.j,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},q:function(a,b){this.eval(a+"="+v(b))},eval:function(a,b){var c= -u.V[a];if(!c){var d=new E(a);c=d.ra();d.L();u.V[a]=c}b=b||{};b.scope=this;b.self=this.j;return c(b)},o:function(a,b,c,d,e){try{var f=this.eval(b,c);if(a.C){a.C=false;k(a.view).removeClass("ng-exception").removeAttr("ng-error")}d&&d.apply(a,[f]);return true}catch(g){aa("Eval Widget Error:",g);b=v(g,true);a.C=true;k(a.view).addClass("ng-exception").attr("ng-error",b);e&&e.apply(a,[g,b]);return false}},uc:function(a,b,c){var d=u.V[a];if(!d){d=(new E(a)).Q();u.V[a]=d}a={scope:this,self:this.j,$element:c}; -return d(a)(a,b)},G:function(a,b){return(new E(a)).Eb()({scope:this,l:b})},ub:function(){for(var a=this.j.$invalidWidgets;a.length>0;)a.pop()},Sb:function(a){this.j.$invalidWidgets.push(a)},watch:function(a){var b=this;(new E(a)).watch()({scope:this,mb:function(c,d){b.nb(c,function(e,f){try{return d({scope:b},e,f)}catch(g){alert(g)}})}})},nb:function(a,b){var c=this.ta[a];if(!c){c={La:[],s:a};this.ta[a]=c}c.La.push(b)},Hb:function(){var a=this,b=false;o(this.ta,function(c){var d=a.eval(c.s);if(d!== -c.c){o(c.La,function(e){e(d,c.c);b=true});c.c=d}});return b}};oa.prototype={sb:function(a){return fa.Cb(a)},P:function(a,b,c,d){var e=this.tc+this.Tb++;a=this.sb(v({u:b,m:a,p:c}));b=Math.ceil(a.length/this.oa);c=this.url+"/$/"+e+"/"+b+"/";P[e]=function(h){delete P[e];d(200,h)};for(var f=0;f<b;f++){var g=a.substr(f*this.oa,this.oa);this.getScript(c+(f+1)+"?h="+g,n)}}};ca.Bc="$DATASET:";ca.prototype={P:G()};pa.prototype={P:function(a,b,c,d){var e=this;this.status.za(c);this.Ab.P(a,b,c,function(){e.status.Ca(); -try{d.apply(this,arguments)}catch(f){alert(v(f))}e.update()})}};B(ra.prototype,{fetchCurrentUser:function(a){var b=this;this.nc.P("GET","/account.json",{},function(c,d){b.current=d.user;a(d.user)})},logout:function(a){var b=this;this.ia.Rb(function(){delete b.current;(a||n)()})},login:function(a){var b=this;this.ia.Qb(function(){b.fetchCurrentUser(function(){(a||n)()})})},notAuthorized:function(){this.ia.dc()}});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){if(b=ga.number(a,b,c))return b;if(!(""+a).match(/^\s*[\d+]*\s*$/)||a!=Math.round(a))return"Value is not a whole number.";return null},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{N(a);return null}catch(b){return b.toString()}},asynchronous:function(a,b){var c=this.$element,d=c.$validateState=c.$validateState||{},e=d[a],f=this.$updateView;c.$lastKey=a;if(e===undefined){k(c).addClass("ng-input-indicator-wait");e=d[a]=null;b(a,function(g){e=d[a]=g?g:false;d[c.$lastKey]!== -null&&k(c).removeClass("ng-input-indicator-wait");f()})}e===null&&this.$invalidWidgets.push(this.R);return e}},function(a,b){ga[b]=a});na.prototype={wb: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,h=ha[a.attr("ng-format")]||ha.noop;if(d=="button"||d=="submit"||d=="reset"||d=="image"){c=new Sa(a[0],e,h);f="click";g=false}else if(d=="text"||d=="textarea"||d=="hidden"||d=="password"){c=new ta(a[0],e,h);f="keyup change"}else if(d== -"checkbox"){c=new ua(a[0],e,h);f="click"}else if(d=="radio"){c=new xa(a[0],e,h);f="click"}else if(d=="select-one")c=new va(a[0],e,h);else if(d=="select-multiple")c=new wa(a[0],e,h);else if(d=="file")c=this.xb(a,e,h);else throw"Unknown type: "+d;a.data("controller",c);var i=b.get("$updateView");k(c.view,":input").bind(f,function(){if(c.e(b)){var j=k(c.view).attr("ng-action")||"";b.o(c,j)&&i(b)}return g});return c},xb:function(a){var b="__uploadWidget_"+this.Ub++,c=U.template(b);a.after(c);b=this.yb({data:this.fa+ -"/admin/ServerAPI.swf",width:"95",height:"20",align:"top",wmode:"transparent"},{flashvars:"uploadWidgetId="+b,allowScriptAccess:"always"},b);a.remove();a=new U(c,a[0].name,b,this.fa+"/data/"+this.ka);k(b).parent().data("controller",a);return a}};P.flashEvent=function(a,b,c){a=x.getElementById(a);a=k(a);var d=a.parent().data("controller");U.prototype[b].apply(d,c);_.defer(a.scope().get("$updateView"))};U.template=function(a){return k('<span class="ng-upload-widget"><input type="checkbox" ng-non-bindable="true"/><object id="'+ -a+'" /><a></a><span/></span>')};B(U.prototype,{cancel:n,complete:n,httpStatus:function(a){alert("httpStatus:"+this.J+" status:"+a)},ioError:function(){alert("ioError:"+this.J)},open:function(){alert("open:"+this.J)},progress:n,securityError:function(){alert("securityError:"+this.J)},uploadCompleteData:function(a){a=N(a);a.url=this.ya+"/"+a.id+"/"+a.text;this.view.find("input").attr("checked",true);var b=this.view.scope();this.value=a;this.e(b);this.value=null},select:function(a,b){this.name=a;this.view.find("a").text(a).attr("href", -a);this.view.find("span").text(m.filter.bytes(b));this.upload()},e:function(a){var b=this.view.find("input").attr("checked")?this.value:null;if(this.c===b)return false;else{a.i(this.J,b);return true}},d:function(a){if((a=a.get(this.J))&&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)},upload:function(){this.name&&this.rc.uploadFile(this.ya)}}); -R.prototype={e:L(true),d:n};R.Kb=new R;var Sa=R;ta.prototype={e:function(a){var b=this.A.parse(this.view.value);if(this.c===b)return false;else{a.q(this.exp,b);this.c=b;return true}},d:function(a){var b=this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.w;a.q(this.exp,c)}c=c?c:"";if(!_(this.c).isEqual(c)){b.value=this.A.format(c);this.c=c}var d=false;b.removeAttribute("ng-error");if(this.lc)d=!(c&&$.trim(""+c).length>0);var e=d?"Required Value":null;if(!d&&this.Q&&c){e=a.uc(this.Q,c,b); -d=!!e}if(this.Ja!==e){this.Ja=d;if(e&&Ia(b)){b.setAttribute("ng-error",e);a.Sb(this)}k(b).toggleClass("ng-validation-error",d)}}};ua.prototype={e:function(a){var b=this.view;b=b.checked?b.value:"";b=this.A.parse(b);b=this.A.format(b);if(this.c===b)return false;else{a.q(this.exp,this.A.parse(b));this.c=b;return true}},d:function(a){var b=this.view,c=a.eval(this.exp);if(typeof c==="undefined"){c=this.w;a.q(this.exp,c)}b.checked=this.A.parse(b.value)==c}};va.prototype={e:function(a){if(this.view.selectedIndex< -0)a.q(this.exp,null);else{var b=this.view.value;if(this.c===b)return false;else{a.q(this.exp,b);this.c=b;return true}}},d:function(a){var b=this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.w;a.q(this.exp,c)}if(c!==this.c){b.value=c?c:"";this.c=c}}};wa.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.c===b)return false;else{a.q(this.exp,b);this.c=b;return true}}, -d:function(a){var b=this.view,c=a.get(this.exp);if(typeof c==="undefined"){c=this.w;a.q(this.exp,c)}if(c!==this.c){a=b.options;for(b=0;b<a.length;b++){var d=a[b];d.selected=_.include(c,d.value)}this.c=c}}};xa.prototype={e:function(a){var b=this.view;if(this.la)return false;else{b.checked=true;this.c=a.q(this.exp,this.Ia);return this.la=true}},d:function(a){var b=this.view,c=a.get(this.exp);if(this.w&&typeof c==="undefined"){c=this.w;a.q(this.exp,c)}if(this.c!=c){this.la=b.checked=this.Ia==""+c;this.c= -c}}};O.Ya=function(a){switch(typeof a){case "string":case "boolean":case "number":return D(a);case "function":return O.Ya(a());case "object":if(a&&a.tagName&&a.nodeName&&a.ownerDocument&&a.removeAttribute)return la(a);else if(a instanceof m.filter.g){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 la(a.html);default:break}switch(typeof a.text){case "string":case "number":return D(a.text); -case "function":return D(a.text());default:break}}if(a===null)return"";return D(v(a,true));default:return""}};O.prototype={e:n,d:function(a){for(var b=[],c=this.exp,d=c.length,e=0;e<d;e++){var f=c[e],g=t.M(f);if(g){a.o(this,g,{R:this.view},function(h){b.push(O.Ya(h))},function(h,i){ka(this.view,i)});if(this.C)return}else b.push(D(f))}ka(this.view,b.join(""))}};ya.prototype={e:n,d:function(a){var b=k(this.view),c=this.rb;if(this.C){this.C=false;b.removeClass("ng-exception").removeAttr("ng-error")}var d= -b.is("img");for(var e in c){for(var f=t.ba(c[e]),g=[],h=0;h<f.length;h++){var i=t.M(f[h]);if(i)try{var j=a.eval(i,{R:b[0],attrName:e});if(j&&(j.constructor!==array||j.length!==0))g.push(j)}catch(l){this.C=true;aa("BindAttrUpdater",l);i=v(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.blankImage");b.attr(e,f)}}};za.prototype={e:n,d:function(a){a.o(this,this.exp)}};Aa.prototype={e:n,d:function(a){a.o(this, -this.exp,{},function(b){var c=k(this.view);W(b)?c.hide():c.show()})}};Ba.prototype={e:n,d:function(a){a.o(this,this.exp,{},function(b){var c=k(this.view);W(b)?c.show():c.hide()})}};Ca.prototype={e:n,d:function(a){a.o(this,this.exp,{},function(b){if(b!==null&&b!==undefined)this.view.className=b})}};Da.prototype={e:n,d:function(a){a.o(this,this.exp,{},function(b){var c=a.get("$index");k(this.view).toggleClass(b,c%2===1)})}};Ea.prototype={e:n,d:function(a){a.o(this,this.exp,{},function(b){var c=a.get("$index"); -k(this.view).toggleClass(b,c%2===0)})}};Fa.prototype={e:n,d:function(a){a.o(this,this.exp,{},function(b){k(this.view).attr("style","").css(b)})}};Ga.prototype={e:n,d:function(a){a.o(this,this.Z,{},function(b){var c=this;if(!b){b=[];a.Lb(this.Z)&&a.i(this.Z,b)}var d=this.children.length,e=this.view,f=0,g=null,h=this.Ob,i=this.vc,j=0;o(b,function(l,q){if(j<d){g=c.children[j];g.scope.i(i,l)}else{var C=new u(a.j,c.prefix+i+" in "+c.Z+"["+j+"]");C.i("$index",j);h&&C.i(h,q);C.i(i,l);g={scope:C,element:c.template(C, -c.prefix,j)};e.after(g.element);c.children.push(g)}e=g.element;l=(new Date).getTime();g.scope.d();f+=(new Date).getTime()-l;j++});for(b=d;b>j;--b)this.children.pop().element.remove();if(g&&g.element[0].nodeName==="OPTION")if(b=k(g.element[0].parentNode).data("controller")){b.c=undefined;b.d(a)}})}};z.va="mouseleave mouseout click dblclick keypress keyup";z.fc=function(){z.pa();var a=k(this);a.bind(z.va,z.pa);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};z.pa=function(){k("#ng-callout").unbind(z.va,z.pa).remove(); -return true};z.prototype={bind:function(){this.F.find(".ng-validation-error,.ng-exception").live("mouseover",z.fc)}};qa.prototype={za:G(),Ca:G()};ea.cb='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>';ea.prototype={za:function(){if(this.ea===0)(this.Oa=this.Oa||this.body.append(ea.cb).find("#ng-loading")).show();this.ea++},Ca:function(){this.ea--;this.ea===0&&this.Oa.hide("fold")}}})(window,document); diff --git a/angular.js b/angular.js deleted file mode 100644 index da3301dd..00000000 --- a/angular.js +++ /dev/null @@ -1,4087 +0,0 @@ -/** - * 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){ -/** -* -* Base64 encode / decode -* http://www.webtoolkit.info/ -* -**/ - -var Base64 = { - - // private property - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=", - - // public method for encoding - encode : function (input) { - var output = ""; - var chr1, chr2, chr3, enc1, enc2, enc3, enc4; - var i = 0; - - input = Base64._utf8_encode(input); - - while (i < input.length) { - - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - output = output + - this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + - this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); - - } - - return output; - }, - - // public method for decoding - decode : function (input) { - var output = ""; - var chr1, chr2, chr3; - var enc1, enc2, enc3, enc4; - var i = 0; - - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - - while (i < input.length) { - - enc1 = this._keyStr.indexOf(input.charAt(i++)); - enc2 = this._keyStr.indexOf(input.charAt(i++)); - enc3 = this._keyStr.indexOf(input.charAt(i++)); - enc4 = this._keyStr.indexOf(input.charAt(i++)); - - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - - output = output + String.fromCharCode(chr1); - - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); - } - - } - - output = Base64._utf8_decode(output); - - return output; - - }, - - // private method for UTF-8 encoding - _utf8_encode : function (string) { - string = string.replace(/\r\n/g,"\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) { - - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } - - } - - return utftext; - }, - - // private method for UTF-8 decoding - _utf8_decode : function (utftext) { - var string = ""; - var i = 0; - var c = c1 = c2 = 0; - - while ( i < utftext.length ) { - - c = utftext.charCodeAt(i); - - if (c < 128) { - string += String.fromCharCode(c); - i++; - } - else if((c > 191) && (c < 224)) { - c2 = utftext.charCodeAt(i+1); - string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); - i += 2; - } - else { - c2 = utftext.charCodeAt(i+1); - c3 = utftext.charCodeAt(i+2); - string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); - i += 3; - } - - } - - return string; - } - -};if (typeof document.getAttribute == 'undefined') - document.getAttribute = function() {}; -if (typeof Node == 'undefined') { - Node = { - ELEMENT_NODE : 1, - ATTRIBUTE_NODE : 2, - TEXT_NODE : 3, - CDATA_SECTION_NODE : 4, - ENTITY_REFERENCE_NODE : 5, - ENTITY_NODE : 6, - PROCESSING_INSTRUCTION_NODE : 7, - COMMENT_NODE : 8, - DOCUMENT_NODE : 9, - DOCUMENT_TYPE_NODE : 10, - DOCUMENT_FRAGMENT_NODE : 11, - NOTATION_NODE : 12 - }; -} - -function noop() {} -if (!window['console']) window['console']={'log':noop, 'error':noop}; - -var consoleNode, msie, - jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy - foreach = _.each, - extend = _.extend, - identity = _.identity, - angular = window['angular'] || (window['angular'] = {}), - angularValidator = angular['validator'] || (angular['validator'] = {}), - angularFilter = angular['filter'] || (angular['filter'] = {}), - angularFormatter = angular['formatter'] || (angular['formatter'] = {}), - angularCallbacks = angular['callbacks'] || (angular['callbacks'] = {}), - angularAlert = angular['alert'] || (angular['alert'] = function(){ - log(arguments); window.alert.apply(window, arguments); - }); - -var isVisible = isVisible || function (element) { - return jQuery(element).is(":visible"); -} - -function log(a, b, c){ - var console = window['console']; - switch(arguments.length) { - case 1: - console['log'](a); - break; - case 2: - console['log'](a, b); - break; - default: - console['log'](a, b, c); - break; - } -} - -function error(a, b, c){ - var console = window['console']; - switch(arguments.length) { - case 1: - console['error'](a); - break; - case 2: - console['error'](a, b); - break; - default: - console['error'](a, b, c); - break; - } -} - -function consoleLog(level, objs) { - var log = document.createElement("div"); - log.className = level; - var msg = ""; - var sep = ""; - for ( var i = 0; i < objs.length; i++) { - var obj = objs[i]; - msg += sep + (typeof obj == 'string' ? obj : toJson(obj)); - sep = " "; - } - log.appendChild(document.createTextNode(msg)); - consoleNode.appendChild(log); -} - -function isNode(inp) { - return inp && - inp.tagName && - inp.nodeName && - inp.ownerDocument && - inp.removeAttribute; -} - -function isLeafNode (node) { - switch (node.nodeName) { - case "OPTION": - case "PRE": - case "TITLE": - return true; - default: - return false; - } -} - -function setHtml(node, html) { - if (isLeafNode(node)) { - if (msie) { - node.innerText = html; - } else { - node.textContent = html; - } - } else { - node.innerHTML = html; - } -} - -function escapeHtml(html) { - if (!html || !html.replace) - return html; - return 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) - throw "Missing this"; - if (!_.isFunction(_function)) - throw "Missing function"; - return function() { - return _function.apply(_this, arguments); - }; -} - -function outerHTML(node) { - var temp = document.createElement('div'); - temp.appendChild(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) { - var value = dst[key]; - var type = typeof value; - if (type == 'undefined') { - dst[key] = fromJson(toJson(src[key])); - } else if (type == 'object' && value.constructor != array && - key.substring(0, 1) != "$") { - merge(src[key], value); - } - } -} - -// //////////////////////////// -// UrlWatcher -// //////////////////////////// - -function UrlWatcher(location) { - this.location = location; - this.delay = 25; - this.setTimeout = function(fn, delay) { - window.setTimeout(fn, delay); - }; - this.listener = function(url) { - return url; - }; - this.expectedUrl = location.href; -} - -UrlWatcher.prototype = { - listen: function(fn){ - this.listener = fn; - }, - watch: function() { - var self = this; - var pull = function() { - if (self.expectedUrl !== self.location.href) { - var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/); - if (notify) { - if (!self.expectedUrl.match(/#/)) { - self.expectedUrl += "#"; - } - self.location.href = self.expectedUrl; - var id = '_iframe_notify_' + notify[1]; - var notifyFn = angularCallbacks[id]; - delete angularCallbacks[id]; - try { - (notifyFn||noop)(); - } catch (e) { - alert(e); - } - } else { - self.listener(self.location.href); - self.expectedUrl = self.location.href; - } - } - self.setTimeout(pull, self.delay); - }; - pull(); - }, - - set: function(url) { - var existingURL = this.location.href; - if (!existingURL.match(/#/)) - existingURL += '#'; - if (existingURL != url) - this.location.href = url; - this.existingURL = url; - }, - - get: function() { - return window.location.href; - } -}; - -///////////////////////////////////////////////// -function configureJQueryPlugins() { - var fn = jQuery['fn']; - fn['scope'] = function() { - var element = this; - while (element && element.get(0)) { - var scope = element.data("scope"); - if (scope) - return scope; - element = element.parent(); - } - return null; - }; - fn['controller'] = function() { - return this.data('controller') || NullController.instance; - }; -} - -function configureLogging(config) { - if (config.debug == 'console' && !consoleNode) { - consoleNode = document.createElement("div"); - consoleNode.id = 'ng-console'; - document.getElementsByTagName('body')[0].appendChild(consoleNode); - log = function() { - consoleLog('ng-console-info', arguments); - }; - console.error = function() { - consoleLog('ng-console-error', arguments); - }; - } -} - -function exposeMethods(obj, methods){ - var bound = {}; - foreach(methods, function(fn, name){ - bound[name] = _(fn).bind(obj); - }); - return bound; -} - -function wireAngular(element, config) { - var widgetFactory = new WidgetFactory(config['server'], config['database']); - var binder = new Binder(element[0], widgetFactory, datastore, config['location'], config); - binder.updateListeners.push(config.onUpdateView); - var controlBar = new ControlBar(element.find('body'), config['server'], config['database']); - var onUpdate = function(){binder.updateView();}; - var server = config['database'] =="$MEMORY" ? - new FrameServer(window) : - new Server(config['server'], jQuery['getScript']); - server = new VisualServer(server, new NullStatus(element.find('body')), onUpdate); - var users = new Users(server, controlBar); - var databasePath = '/data/' + config['database']; - var post = function(request, callback){ - server.request("POST", databasePath, request, callback); - }; - var datastore = new DataStore(post, users, binder.anchor); - binder.datastore = datastore; - binder.updateListeners.push(function(){datastore.flush();}); - var scope = new Scope({ - '$anchor' : binder.anchor, - '$updateView': _(binder.updateView).bind(binder), - '$config' : config, - '$invalidWidgets': [], - '$console' : window.console, - '$datastore' : exposeMethods(datastore, { - 'load': datastore.load, - 'loadMany': datastore.loadMany, - 'loadOrCreate': datastore.loadOrCreate, - 'loadAll': datastore.loadAll, - 'save': datastore.save, - 'remove': datastore.remove, - 'flush': datastore.flush, - 'query': datastore.query, - 'entity': datastore.entity, - 'entities': datastore.entities, - 'documentCountsByUser': datastore.documentCountsByUser, - 'userDocumentIdsByEntity': datastore.userDocumentIdsByEntity, - 'join': datastore.join - }), - '$save' : function(callback) { - datastore.saveScope(scope.state, callback, binder.anchor); - }, - '$window' : window, - '$uid' : function() { - return "" + new Date().getTime(); - }, - '$users' : users - }, "ROOT"); - - element.data('scope', scope); - binder.entity(scope); - binder.compile(); - controlBar.bind(); - - //TODO: remove this code - new PopUp(element).bind(); - - var self = _(exposeMethods(scope, { - 'set': scope.set, - 'get': scope.get, - 'eval': scope.eval - })).extend({ - 'init':function(){ - config['location']['listen'](_(binder.onUrlChange).bind(binder)); - binder.parseAnchor(); - binder.executeInit(); - binder.updateView(); - return self; - }, - 'element':element[0], - 'updateView': _(binder.updateView).bind(binder), - 'config':config - }); - return self; -} - -angular['startUrlWatcher'] = function(){ - var watcher = new UrlWatcher(window['location']); - watcher.watch(); - return exposeMethods(watcher, {'listen':watcher.listen, 'set':watcher.set, 'get':watcher.get}); -}; - -angular['compile'] = function(element, config) { - jQuery = window['jQuery']; - msie = jQuery['browser']['msie']; - config = _({ - 'onUpdateView': noop, - 'server': "", - 'location': {'get':noop, 'set':noop, 'listen':noop} - }).extend(config||{}); - - configureLogging(config); - configureJQueryPlugins(); - - return wireAngular(jQuery(element), config); -}; -var angularGlobal = { - 'typeOf':function(obj){ - if (obj === null) return "null"; - var type = typeof obj; - if (type == "object") { - if (obj instanceof Array) return "array"; - if (obj instanceof Date) return "date"; - if (obj.nodeType == 1) return "element"; - } - return type; - } -}; - -var angularCollection = {}; -var angularObject = {}; -var angularArray = { - 'includeIf':function(array, value, condition) { - var index = _.indexOf(array, value); - if (condition) { - if (index == -1) - array.push(value); - } else { - array.splice(index, 1); - } - return array; - }, - 'sum':function(array, expression) { - var fn = angular['Function']['compile'](expression); - var sum = 0; - for (var i = 0; i < array.length; i++) { - var value = 1 * fn(array[i]); - if (!isNaN(value)){ - sum += value; - } - } - return sum; - }, - 'remove':function(array, value) { - var index = _.indexOf(array, value); - if (index >=0) - array.splice(index, 1); - return value; - }, - 'find':function(array, condition, defaultValue) { - if (!condition) return undefined; - var fn = angular['Function']['compile'](condition); - _.detect(array, function($){ - if (fn($)){ - defaultValue = $; - return true; - } - }); - return defaultValue; - }, - 'findById':function(array, id) { - return angular.Array.find(array, function($){return $.$id == id;}, null); - }, - 'filter':function(array, expression) { - var predicates = []; - predicates.check = function(value) { - for (var j = 0; j < predicates.length; j++) { - if(!predicates[j](value)) { - return false; - } - } - return true; - }; - var getter = Scope.getter; - var search = function(obj, text){ - if (text.charAt(0) === '!') { - return !search(obj, text.substr(1)); - } - switch (typeof obj) { - case "boolean": - case "number": - case "string": - return ('' + obj).toLowerCase().indexOf(text) > -1; - case "object": - for ( var objKey in obj) { - if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { - return true; - } - } - return false; - case "array": - for ( var i = 0; i < obj.length; i++) { - if (search(obj[i], text)) { - return true; - } - } - return false; - default: - return false; - } - }; - switch (typeof expression) { - case "boolean": - case "number": - case "string": - expression = {$:expression}; - case "object": - for (var key in expression) { - if (key == '$') { - (function(){ - var text = (''+expression[key]).toLowerCase(); - if (!text) return; - predicates.push(function(value) { - return search(value, text); - }); - })(); - } else { - (function(){ - var path = key; - var text = (''+expression[key]).toLowerCase(); - if (!text) return; - predicates.push(function(value) { - return search(getter(value, path), text); - }); - })(); - } - } - break; - case "function": - predicates.push(expression); - break; - default: - return array; - } - var filtered = []; - for ( var j = 0; j < array.length; j++) { - var value = array[j]; - if (predicates.check(value)) { - filtered.push(value); - } - } - return filtered; - }, - 'add':function(array, value) { - array.push(_.isUndefined(value)? {} : value); - return array; - }, - 'count':function(array, condition) { - if (!condition) return array.length; - var fn = angular['Function']['compile'](condition); - return _.reduce(array, 0, function(count, $){return count + (fn($)?1:0);}); - }, - 'orderBy':function(array, expression, descend) { - function reverse(comp, descending) { - return toBoolean(descending) ? - function(a,b){return comp(b,a);} : comp; - } - function compare(v1, v2){ - var t1 = typeof v1; - var t2 = typeof v2; - if (t1 == t2) { - if (t1 == "string") v1 = v1.toLowerCase(); - if (t1 == "string") v2 = v2.toLowerCase(); - if (v1 === v2) return 0; - return v1 < v2 ? -1 : 1; - } else { - return t1 < t2 ? -1 : 1; - } - } - expression = _.isArray(expression) ? expression: [expression]; - expression = _.map(expression, function($){ - var descending = false; - if (typeof $ == "string" && ($.charAt(0) == '+' || $.charAt(0) == '-')) { - descending = $.charAt(0) == '-'; - $ = $.substring(1); - } - var get = $ ? angular['Function']['compile']($) : _.identity; - return reverse(function(a,b){ - return compare(get(a),get(b)); - }, descending); - }); - var comparator = function(o1, o2){ - for ( var i = 0; i < expression.length; i++) { - var comp = expression[i](o1, o2); - if (comp !== 0) return comp; - } - return 0; - }; - return _.clone(array).sort(reverse(comparator, descend)); - }, - 'orderByToggle':function(predicate, attribute) { - var STRIP = /^([+|-])?(.*)/; - var ascending = false; - var index = -1; - _.detect(predicate, function($, i){ - if ($ == attribute) { - ascending = true; - index = i; - return true; - } - if (($.charAt(0)=='+'||$.charAt(0)=='-') && $.substring(1) == attribute) { - ascending = $.charAt(0) == '+'; - index = i; - return true; - } - }); - if (index >= 0) { - predicate.splice(index, 1); - } - predicate.unshift((ascending ? "-" : "+") + attribute); - return predicate; - }, - 'orderByDirection':function(predicate, attribute, ascend, descend) { - ascend = ascend || 'ng-ascend'; - descend = descend || 'ng-descend'; - var att = predicate[0] || ''; - var direction = true; - if (att.charAt(0) == '-') { - att = att.substring(1); - direction = false; - } else if(att.charAt(0) == '+') { - att = att.substring(1); - } - return att == attribute ? (direction ? ascend : descend) : ""; - }, - 'merge':function(array, index, mergeValue) { - var value = array[index]; - if (!value) { - value = {}; - array[index] = value; - } - merge(mergeValue, value); - return array; - } -}; - -var angularString = { - 'quote':function(string) { - return '"' + string.replace(/\\/g, '\\\\'). - replace(/"/g, '\\"'). - replace(/\n/g, '\\n'). - replace(/\f/g, '\\f'). - replace(/\r/g, '\\r'). - replace(/\t/g, '\\t'). - replace(/\v/g, '\\v') + - '"'; - }, - 'quoteUnicode':function(string) { - var str = angular['String']['quote'](string); - var chars = []; - for ( var i = 0; i < str.length; i++) { - var ch = str.charCodeAt(i); - if (ch < 128) { - chars.push(str.charAt(i)); - } else { - var encode = "000" + ch.toString(16); - chars.push("\\u" + encode.substring(encode.length - 4)); - } - } - return chars.join(''); - }, - 'toDate':function(string){ - var match; - if (typeof string == 'string' && - (match = string.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){ - var date = new Date(0); - date.setUTCFullYear(match[1], match[2] - 1, match[3]); - date.setUTCHours(match[4], match[5], match[6], 0); - return date; - } - return string; - } -}; - -var angularDate = { - 'toString':function(date){ - function pad(n) { return n < 10 ? "0" + n : n; } - return (date.getUTCFullYear()) + '-' + - pad(date.getUTCMonth() + 1) + '-' + - pad(date.getUTCDate()) + 'T' + - pad(date.getUTCHours()) + ':' + - pad(date.getUTCMinutes()) + ':' + - pad(date.getUTCSeconds()) + 'Z'; - } - }; - -var angularFunction = { - 'compile':function(expression) { - if (_.isFunction(expression)){ - return expression; - } else if (expression){ - var scope = new Scope(); - return function($) { - scope.state = $; - return scope.eval(expression); - }; - } else { - return function($){return $;}; - } - } -}; - -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], []); -//IE bug -angular['Date']['toString'] = angularDate['toString']; -defineApi('Function', [angularGlobal, angularCollection, angularFunction], - ['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']); -function Binder(doc, widgetFactory, datastore, location, config) { - this.doc = doc; - this.location = location; - this.datastore = datastore; - this.anchor = {}; - this.widgetFactory = widgetFactory; - this.config = config || {}; - this.updateListeners = []; -} - -Binder.parseBindings = function(string) { - var results = []; - var lastIndex = 0; - var index; - while((index = string.indexOf('{{', lastIndex)) > -1) { - if (lastIndex < index) - results.push(string.substr(lastIndex, index - lastIndex)); - lastIndex = index; - - index = string.indexOf('}}', index); - index = index < 0 ? string.length : index + 2; - - results.push(string.substr(lastIndex, index - lastIndex)); - lastIndex = index; - } - if (lastIndex != string.length) - results.push(string.substr(lastIndex, string.length - lastIndex)); - return results.length === 0 ? [ string ] : results; -}; - -Binder.hasBinding = function(string) { - var bindings = Binder.parseBindings(string); - return bindings.length > 1 || Binder.binding(bindings[0]) !== null; -}; - -Binder.binding = function(string) { - var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/); - return binding ? binding[1] : null; -}; - - -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() { - var self = this, url = this.location['get']() || ""; - - 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() { - this.parseAnchor(); - this.updateView(); - }, - - updateAnchor: function() { - var url = this.location['get']() || ""; - 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.location['set'](url); - return url; - }, - - updateView: function() { - var start = new Date().getTime(); - var scope = jQuery(this.doc).scope(); - scope.clearInvalid(); - scope.updateView(); - var end = new Date().getTime(); - this.updateAnchor(); - foreach(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(); - } - 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) { - var self = this; - this.docFindWithSelf("[ng-entity]").attr("ng-watch", function() { - try { - var jNode = jQuery(this); - var decl = scope.entity(jNode.attr("ng-entity"), self.datastore); - return decl + (jNode.attr('ng-watch') || ""); - } catch (e) { - log(e); - alert(e); - } - }); - }, - - compile: function() { - var jNode = jQuery(this.doc); - 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); - var scope = jNode.scope(); - try { - 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)); - } - scope.get('$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); - } - parent.insertBefore(newNode, node); - } - } - parent.removeChild(node); - } - }, - - 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); - } - } - }; - }, - - 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('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)) { - if (Binder.hasBinding(node.text)) { - jQuery(node).attr('ng-bind-attr', angular.toJson({'value':node.text})); - } else { - 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')); - } -}; -function ControlBar(document, serverUrl, database) { - this._document = document; - this.serverUrl = serverUrl; - this.database = database; - this._window = window; - this.callbacks = []; -}; - -ControlBar.HTML = - '<div>' + - '<div class="ui-widget-overlay"></div>' + - '<div id="ng-login" ng-non-bindable="true">' + - '<div class="ng-login-container"></div>' + - '</div>' + - '</div>'; - - -ControlBar.FORBIDEN = - '<div ng-non-bindable="true" title="Permission Error:">' + - 'Sorry, you do not have permission for this!'+ - '</div>'; - -ControlBar.prototype = { - bind: function () { - }, - - login: function (loginSubmitFn) { - this.callbacks.push(loginSubmitFn); - if (this.callbacks.length == 1) { - this.doTemplate("/user_session/new.mini?database="+encodeURIComponent(this.database)+"&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() + "#$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>' - }); - angularCallbacks["_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 = []; - this._cache = {'$collections':this._cache_collections}; - this.anchor = anchor; - this.bulkRequest = []; -}; - -DataStore.NullEntity = extend(function(){}, { - 'all': function(){return [];}, - 'query': function(){return [];}, - 'load': function(){return {};}, - 'title': undefined -}); - -DataStore.prototype = { - cache: function(document) { - if (! document.datastore === this) { - 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; - }, - - 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; - } - }); - }, - - 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)) { - angularArray['includeIf'](collection, cachedDoc, true); - } - }); - if (document['$$anchor']) { - self.anchor[document['$$anchor']] = document['$id']; - } - if (callback) - callback(document); - }); - }, - - remove: function(document, callback) { - var self = this; - var data = {}; - document['$saveTo'](data); - this._jsonRequest(["DELETE", "", data], function(response) { - delete self._cache[document['$entity'] + '/' + document['$id']]; - _.each(self._cache_collections, function(collection){ - for ( var i = 0; i < collection.length; i++) { - var item = collection[i]; - if (item['$id'] == document['$id']) { - collection.splice(i, 1); - } - } - }); - (callback||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['$$callback'](response); - } - } - } - } - 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); - } - } - 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; - foreach(list, function(item){ - var document = type()['$loadFrom'](item); - queryList.push(self.cache(document)); - }); - (callback||noop)(queryList); - }); - return queryList; - }, - - entities: function(callback) { - var entities = []; - var self = this; - this._jsonRequest(["GET", "$entities"], function(response) { - foreach(response, function(value, entityName){ - entities.push(self.entity(entityName)); - }); - entities.sort(function(a,b){return a.title > b.title ? 1 : -1;}); - (callback||noop)(entities); - }); - return entities; - }, - - documentCountsByUser: function(){ - var counts = {}; - var self = this; - self.post([["GET", "$users"]], function(code, response){ - extend(counts, response[0]); - }); - return counts; - }, - - userDocumentIdsByEntity: function(user){ - var ids = {}; - var self = this; - self.post([["GET", "$users/" + user]], function(code, response){ - extend(ids, response[0]); - }); - 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, //private, obfuscate - '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){ - var row = {}; - joinedResult.push(row); - row[baseName] = doc; - var id = Scope.getter(row, nextJoinOn); - joinIds[id] = id; - }); - nextJoin.join.loadMany(_.toArray(joinIds), function(result){ - var byId = {}; - _(result).each(function(doc){ - byId[doc.$id] = doc; - }); - _(joinedResult).each(function(row){ - var id = Scope.getter(row, nextJoinOn); - row[nextJoinName] = byId[id]; - }); - }); - }); - return joinedResult; - }; - return fn; - } -};angularFilter.Meta = function(obj){ - if (obj) { - for ( var key in obj) { - this[key] = obj[key]; - } - } -}; -angularFilter.Meta.get = function(obj, attr){ - attr = attr || 'text'; - switch(typeof obj) { - case "string": - return attr == "text" ? obj : undefined; - case "object": - if (obj && typeof obj[attr] !== "undefined") { - return obj[attr]; - } - return undefined; - default: - return obj; - } -}; - -var angularFilterGoogleChartApi; - -foreach({ - 'currency': function(amount){ - jQuery(this.$element).toggleClass('ng-format-negative', amount < 0); - return '$' + angularFilter['number'].apply(this, [amount, 2]); - }, - - 'number': function(amount, fractionSize){ - if (isNaN(amount) || !isFinite(amount)) { - return ''; - } - fractionSize = typeof fractionSize == 'undefined' ? 2 : fractionSize; - var isNegative = amount < 0; - amount = Math.abs(amount); - var pow = Math.pow(10, fractionSize); - var text = "" + Math.round(amount * pow); - var whole = text.substring(0, text.length - fractionSize); - whole = whole || '0'; - var frc = text.substring(text.length - fractionSize); - text = isNegative ? '-' : ''; - for (var i = 0; i < whole.length; i++) { - if ((whole.length - i)%3 === 0 && i !== 0) { - text += ','; - } - text += whole.charAt(i); - } - if (fractionSize > 0) { - for (var j = frc.length; j < fractionSize; j++) { - frc += '0'; - } - text += '.' + frc.substring(0, fractionSize); - } - return text; - }, - - 'date': function(amount) { - }, - - 'json': function(object) { - jQuery(this.$element).addClass("ng-monospace"); - return toJson(object, true); - }, - - 'trackPackage': (function(){ - var MATCHERS = [ - { name: "UPS", - url: "http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=en_US&track.x=0&track.y=0&InquiryNumber1=", - regexp: [ - /^1Z[0-9A-Z]{16}$/i]}, - { name: "FedEx", - url: "http://www.fedex.com/Tracking?tracknumbers=", - regexp: [ - /^96\d{10}?$/i, - /^96\d{17}?$/i, - /^96\d{20}?$/i, - /^\d{15}$/i, - /^\d{12}$/i]}, - { name: "USPS", - url: "http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?origTrackNum=", - regexp: [ - /^(91\d{20})$/i, - /^(91\d{18})$/i]}]; - return function(trackingNo, noMatch) { - trackingNo = trim(trackingNo); - var tNo = trackingNo.replace(/ /g, ''); - var returnValue; - foreach(MATCHERS, function(carrier){ - foreach(carrier.regexp, function(regexp){ - if (regexp.test(tNo)) { - var text = carrier.name + ": " + trackingNo; - var url = carrier.url + trackingNo; - returnValue = new angularFilter.Meta({ - text:text, - url:url, - html: '<a href="' + escapeAttr(url) + '">' + text + '</a>', - trackingNo:trackingNo}); - _.breakLoop(); - } - }); - if (returnValue) _.breakLoop(); - }); - if (returnValue) - return returnValue; - else if (trackingNo) - return noMatch || new angularFilter.Meta({text:trackingNo + " is not recognized"}); - else - return null; - };})(), - - 'link': function(obj, title) { - var text = title || angularFilter.Meta.get(obj); - var url = angularFilter.Meta.get(obj, "url") || angularFilter.Meta.get(obj); - if (url) { - if (angular.validator.email(url) === null) { - url = "mailto:" + url; - } - var html = '<a href="' + escapeHtml(url) + '">' + text + '</a>'; - return new angularFilter.Meta({text:text, url:url, html:html}); - } - return obj; - }, - - - 'bytes': (function(){ - var SUFFIX = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB']; - return function(size) { - if(size === null) return ""; - - var suffix = 0; - while (size > 1000) { - size = size / 1024; - suffix++; - } - var txt = "" + size; - var dot = txt.indexOf('.'); - if (dot > -1 && dot + 2 < txt.length) { - txt = txt.substring(0, dot + 2); - } - return txt + " " + SUFFIX[suffix]; - }; - })(), - - 'image': function(obj, width, height) { - if (obj && obj.url) { - var style = ""; - if (width) { - style = ' style="max-width: ' + width + - 'px; max-height: ' + (height || width) + 'px;"'; - } - return new angularFilter.Meta({url:obj.url, text:obj.url, - html:'<img src="'+obj.url+'"' + style + '/>'}); - } - return null; - }, - - 'lowercase': function (obj) { - var text = angularFilter.Meta.get(obj); - return text ? ("" + text).toLowerCase() : text; - }, - - 'uppercase': function (obj) { - var text = angularFilter.Meta.get(obj); - return text ? ("" + text).toUpperCase() : text; - }, - - 'linecount': function (obj) { - var text = angularFilter.Meta.get(obj); - if (text==='' || !text) return 1; - return text.split(/\n|\f/).length; - }, - - 'if': function (result, expression) { - return expression ? result : undefined; - }, - - 'unless': function (result, expression) { - return expression ? undefined : result; - }, - - 'googleChartApi': extend( - function(type, data, width, height) { - data = data || {}; - var chart = { - 'cht':type, - 'chco':angularFilterGoogleChartApi['collect'](data, 'color'), - 'chtt':angularFilterGoogleChartApi['title'](data), - 'chdl':angularFilterGoogleChartApi['collect'](data, 'label'), - 'chd':angularFilterGoogleChartApi['values'](data), - 'chf':'bg,s,FFFFFF00' - }; - if (_.isArray(data['xLabels'])) { - chart['chxt']='x'; - chart['chxl']='0:|' + data.xLabels.join('|'); - } - return angularFilterGoogleChartApi['encode'](chart, width, height); - }, - { - 'values': function(data){ - var seriesValues = []; - foreach(data['series']||[], function(serie){ - var values = []; - foreach(serie['values']||[], function(value){ - values.push(value); - }); - seriesValues.push(values.join(',')); - }); - var values = seriesValues.join('|'); - return values === "" ? null : "t:" + values; - }, - - 'title': function(data){ - var titles = []; - var title = data['title'] || []; - foreach(_.isArray(title)?title:[title], function(text){ - titles.push(encodeURIComponent(text)); - }); - return titles.join('|'); - }, - - 'collect': function(data, key){ - var outterValues = []; - var count = 0; - foreach(data['series']||[], function(serie){ - var innerValues = []; - var value = serie[key] || []; - foreach(_.isArray(value)?value:[value], function(color){ - innerValues.push(encodeURIComponent(color)); - count++; - }); - outterValues.push(innerValues.join('|')); - }); - return count?outterValues.join(','):null; - }, - - 'encode': function(params, width, height) { - width = width || 200; - height = height || width; - var url = "http://chart.apis.google.com/chart?"; - var urlParam = []; - params['chs'] = width + "x" + height; - foreach(params, function(value, key){ - if (value) { - urlParam.push(key + "=" + value); - } - }); - urlParam.sort(); - url += urlParam.join("&"); - return new angularFilter.Meta({url:url, - html:'<img width="' + width + '" height="' + height + '" src="'+url+'"/>'}); - } - } - ), - - - 'qrcode': function(value, width, height) { - return angularFilterGoogleChartApi['encode']({ - 'cht':'qr', 'chl':encodeURIComponent(value)}, width, height); - }, - 'chart': { - 'pie':function(data, width, height) { - return angularFilterGoogleChartApi('p', data, width, height); - }, - 'pie3d':function(data, width, height) { - return angularFilterGoogleChartApi('p3', data, width, height); - }, - 'pieConcentric':function(data, width, height) { - return angularFilterGoogleChartApi('pc', data, width, height); - }, - 'barHorizontalStacked':function(data, width, height) { - return angularFilterGoogleChartApi('bhs', data, width, height); - }, - 'barHorizontalGrouped':function(data, width, height) { - return angularFilterGoogleChartApi('bhg', data, width, height); - }, - 'barVerticalStacked':function(data, width, height) { - return angularFilterGoogleChartApi('bvs', data, width, height); - }, - 'barVerticalGrouped':function(data, width, height) { - return angularFilterGoogleChartApi('bvg', data, width, height); - }, - 'line':function(data, width, height) { - return angularFilterGoogleChartApi('lc', data, width, height); - }, - 'sparkline':function(data, width, height) { - return angularFilterGoogleChartApi('ls', data, width, height); - }, - 'scatter':function(data, width, height) { - return angularFilterGoogleChartApi('s', data, width, height); - } - }, - - 'html': function(html){ - return new angularFilter.Meta({html:html}); - }, - - 'linky': function(text){ - if (!text) return text; - function regExpEscape(text) { - return text.replace(/([\/\.\*\+\?\|\(\)\[\]\{\}\\])/g, '\\$1'); - } - var URL = /(ftp|http|https|mailto):\/\/([^\(\)|\s]+)/; - var match; - var raw = text; - var html = []; - while (match=raw.match(URL)) { - var url = match[0].replace(/[\.\;\,\(\)\{\}\<\>]$/,''); - var i = raw.indexOf(url); - html.push(escapeHtml(raw.substr(0, i))); - html.push('<a href="' + url + '">'); - html.push(url); - html.push('</a>'); - raw = raw.substring(i + url.length); - } - html.push(escapeHtml(raw)); - return new angularFilter.Meta({text:text, html:html.join('')}); - } -}, function(v,k){angularFilter[k] = v;}); - -angularFilterGoogleChartApi = angularFilter['googleChartApi']; -function formater(format, parse) {return {'format':format, 'parse':parse || format};} -function toString(obj) {return ""+obj;}; -extend(angularFormatter, { - 'noop':formater(identity, identity), - 'boolean':formater(toString, toBoolean), - 'number':formater(toString, function(obj){return 1*obj;}), - - 'list':formater( - function(obj) { return obj ? obj.join(", ") : obj; }, - function(value) { - return value ? _(_(value.split(',')).map(jQuery.trim)).select(_.identity) : []; - } - ), - - 'trim':formater( - function(obj) { return obj ? $.trim("" + obj) : ""; } - ) -}); -array = [].constructor; - -function toJson(obj, pretty){ - var buf = []; - toJsonArray(buf, obj, pretty ? "\n " : null, _([])); - return buf.join(''); -}; - -function toPrettyJson(obj) { - return toJson(obj, true); -}; - -function fromJson(json) { - try { - var parser = new Parser(json, true); - var expression = parser.primary(); - parser.assertAllConsumed(); - return expression(); - } catch (e) { - error("fromJson error: ", json, e); - throw e; - } -}; - -angular['toJson'] = toJson; -angular['fromJson'] = fromJson; - -function toJsonArray(buf, obj, pretty, stack){ - if (typeof obj == "object") { - if (stack.include(obj)) { - buf.push("RECURSION"); - return; - } - stack.push(obj); - } - var type = typeof obj; - if (obj === null) { - buf.push("null"); - } else if (type === 'function') { - return; - } else if (type === 'boolean') { - buf.push('' + obj); - } else if (type === 'number') { - if (isNaN(obj)) { - buf.push('null'); - } else { - buf.push('' + obj); - } - } else if (type === 'string') { - return buf.push(angular['String']['quoteUnicode'](obj)); - } else if (type === 'object') { - if (obj instanceof Array) { - buf.push("["); - var len = obj.length; - var sep = false; - for(var i=0; i<len; i++) { - var item = obj[i]; - if (sep) buf.push(","); - if (typeof item == 'function' || typeof item == 'undefined') { - buf.push("null"); - } else { - toJsonArray(buf, item, pretty, stack); - } - sep = true; - } - buf.push("]"); - } else if (obj instanceof Date) { - buf.push(angular['String']['quoteUnicode'](angular['Date']['toString'](obj))); - } else { - buf.push("{"); - if (pretty) buf.push(pretty); - var comma = false; - var childPretty = pretty ? pretty + " " : false; - var keys = []; - for(var k in obj) { - if (k.indexOf('$$') === 0) - continue; - keys.push(k); - } - keys.sort(); - for ( var keyIndex = 0; keyIndex < keys.length; keyIndex++) { - var key = keys[keyIndex]; - try { - var value = obj[key]; - if (typeof value != 'function') { - if (comma) { - buf.push(","); - if (pretty) buf.push(pretty); - } - buf.push(angular['String']['quote'](key)); - buf.push(":"); - toJsonArray(buf, value, childPretty, stack); - comma = true; - } - } catch (e) { - } - } - buf.push("}"); - } - } - if (typeof obj == "object") { - stack.pop(); - } -}; -// Single $ is special and does not get searched -// Double $$ is special an is client only (does not get sent to server) - -function Model(entity, initial) { - this['$$entity'] = entity; - this['$loadFrom'](initial||{}); - this['$entity'] = entity['title']; - this['$migrate'](); -}; - -Model.copyDirectFields = function(src, dst) { - if (src === dst || !src || !dst) return; - var isDataField = function(src, dst, field) { - return (field.substring(0,2) !== '$$') && - (typeof src[field] !== 'function') && - (typeof dst[field] !== 'function'); - }; - for (var field in dst) { - if (isDataField(src, dst, field)) - delete dst[field]; - } - for (field in src) { - if (isDataField(src, dst, field)) - dst[field] = src[field]; - } -}; - -extend(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; - this.tokens = []; - this.index = 0; -}; - -Lexer.OPERATORS = { - 'null':function(self){return null;}, - 'true':function(self){return true;}, - 'false':function(self){return false;}, - '+':function(self, a,b){return (a||0)+(b||0);}, - '-':function(self, a,b){return (a||0)-(b||0);}, - '*':function(self, a,b){return a*b;}, - '/':function(self, a,b){return a/b;}, - '%':function(self, a,b){return a%b;}, - '^':function(self, a,b){return a^b;}, - '=':function(self, a,b){return self.scope.set(a, b);}, - '==':function(self, a,b){return a==b;}, - '!=':function(self, a,b){return a!=b;}, - '<':function(self, a,b){return a<b;}, - '>':function(self, a,b){return a>b;}, - '<=':function(self, a,b){return a<=b;}, - '>=':function(self, a,b){return a>=b;}, - '&&':function(self, a,b){return a&&b;}, - '||':function(self, a,b){return a||b;}, - '&':function(self, a,b){return a&b;}, -// '|':function(self, a,b){return a|b;}, - '|':function(self, a,b){return b(self, a);}, - '!':function(self, a){return !a;} -}; -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; - } - }, - - 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}); - this.index++; - } else { - tokens.push({index:this.index, text:ch}); - } - this.index++; - canStartRegExp = true; - } else if (ch == ')' || ch == ']' || ch == '}' ) { - tokens.push({index:this.index, text:ch}); - this.index++; - canStartRegExp = false; - } else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') { - tokens.push({index:this.index, text:ch}); - this.index++; - canStartRegExp = true; - } else if ( canStartRegExp && ch == '/' ) { - this.readRegexp(); - canStartRegExp = false; - } else if ( this.isNumber(ch) ) { - this.readNumber(); - canStartRegExp = false; - } else if (this.isIdent(ch)) { - this.readIdent(); - canStartRegExp = false; - } else if (this.isWhitespace(ch)) { - this.index++; - } else { - var ch2 = ch + this.peek(); - var fn = OPERATORS[ch]; - var fn2 = OPERATORS[ch2]; - if (fn2) { - tokens.push({index:this.index, text:ch2, fn:fn2}); - this.index += 2; - } else if (fn) { - tokens.push({index:this.index, text:ch, fn:fn}); - this.index += 1; - } else { - throw "Lexer Error: Unexpected next character [" + - this.text.substring(this.index) + - "] in expression '" + this.text + - "' at column '" + (this.index+1) + "'."; - } - canStartRegExp = true; - } - } - return tokens; - }, - - 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 { - break; - } - this.index++; - } - 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++; - } - 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 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; - } else { - string += ch; - } - } - escape = false; - } else if (ch == '\\') { - escape = true; - } else if (ch == quote) { - this.index++; - this.tokens.push({index:start, text:string, - fn:function(){ - return (string.length == dateParseLength) ? - angular['String']['toDate'](string) : string; - }}); - return; - } else { - string += ch; - } - this.index++; - } - throw "Lexer Error: Unterminated quote [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; - }, - - readRegexp: function(quote) { - var start = this.index; - this.index++; - var regexp = ""; - var escape = false; - while (this.index < this.text.length) { - var ch = this.text.charAt(this.index); - if (escape) { - regexp += ch; - escape = false; - } else if (ch === '\\') { - regexp += ch; - escape = true; - } else if (ch === '/') { - this.index++; - var flags = ""; - if (this.isIdent(this.text.charAt(this.index))) { - this.readIdent(); - flags = this.tokens.pop().text; - } - var compiledRegexp = new RegExp(regexp, flags); - this.tokens.push({index:start, text:regexp, flags:flags, - fn:function(){return compiledRegexp;}}); - return; - } else { - regexp += ch; - } - this.index++; - } - throw "Lexer Error: Unterminated RegExp [" + - this.text.substring(start) + "] starting at column '" + - (start+1) + "' in expression '" + this.text + "'."; - } -}; - -///////////////////////////////////////// - -function Parser(text, parseStrings){ - this.text = text; - this.tokens = new Lexer(text, parseStrings).parse(); - this.index = 0; -}; - -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) + "'."; - }, - - 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; - }, - - 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) + "'."; - } - }, - - _unary: function(fn, right) { - return function(self) { - return fn(self, right(self)); - }; - }, - - _binary: function(left, fn, right) { - 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 + "'."; - } - }, - - 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; - }; - } - } - }, - - 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(angularFilter); - }, - - validator: function(){ - return this._pipeFunction(angularValidator); - }, - - _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)); - } - var pipeThis = function(){ - var _this = this; - foreach(self, function(v, k) { - if (k.charAt(0) == '$') { - _this[k] = v; - } - }); - }; - pipeThis.prototype = self.self; - return fn.apply(new pipeThis(), 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 this.assignment(); - } - }, - - 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; - } - }, - - 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.equality(); - var token; - if ((token = this.expect('&&'))) { - left = this._binary(left, token.fn, this.logicalAND()); - } - return left; - }, - - equality: function(){ - var left = this.relational(); - var token; - if ((token = this.expect('==','!='))) { - left = this._binary(left, token.fn, this.equality()); - } - return left; - }, - - relational: function(){ - var left = this.additive(); - var token; - if (token = this.expect('<', '>', '<=', '>=')) { - left = this._binary(left, token.fn, this.relational()); - } - 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.unary()); - } else if (token = this.expect('!')) { - return this._unary(token.fn, this.unary()); - } else { - return this.primary(); - } - }, - - 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; - }, - - primary: function() { - var primary; - if (this.expect('(')) { - var expression = this.filterChain(); - this.consume(')'); - primary = expression; - } else if (this.expect('[')) { - primary = this.arrayDeclaration(); - } else if (this.expect('{')) { - primary = this.object(); - } else if (this.expect('{:')) { - primary = this.closure(false); - } else if (this.expect('{(')) { - primary = this.closure(true); - } else { - var token = this.expect(); - primary = token.fn; - if (!primary) { - this.error("not a primary expression", token); - } - } - var next; - while (next = this.expect('(', '[', '.')) { - if (next.text === '(') { - primary = this.functionCall(primary); - } else if (next.text === '[') { - primary = this.objectIndex(primary); - } else if (next.text === '.') { - primary = this.fieldAccess(primary); - } else { - throw "IMPOSSIBLE"; - } - } - return primary; - }, - - closure: function(hasArgs) { - var args = []; - if (hasArgs) { - if (!this.expect(')')) { - args.push(this.expect().text); - while(this.expect(',')) { - args.push(this.expect().text); - } - this.consume(')'); - } - this.consume(":"); - } - var statements = this.statements(); - this.consume("}"); - return function(self){ - return function($){ - var scope = new 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."; - } - }; - }, - - // 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){ - 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(',')); - } - 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(); - } - } - 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 Entity = self.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); - } - }; - }, - - 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}; - }; - } -}; - - -function Scope(initialState, name) { - this.widgets = []; - this.watchListeners = {}; - this.name = name; - initialState = initialState || {}; - var State = function(){}; - State.prototype = initialState; - this.state = new State(); - this.state['$parent'] = initialState; - if (name == "ROOT") { - this.state['$root'] = this.state; - } -}; - -Scope.expressionCache = {}; -Scope.getter = function(instance, path) { - if (!path) return instance; - var element = path.split('.'); - var key; - var lastInstance = instance; - var len = element.length; - for ( var i = 0; i < len; i++) { - key = element[i]; - if (!key.match(/^[\$\w][\$\w\d]*$/)) - throw "Expression '" + path + "' is not a valid expression for accesing variables."; - if (instance) { - lastInstance = instance; - instance = instance[key]; - } - if (_.isUndefined(instance) && key.charAt(0) == '$') { - var type = angular['Global']['typeOf'](lastInstance); - type = angular[type.charAt(0).toUpperCase()+type.substring(1)]; - var fn = type ? type[[key.substring(1)]] : undefined; - if (fn) { - instance = _.bind(fn, lastInstance, lastInstance); - return instance; - } - } - } - if (typeof instance === 'function' && !instance['$$factory']) { - return bind(lastInstance, instance); - } - return instance; -}; - -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; - }, - - get: function(path) { -// log('SCOPE.get', path, Scope.getter(this.state, path)); - return Scope.getter(this.state, path); - }, - - set: function(path, value) { -// log('SCOPE.set', 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; - }, - - setEval: function(expressionText, value) { - this.eval(expressionText + "=" + toJson(value)); - }, - - eval: function(expressionText, context) { -// log('Scope.eval', expressionText); - 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; - context.self = this.state; - 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; - } - }, - - validate: function(expressionText, value, element) { - var expression = Scope.expressionCache[expressionText]; - if (!expression) { - expression = new Parser(expressionText).validator(); - Scope.expressionCache[expressionText] = expression; - } - var self = {scope:this, self:this.state, '$element':element}; - return expression(self)(self, value); - }, - - entity: function(entityDeclaration, datastore) { - var expression = new Parser(entityDeclaration).entityDeclaration(); - return expression({scope:this, datastore:datastore}); - }, - - clearInvalid: function() { - var invalid = this.state['$invalidWidgets']; - while(invalid.length > 0) {invalid.pop();} - }, - - 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; - } - 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; - this.uuid = "_" + ("" + Math.random()).substr(2) + "_"; - this.maxSize = 1800; -}; - -Server.prototype = { - base64url: function(txt) { - return Base64.encode(txt); - }, - - request: function(method, url, request, callback) { - var requestId = this.uuid + (this.nextId++); - var payload = this.base64url(toJson({'u':url, 'm':method, 'p':request})); - var totalPockets = Math.ceil(payload.length / this.maxSize); - var baseUrl = this.url + "/$/" + requestId + "/" + totalPockets + "/"; - angularCallbacks[requestId] = function(response) { - delete angularCallbacks[requestId]; - callback(200, response); - }; - for ( var pocketNo = 0; pocketNo < totalPockets; pocketNo++) { - var pocket = payload.substr(pocketNo * this.maxSize, this.maxSize); - this.getScript(baseUrl + (pocketNo+1) + "?h=" + pocket, noop); - } - } -}; - -function FrameServer(frame) { - this.frame = frame; -}; -FrameServer.PREFIX = "$DATASET:"; - -FrameServer.prototype = { - read:function(){ - this.data = fromJson(this.frame.name.substr(FrameServer.PREFIX.length)); - }, - write:function(){ - this.frame.name = FrameServer.PREFIX + toJson(this.data); - }, - request: function(method, url, request, callback) { - //alert(method + " " + url + " " + toJson(request) + " " + toJson(callback)); - } -}; - - -function VisualServer(delegate, status, update) { - this.delegate = delegate; - this.update = update; - this.status = status; -}; - -VisualServer.prototype = { - request:function(method, url, request, callback) { - var self = this; - this.status.beginRequest(request); - this.delegate.request(method, url, request, function() { - self.status.endRequest(); - try { - callback.apply(this, arguments); - } catch (e) { - alert(toJson(e)); - } - self.update(); - }); - } -}; -function Users(server, controlBar) { - this.server = server; - this.controlBar = controlBar; -}; - -extend(Users.prototype, { - 'fetchCurrentUser':function(callback) { - var self = this; - this.server.request("GET", "/account.json", {}, function(code, response){ - self['current'] = response['user']; - callback(response['user']); - }); - }, - - 'logout': function(callback) { - var self = this; - this.controlBar.logout(function(){ - delete self['current']; - (callback||noop)(); - }); - }, - - 'login': function(callback) { - var self = this; - this.controlBar.login(function(){ - self['fetchCurrentUser'](function(){ - (callback||noop)(); - }); - }); - }, - - 'notAuthorized': function(){ - this.controlBar.notAuthorized(); - } -}); -foreach({ - 'regexp': function(value, regexp, msg) { - if (!value.match(regexp)) { - return msg || - "Value does not match expected format " + regexp + "."; - } else { - return null; - } - }, - - 'number': function(value, min, max) { - var num = 1 * value; - if (num == value) { - if (typeof min != 'undefined' && num < min) { - return "Value can not be less than " + min + "."; - } - if (typeof min != 'undefined' && num > max) { - return "Value can not be greater than " + max + "."; - } - return null; - } else { - return "Value is not a number."; - } - }, - - 'integer': function(value, min, max) { - var numberError = angularValidator['number'](value, min, max); - if (numberError) return numberError; - if (!("" + value).match(/^\s*[\d+]*\s*$/) || value != Math.round(value)) { - return "Value is not a whole number."; - } - return null; - }, - - 'date': function(value, min, max) { - if (value.match(/^\d\d?\/\d\d?\/\d\d\d\d$/)) { - return null; - } - return "Value is not a date. (Expecting format: 12/31/2009)."; - }, - - 'ssn': function(value) { - if (value.match(/^\d\d\d-\d\d-\d\d\d\d$/)) { - return null; - } - return "SSN needs to be in 999-99-9999 format."; - }, - - 'email': function(value) { - if (value.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/)) { - return null; - } - return "Email needs to be in username@host.com format."; - }, - - 'phone': function(value) { - if (value.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/)) { - return null; - } - if (value.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/)) { - return null; - } - return "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."; - }, - - 'url': function(value) { - if (value.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/)) { - return null; - } - return "URL needs to be in http://server[:port]/path format."; - }, - - 'json': function(value) { - try { - fromJson(value); - return null; - } catch (e) { - return e.toString(); - } - }, - - 'asynchronous': function(text, asynchronousFn) { - var stateKey = '$validateState'; - var lastKey = '$lastKey'; - var obj = this['$element']; - var stateCache = obj[stateKey] = obj[stateKey] || {}; - var state = stateCache[text]; - var updateView = this['$updateView']; - obj[lastKey] = text; - if (state === undefined) { - // we have never seen this before, Request it - jQuery(obj).addClass('ng-input-indicator-wait'); - state = stateCache[text] = null; - asynchronousFn(text, function(error){ - state = stateCache[text] = error ? error : false; - if (stateCache[obj[lastKey]] !== null) { - jQuery(obj).removeClass('ng-input-indicator-wait'); - } - updateView(); - }); - } - - if (state === null){ - // request in flight, mark widget invalid, but don't show it to user - this['$invalidWidgets'].push(this.$element); - } - return state; - } - -}, function(v,k) {angularValidator[k] = v;}); -function WidgetFactory(serverUrl, database) { - this.nextUploadId = 0; - this.serverUrl = serverUrl; - this.database = database; - if (window['swfobject']) { - this.createSWF = window['swfobject']['createSWF']; - } else { - this.createSWF = function(){ - alert("ERROR: swfobject not loaded!"); - }; - } -}; - -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; - var formatter = angularFormatter[input.attr('ng-format')] || angularFormatter['noop']; - if (type == 'button' || type == 'submit' || type == 'reset' || type == 'image') { - controller = new ButtonController(input[0], exp, formatter); - event = "click"; - bubbleEvent = false; - } else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') { - controller = new TextController(input[0], exp, formatter); - event = "keyup change"; - } else if (type == 'checkbox') { - controller = new CheckboxController(input[0], exp, formatter); - event = "click"; - } else if (type == 'radio') { - controller = new RadioController(input[0], exp, formatter); - event="click"; - } else if (type == 'select-one') { - controller = new SelectController(input[0], exp, formatter); - } else if (type == 'select-multiple') { - controller = new MultiSelectController(input[0], exp, formatter); - } else if (type == 'file') { - controller = this.createFileController(input, exp, formatter); - } else { - throw 'Unknown type: ' + type; - } - input.data('controller', controller); - var updateView = scope.get('$updateView'); - var action = function() { - if (controller.updateModel(scope)) { - var action = jQuery(controller.view).attr('ng-action') || ""; - if (scope.evalWidget(controller, action)) { - 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).parent().data('controller', cntl); - return cntl; - } -}; -///////////////////// -// FileController -/////////////////////// - -function FileController(view, scopeName, uploader, databaseUrl) { - this.view = view; - this.uploader = uploader; - this.scopeName = scopeName; - this.attachmentsPath = databaseUrl + '/_attachments'; - this.value = null; - this.lastValue = undefined; -}; - -angularCallbacks['flashEvent'] = function(id, event, args) { - var object = document.getElementById(id); - var jobject = jQuery(object); - var controller = jobject.parent().data("controller"); - FileController.prototype[event].apply(controller, args); - _.defer(jobject.scope().get('$updateView')); -}; - -FileController.template = function(id) { - return jQuery('<span class="ng-upload-widget">' + - '<input type="checkbox" ng-non-bindable="true"/>' + - '<object id="' + id + '" />' + - '<a></a>' + - '<span/>' + - '</span>'); -}; - -extend(FileController.prototype, { - 'cancel': noop, - 'complete': noop, - 'httpStatus': function(status) { - alert("httpStatus:" + this.scopeName + " status:" + status); - }, - 'ioError': function() { - alert("ioError:" + this.scopeName); - }, - 'open': function() { - alert("open:" + this.scopeName); - }, - 'progress':noop, - 'securityError': function() { - alert("securityError:" + this.scopeName); - }, - '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; - }, - '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 -/////////////////////// -function NullController(view) {this.view = view;}; -NullController.prototype = { - updateModel: function() { return true; }, - updateView: noop -}; -NullController.instance = new NullController(); - - -/////////////////////// -// ButtonController -/////////////////////// -var ButtonController = NullController; - -/////////////////////// -// TextController -/////////////////////// -function TextController(view, exp, formatter) { - this.view = view; - this.formatter = formatter; - this.exp = exp; - this.validator = view.getAttribute('ng-validate'); - this.required = typeof view.attributes['ng-required'] != "undefined"; - this.lastErrorText = null; - this.lastValue = undefined; - this.initialValue = this.formatter['parse'](view.value); - var widget = view.getAttribute('ng-widget'); - if (widget === 'datepicker') { - jQuery(view).datepicker(); - } -}; - -TextController.prototype = { - updateModel: function(scope) { - var value = this.formatter['parse'](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).isEqual(value)) { - view.value = this.formatter['format'](value); - this.lastValue = value; - } - - var isValidationError = false; - view.removeAttribute('ng-error'); - if (this.required) { - isValidationError = !(value && $.trim("" + value).length > 0); - } - var errorText = isValidationError ? "Required Value" : null; - if (!isValidationError && this.validator && value) { - errorText = scope.validate(this.validator, value, view); - isValidationError = !!errorText; - } - if (this.lastErrorText !== errorText) { - this.lastErrorText = isValidationError; - if (errorText && isVisible(view)) { - view.setAttribute('ng-error', errorText); - scope.markInvalid(this); - } - jQuery(view).toggleClass('ng-validation-error', isValidationError); - } - } -}; - -/////////////////////// -// CheckboxController -/////////////////////// -function CheckboxController(view, exp, formatter) { - this.view = view; - this.exp = exp; - this.lastValue = undefined; - this.formatter = formatter; - this.initialValue = this.formatter['parse'](view.checked ? view.value : ""); -}; - -CheckboxController.prototype = { - updateModel: function(scope) { - var input = this.view; - var value = input.checked ? input.value : ''; - value = this.formatter['parse'](value); - value = this.formatter['format'](value); - if (this.lastValue === value) { - return false; - } else { - scope.setEval(this.exp, this.formatter['parse'](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 = this.formatter['parse'](input.value) == value; - } -}; - -/////////////////////// -// SelectController -/////////////////////// -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; - } 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; - } - } -}; - -/////////////////////// -// MultiSelectController -/////////////////////// -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; - }, - - 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; - } - } -}; - -/////////////////////// -// RadioController -/////////////////////// -function RadioController(view, exp) { - this.view = view; - this.exp = exp; - this.lastChecked = undefined; - this.lastValue = undefined; - this.inputValue = view.value; - this.initialValue = view.checked ? view.value : null; -}; - -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 -/////////////////////// -function BindUpdater(view, exp) { - this.view = view; - this.exp = Binder.parseBindings(exp); - this.hasError = false; -}; - -BindUpdater.toText = function(obj) { - var e = escapeHtml; - switch(typeof obj) { - case "string": - case "boolean": - case "number": - return e(obj); - case "function": - return BindUpdater.toText(obj()); - case "object": - if (isNode(obj)) { - return outerHTML(obj); - } else if (obj instanceof angular.filter.Meta) { - switch(typeof obj.html) { - case "string": - case "number": - return obj.html; - case "function": - return obj.html(); - case "object": - if (isNode(obj.html)) - return outerHTML(obj.html); - default: - break; - } - switch(typeof obj.text) { - case "string": - case "number": - return e(obj.text); - case "function": - return e(obj.text()); - default: - break; - } - } - if (obj === null) - return ""; - return e(toJson(obj, true)); - default: - 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, {$element:this.view}, function(value){ - html.push(BindUpdater.toText(value)); - }, function(e, text){ - setHtml(this.view, text); - }); - if (this.hasError) { - return; - } - } else { - html.push(escapeHtml(part)); - } - } - setHtml(this.view, html.join('')); - } -}; - -function BindAttrUpdater(view, attrs) { - this.view = view; - this.attrs = attrs; -}; - -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]); - } - } - var attrValue = attrValues.length ? attrValues.join('') : null; - if(isImage && attrName == 'src' && !attrValue) - attrValue = scope.get('$config.blankImage'); - jNode.attr(attrName, attrValue); - } - } -}; - -function EvalUpdater(view, exp) { - this.view = view; - this.exp = exp; - this.hasError = false; -}; -EvalUpdater.prototype = { - updateModel: noop, - updateView: function(scope) { - scope.evalWidget(this, this.exp); - } -}; - -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(); - } - }); - } -}; - -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(); - } - }); - } -}; - -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; - } - }); - } -}; - -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); - }); - } -}; - -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); - }); - } -}; - -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 -/////////////////////// -function RepeaterUpdater(view, repeaterExpression, template, prefix) { - this.view = view; - this.template = template; - this.prefix = prefix; - this.children = []; - var match = repeaterExpression.match(/^\s*(.+)\s+in\s+(.*)\s*$/); - if (! match) { - throw "Expected ng-repeat in form of 'item in collection' but got '" + - repeaterExpression + "'."; - } - var keyValue = match[1]; - this.iteratorExp = match[2]; - match = keyValue.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/); - if (!match) { - throw "'item' in 'item in collection' should be identifier or (key, value) but get '" + - keyValue + "'."; - } - this.valueExp = match[3] || match[1]; - this.keyExp = match[2]; -}; - -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 childrenLength = this.children.length; - var cursor = this.view; - var time = 0; - var child = null; - var keyExp = this.keyExp; - var valueExp = this.valueExp; - var iteratorCounter = 0; - foreach(iterator, function(value, key){ - if (iteratorCounter < childrenLength) { - // reuse children - child = self.children[iteratorCounter]; - child.scope.set(valueExp, value); - } else { - // grow children - var name = self.prefix + - valueExp + " in " + self.iteratorExp + "[" + iteratorCounter + "]"; - var childScope = new Scope(scope.state, name); - childScope.set('$index', iteratorCounter); - if (keyExp) - childScope.set(keyExp, key); - childScope.set(valueExp, value); - child = { scope:childScope, element:self.template(childScope, self.prefix, iteratorCounter) }; - cursor.after(child.element); - self.children.push(child); - } - cursor = child.element; - var s = new Date().getTime(); - child.scope.updateView(); - time += new Date().getTime() - s; - iteratorCounter++; - }); - // shrink children - for ( var r = childrenLength; r > iteratorCounter; --r) { - this.children.pop().element.remove(); - } - // 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 -////////////////////////////////// - -function PopUp(doc) { - this.doc = doc; -}; - -PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup"; - -PopUp.onOver = function(e) { - PopUp.onOut(); - var jNode = jQuery(this); - jNode.bind(PopUp.OUT_EVENT, PopUp.onOut); - var position = jNode.position(); - var de = document.documentElement; - var w = self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth; - var hasArea = w - position.left; - var width = 300; - var title = jNode.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error..."; - var msg = jNode.attr("ng-error"); - - var x; - var arrowPos = hasArea>(width+75) ? "left" : "right"; - var tip = jQuery( - "<div id='ng-callout' style='width:"+width+"px'>" + - "<div class='ng-arrow-"+arrowPos+"'/>" + - "<div class='ng-title'>"+title+"</div>" + - "<div class='ng-content'>"+msg+"</div>" + - "</div>"); - jQuery("body").append(tip); - if(arrowPos === 'left'){ - x = position.left + this.offsetWidth + 11; - }else{ - x = position.left - (width + 15); - tip.find('.ng-arrow-right').css({left:width+1}); - } - - tip.css({left: x+"px", top: (position.top - 3)+"px"}); - return true; -}; - -PopUp.onOut = function() { - jQuery('#ng-callout'). - unbind(PopUp.OUT_EVENT, PopUp.onOut). - remove(); - return true; -}; - -PopUp.prototype = { - bind: function () { - var self = this; - this.doc.find('.ng-validation-error,.ng-exception'). - live("mouseover", PopUp.onOver); - } -}; - -////////////////////////////////// -// Status -////////////////////////////////// - -function NullStatus(body) { -}; - -NullStatus.prototype = { - beginRequest:function(){}, - endRequest:function(){} -}; - -function Status(body) { - this.requestCount = 0; - this.body = body; -}; - -Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>'; - -Status.prototype = { - beginRequest: function () { - if (this.requestCount === 0) { - (this.loader = this.loader || this.body.append(Status.DOM).find("#ng-loading")).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/example/memoryLeak.html b/example/memoryLeak.html index 35169da8..bdfe3faf 100644 --- a/example/memoryLeak.html +++ b/example/memoryLeak.html @@ -6,17 +6,50 @@ <script type="text/javascript" src="../src/angular-bootstrap.js"></script> <script type="text/javascript"> $(document).ready(function(){ - angular.compile(document).init(); + var scope = angular.compile(document); + scope.init(); + scope.set("add", function(){ + var partial = $("#partial"); + //id++; + partial.html('<div>{{ error() }}<br/></div>'); + var scope = angular.compile(partial); + scope.set("hello", function (){ + return 'who dat?'; + }); + scope.set("error", function (){ + this.misko.length; + }); + function XXXXXXXXX(){}; + scope.set('xxx', new XXXXXXXXX()); + scope.set("names", ["adam", "misko", "shyam"]); + scope.init(); + }); + scope.set("remove", function(){ + var partial = $("#partial"); + /* + partial.find('*').andSelf().each(function(){ + var scope = $(this).data('scope'); + if (scope) { + delete scope.state; + delete scope.widgets; + } + var cntl = $(this).data('controller'); + if (cntl) { + delete cntl.view; + } + $(this).removeData(); + }); + */ + partial.removeData('scope'); + partial.children().remove(); + }); }); </script> <link rel="StyleSheet" type="text/css" href="../css/angular.css"/> </head> <body> - Filter: <input name="filterText"> - <ul ng-init="names=['misko']"> - <li ng-repeat="name in names.$filter(filterText)"> - <a href="" ng-action="greet(name)">{{name}}</a> - </li> - </ul> + <input type="button" value="add" ng-action="add()"/> + <input type="button" value="remove" ng-action="remove()"/> + <div id="partial"></div> </body> </html> diff --git a/jsTestDriver.conf b/jsTestDriver.conf index 8b36e7d4..c048a294 100644 --- a/jsTestDriver.conf +++ b/jsTestDriver.conf @@ -1,7 +1,7 @@ server: http://localhost:9876 load: - - lib/jasmine/jasmine-0.10.0.js + - lib/jasmine/jasmine-0.10.1.js - lib/jasmine-jstd-adapter/JasmineAdapter.js - lib/webtoolkit/webtoolkit.base64.js - lib/jquery/jquery-1.4.js diff --git a/lib/jasmine/jasmine-0.10.0.js b/lib/jasmine/jasmine-0.10.1.js index bb7547e8..f9bd7d6f 100644 --- a/lib/jasmine/jasmine-0.10.0.js +++ b/lib/jasmine/jasmine-0.10.1.js @@ -15,7 +15,7 @@ jasmine.unimplementedMethod_ = function() { /** * Use <code>jasmine.undefined</code> instead of <code>undefined</code>, since <code>undefined</code is just * a plain old variable and may be redefined by somebody else. - * + * * @private */ jasmine.undefined = jasmine.___undefined___; @@ -66,7 +66,7 @@ jasmine.ExpectationResult = function(params) { /** @deprecated */ this.details = params.details; - + this.message = this.passed_ ? 'Passed.' : params.message; this.trace = this.passed_ ? '' : new Error(this.message); }; @@ -89,11 +89,7 @@ jasmine.getEnv = function() { * @returns {Boolean} */ jasmine.isArray_ = function(value) { - return value && - typeof value === 'object' && - typeof value.length === 'number' && - typeof value.splice === 'function' && - !(value.propertyIsEnumerable('length')); + return Object.prototype.toString.apply(value) === '[object Array]'; }; /** @@ -350,6 +346,9 @@ jasmine.isSpy = function(putativeSpy) { * @param {Array} methodNames array of names of methods to make spies */ jasmine.createSpyObj = function(baseName, methodNames) { + if (!jasmine.isArray_(methodNames) || methodNames.length == 0) { + throw new Error('createSpyObj requires a non-empty array of method names to create spies for'); + } var obj = {}; for (var i = 0; i < methodNames.length; i++) { obj[methodNames[i]] = jasmine.createSpy(baseName + '.' + methodNames[i]); @@ -1002,10 +1001,11 @@ jasmine.JsApiReporter.prototype.summarizeResult_ = function(result){ * @param actual * @param {jasmine.Spec} spec */ -jasmine.Matchers = function(env, actual, spec) { +jasmine.Matchers = function(env, actual, spec, opt_isNot) { this.env = env; this.actual = actual; this.spec = spec; + this.isNot = opt_isNot || false; this.reportWasCalled_ = false; }; @@ -1015,8 +1015,12 @@ jasmine.Matchers.pp = function(str) { /** @deprecated */ jasmine.Matchers.prototype.report = function(result, failing_message, details) { -// todo first: report deprecation warning [xw] -// todo later: throw new Error("As of jasmine 0.xx, custom matchers must be implemented differently -- please see jasmine docs"); + // todo: report a deprecation warning [xw] + + if (this.isNot) { + throw new Error("As of jasmine 0.11, custom matchers must be implemented differently -- please see jasmine docs"); + } + this.reportWasCalled_ = true; var expectationResult = new jasmine.ExpectationResult({ passed: result, @@ -1039,15 +1043,23 @@ jasmine.Matchers.matcherFn_ = function(matcherName, matcherFunction) { return function() { var matcherArgs = jasmine.util.argsToArray(arguments); var result = matcherFunction.apply(this, arguments); + + if (this.isNot) { + result = !result; + } + if (this.reportWasCalled_) return result; - + var message; if (!result) { if (this.message) { message = this.message.apply(this, arguments); + if (jasmine.isArray_(message)) { + message = message[this.isNot ? 1 : 0]; + } } else { var englishyPredicate = matcherName.replace(/[A-Z]/g, function(s) { return ' ' + s.toLowerCase(); }); - message = "Expected " + jasmine.pp(this.actual) + " " + englishyPredicate; + message = "Expected " + jasmine.pp(this.actual) + (this.isNot ? " not " : " ") + englishyPredicate; if (matcherArgs.length > 0) { for (var i = 0; i < matcherArgs.length; i++) { if (i > 0) message += ","; @@ -1204,31 +1216,32 @@ jasmine.Matchers.prototype.wasNotCalled = function() { * */ jasmine.Matchers.prototype.wasCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); } - this.message = function() { if (this.actual.callCount == 0) { - return "Expected spy to have been called with " + jasmine.pp(arguments) + " but it was never called."; + return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but it was never called."; } else { - return "Expected spy to have been called with " + jasmine.pp(arguments) + " but was called with " + jasmine.pp(this.actual.argsForCall); + return "Expected spy to have been called with " + jasmine.pp(expectedArgs) + " but was called with " + jasmine.pp(this.actual.argsForCall); } }; - return this.env.contains_(this.actual.argsForCall, jasmine.util.argsToArray(arguments)); + return this.env.contains_(this.actual.argsForCall, expectedArgs); }; jasmine.Matchers.prototype.wasNotCalledWith = function() { + var expectedArgs = jasmine.util.argsToArray(arguments); if (!jasmine.isSpy(this.actual)) { throw new Error('Expected a spy, but got ' + jasmine.Matchers.pp(this.actual) + '.'); } this.message = function() { - return "Expected spy not to have been called with " + jasmine.pp(arguments) + " but it was"; + return "Expected spy not to have been called with " + jasmine.pp(expectedArgs) + " but it was"; }; - return !this.env.contains_(this.actual.argsForCall, jasmine.util.argsToArray(arguments)); + return !this.env.contains_(this.actual.argsForCall, expectedArgs); }; /** @@ -1818,7 +1831,9 @@ jasmine.Spec.prototype.addMatcherResult = function(result) { }; jasmine.Spec.prototype.expect = function(actual) { - return new (this.getMatchersClass_())(this.env, actual, this); + var positive = new (this.getMatchersClass_())(this.env, actual, this); + positive.not = new (this.getMatchersClass_())(this.env, actual, this, true); + return positive; }; jasmine.Spec.prototype.waits = function(timeout) { @@ -2256,6 +2271,6 @@ window.clearInterval = function(timeoutKey) { jasmine.version_= { "major": 0, "minor": 10, - "build": 0, - "revision": 1261768736 + "build": 1, + "revision": 1267503060 }; diff --git a/src/Angular.js b/src/Angular.js index 6cb3f602..9b3634be 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -20,22 +20,23 @@ if (typeof Node == 'undefined') { function noop() {} if (!window['console']) window['console']={'log':noop, 'error':noop}; -var consoleNode, msie, +var consoleNode, msie, jQuery = window['jQuery'] || window['$'], // weirdness to make IE happy foreach = _.each, extend = _.extend, identity = _.identity, - angular = window['angular'] || (window['angular'] = {}), - angularValidator = angular['validator'] || (angular['validator'] = {}), - angularFilter = angular['filter'] || (angular['filter'] = {}), - angularFormatter = angular['formatter'] || (angular['formatter'] = {}), + angular = window['angular'] || (window['angular'] = {}), + angularValidator = angular['validator'] || (angular['validator'] = {}), + angularFilter = angular['filter'] || (angular['filter'] = {}), + angularFormatter = angular['formatter'] || (angular['formatter'] = {}), angularCallbacks = angular['callbacks'] || (angular['callbacks'] = {}), angularAlert = angular['alert'] || (angular['alert'] = function(){ - log(arguments); window.alert.apply(window, arguments); + log(arguments); window.alert.apply(window, arguments); }); +angular['copy'] = copy; var isVisible = isVisible || function (element) { - return jQuery(element).is(":visible"); + return jQuery(element).is(":visible"); } function log(a, b, c){ @@ -101,6 +102,29 @@ function isLeafNode (node) { } } +function copy(source, destination){ + if (!destination) { + if (!source) { + return source; + } else if (_.isArray(source)) { + return copy(source, []); + } else { + return copy(source, {}); + } + } else { + if (_.isArray(source)) { + while(destination.length) { + destination.pop(); + } + } else { + _(destination).each(function(value, key){ + delete destination[key]; + }); + } + return $.extend(true, destination, source); + } +}; + function setHtml(node, html) { if (isLeafNode(node)) { if (msie) { @@ -218,7 +242,7 @@ UrlWatcher.prototype = { }; pull(); }, - + set: function(url) { var existingURL = this.location.href; if (!existingURL.match(/#/)) @@ -227,7 +251,7 @@ UrlWatcher.prototype = { this.location.href = url; this.existingURL = url; }, - + get: function() { return window.location.href; } @@ -326,10 +350,10 @@ function wireAngular(element, config) { binder.entity(scope); binder.compile(); controlBar.bind(); - + //TODO: remove this code new PopUp(element).bind(); - + var self = _(exposeMethods(scope, { 'set': scope.set, 'get': scope.get, @@ -338,8 +362,8 @@ function wireAngular(element, config) { 'init':function(){ config['location']['listen'](_(binder.onUrlChange).bind(binder)); binder.parseAnchor(); - binder.executeInit(); - binder.updateView(); + binder.executeInit(); + binder.updateView(); return self; }, 'element':element[0], @@ -349,7 +373,7 @@ function wireAngular(element, config) { return self; } -angular['startUrlWatcher'] = function(){ +angular['startUrlWatcher'] = function(){ var watcher = new UrlWatcher(window['location']); watcher.watch(); return exposeMethods(watcher, {'listen':watcher.listen, 'set':watcher.set, 'get':watcher.get}); @@ -366,6 +390,6 @@ angular['compile'] = function(element, config) { configureLogging(config); configureJQueryPlugins(); - + return wireAngular(jQuery(element), config); }; diff --git a/src/Resource.js b/src/Resource.js new file mode 100644 index 00000000..c0c6d220 --- /dev/null +++ b/src/Resource.js @@ -0,0 +1,117 @@ +function Route(template, defaults) { + this.template = template = template + '#'; + this.defaults = defaults || {}; + var urlParams = this.urlParams = {}; + foreach(template.split(/\W/), function(param){ + if (param && template.match(new RegExp(":" + param + "\\W"))) { + urlParams[param] = true; + } + }); +} + +Route.prototype = { + url: function(params) { + var path = []; + var self = this; + var url = this.template; + params = params || {}; + foreach(this.urlParams, function(value, urlParam){ + var value = params[urlParam] || self.defaults[urlParam] || ""; + url = url.replace(new RegExp(":" + urlParam + "(\\W)"), value + "$1"); + }); + url = url.replace(/\/?#$/, ''); + var query = []; + foreach(params, function(value, key){ + if (!self.urlParams[key]) { + query.push(encodeURI(key) + '=' + encodeURI(value)); + } + }); + return url + (query.length ? '?' + query.join('&') : ''); + } +}; + +function ResourceFactory(xhr) { + this.xhr = xhr; +} + +ResourceFactory.DEFAULT_ACTIONS = { + 'get': {method:'GET'}, + 'save': {method:'POST'}, + 'query': {method:'GET', isArray:true}, + 'remove': {method:'DELETE'}, + 'delete': {method:'DELETE'} +}; + +ResourceFactory.prototype = { + route: function(url, idPaths, actions){ + var self = this; + var route = new Route(url); + actions = $.extend({}, ResourceFactory.DEFAULT_ACTIONS, actions); + function extractIds(data){ + var ids = {}; + foreach(idPaths, function(path, id){ + ids[id] = Scope.getter(data, path); + }); + return ids; + } + + function Resource(value){ + copy(value || {}, this); + }; + + foreach(actions, function(action, name){ + var isGet = action.method == 'GET'; + var isPost = action.method == 'POST'; + Resource[name] = function (a1, a2, a3) { + var params = {}; + var data; + var callback = noop; + switch(arguments.length) { + case 3: callback = a3; + case 2: + if (typeof a2 == 'function') { + callback = a2; + } else { + params = a1; + data = a2; + break; + } + case 1: if (isPost) data = a1; else params = a1; break; + case 0: break; + default: + throw "Expected between 0-3 arguments [params, data, callback], got " + arguments.length + " arguments." + } + + var value = action.isArray ? [] : new Resource(data); + self.xhr.method(action.method, route.url($.extend({}, action.params || {}, extractIds(data), params)), data, function(response) { + if (action.isArray) { + foreach(response, function(item){ + value.push(new Resource(item)); + }); + } else { + copy(response, value); + } + (callback||noop)(value); + }); + return value; + }; + + if (!isGet) { + Resource.prototype['$' + name] = function(a1, a2){ + var params = {}; + var callback = noop; + switch(arguments.length) { + case 2: params = a1, callback = a2; + case 1: if (typeof a1 == 'function') callback = a1; else params = a1; + case 0: break; + default: + throw "Expected between 1-3 arguments [params, data, callback], got " + arguments.length + " arguments." + } + Resource[name](params, this, callback); + }; + } + }); + return Resource; + } +}; + diff --git a/src/Scope.js b/src/Scope.js index 4de57dd2..daf4b36c 100644 --- a/src/Scope.js +++ b/src/Scope.js @@ -53,11 +53,11 @@ Scope.prototype = { }); }); }, - + 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); @@ -67,12 +67,12 @@ Scope.prototype = { } return true; }, - + get: function(path) { // log('SCOPE.get', path, Scope.getter(this.state, path)); return Scope.getter(this.state, path); }, - + set: function(path, value) { // log('SCOPE.set', path, value); var element = path.split('.'); @@ -89,11 +89,11 @@ Scope.prototype = { instance[element.shift()] = value; return value; }, - + setEval: function(expressionText, value) { this.eval(expressionText + "=" + toJson(value)); }, - + eval: function(expressionText, context) { // log('Scope.eval', expressionText); var expression = Scope.expressionCache[expressionText]; @@ -108,7 +108,7 @@ Scope.prototype = { context.self = this.state; return expression(context); }, - + //TODO: Refactor. This function needs to be an execution closure for widgets // move to widgets // remove expression, just have inner closure. @@ -126,8 +126,8 @@ Scope.prototype = { } return true; } catch (e){ - error('Eval Widget Error:', e); var jsonError = toJson(e, true); + error('Eval Widget Error:', jsonError); widget.hasError = true; jQuery(widget.view). addClass('ng-exception'). @@ -138,7 +138,7 @@ Scope.prototype = { return false; } }, - + validate: function(expressionText, value, element) { var expression = Scope.expressionCache[expressionText]; if (!expression) { @@ -148,21 +148,21 @@ Scope.prototype = { var self = {scope:this, self:this.state, '$element':element}; return expression(self)(self, value); }, - + entity: function(entityDeclaration, datastore) { var expression = new Parser(entityDeclaration).entityDeclaration(); return expression({scope:this, datastore:datastore}); }, - + clearInvalid: function() { var invalid = this.state['$invalidWidgets']; while(invalid.length > 0) {invalid.pop();} }, - + markInvalid: function(widget) { this.state['$invalidWidgets'].push(widget); }, - + watch: function(declaration) { var self = this; new Parser(declaration).watch()({ @@ -178,7 +178,7 @@ Scope.prototype = { } }); }, - + addWatchListener: function(watchExpression, listener) { var watcher = this.watchListeners[watchExpression]; if (!watcher) { @@ -187,7 +187,7 @@ Scope.prototype = { } watcher.listeners.push(listener); }, - + fireWatchers: function() { var self = this; var fired = false; @@ -203,4 +203,4 @@ Scope.prototype = { }); return fired; } -};
\ No newline at end of file +}; diff --git a/test/AngularSpec.js b/test/AngularSpec.js index 65a32279..043f7bf3 100644 --- a/test/AngularSpec.js +++ b/test/AngularSpec.js @@ -7,4 +7,38 @@ describe('Angular', function(){ scope.updateView(); expect(onUpdateView).wasCalled(); }); -});
\ No newline at end of file +}); + +describe("copy", function(){ + it("should return same object", function (){ + var obj = {}; + var arr = []; + assertSame(obj, copy({}, obj)); + assertSame(arr, copy([], arr)); + }); + + it("should copy array", function(){ + var src = [1, {name:"value"}]; + var dst = [{key:"v"}]; + assertSame(dst, copy(src, dst)); + assertEquals([1, {name:"value"}], dst); + assertEquals({name:"value"}, dst[1]); + assertNotSame(src[1], dst[1]); + }); + + it('should copy empty array', function() { + var src = []; + var dst = [{key: "v"}]; + assertEquals([], copy(src, dst)); + assertEquals([], dst); + }); + + it("should copy object", function(){ + var src = {a:{name:"value"}}; + var dst = {b:{key:"v"}}; + assertSame(dst, copy(src, dst)); + assertEquals({a:{name:"value"}}, dst); + assertEquals(src.a, dst.a); + assertNotSame(src.a, dst.a); + }); +}); diff --git a/test/ResourceSpec.js b/test/ResourceSpec.js new file mode 100644 index 00000000..562d6500 --- /dev/null +++ b/test/ResourceSpec.js @@ -0,0 +1,159 @@ +function MockXHR(){ + this.expectations = { + 'GET': {}, + 'POST': {}, + 'DELETE': {} + }; + this.queue = []; +} +MockXHR.prototype = { + method: function(verb, url, data, callback) { + if (verb == 'POST') + url += '|' + angular.toJson(data); + var response = this.expectations[verb][url]; + if (!response) + throw "No expectation for " + verb + " on '" + url + "'."; + this.queue.push(function(){ + callback(response); + }); + }, + + expectGET: function(url) { + var self = this; + return { + respond: function(response){ + self.expectations.GET[url] = response; + } + }; + }, + + expectDELETE: function(url) { + var self = this; + return { + respond: function(response){ + self.expectations.DELETE[url] = response; + } + }; + }, + + expectPOST: function(url) { + var self = this; + return { + data: function(data){ + return { + respond: function(response){ + self.expectations.POST[url + '|' + angular.toJson(data)] = response; + } + }; + } + }; + }, + + flush: function(){ + while(this.queue.length) { + this.queue.shift()(); + } + } +}; + +describe("resource", function() { + var xhr, resource, CreditCard, callback; + + beforeEach(function(){ + xhr = new MockXHR(); + resource = new ResourceFactory(xhr); + CreditCard = resource.route('/CreditCard/:id:verb', {id:'id.key'}, { + charge:{ + method:'POST', + params:{verb:'!charge'} + } + }); + callback = jasmine.createSpy(); + }); + + it("should build resource", function(){ + expect(typeof CreditCard).toBe('function'); + expect(typeof CreditCard.get).toBe('function'); + expect(typeof CreditCard.save).toBe('function'); + expect(typeof CreditCard.remove).toBe('function'); + expect(typeof CreditCard['delete']).toBe('function'); + expect(typeof CreditCard.query).toBe('function'); + }); + + it("should create resource", function(){ + xhr.expectPOST('/CreditCard').data({name:'misko'}).respond({id:123, name:'misko'}); + + var cc = CreditCard.save({name:'misko'}, callback); + nakedExpect(cc).toEqual({name:'misko'}); + expect(callback).wasNotCalled(); + xhr.flush(); + nakedExpect(cc).toEqual({id:123, name:'misko'}); + expect(callback).wasCalledWith(cc); + }); + + it("should read resource", function(){ + xhr.expectGET("/CreditCard/123").respond({id:123, number:'9876'}); + var cc = CreditCard.get({id:123}, callback); + expect(cc instanceof CreditCard).toBeTruthy(); + nakedExpect(cc).toEqual({}); + expect(callback).wasNotCalled(); + xhr.flush(); + nakedExpect(cc).toEqual({id:123, number:'9876'}); + expect(callback).wasCalledWith(cc); + }); + + it("should update resource", function(){ + xhr.expectPOST('/CreditCard/123').data({id:{key:123}, name:'misko'}).respond({id:{key:123}, name:'rama'}); + + var cc = CreditCard.save({id:{key:123}, name:'misko'}, callback); + nakedExpect(cc).toEqual({id:{key:123}, name:'misko'}); + expect(callback).wasNotCalled(); + xhr.flush(); + nakedExpect(cc).toEqual({id:{key:123}, name:'rama'}); + expect(callback).wasCalledWith(cc); + }); + + it("should query resource", function(){ + xhr.expectGET("/CreditCard?key=value").respond([{id:1}, {id:2}]); + + var ccs = CreditCard.query({key:'value'}, callback); + expect(ccs).toEqual([]); + expect(callback).wasNotCalled(); + xhr.flush(); + nakedExpect(ccs).toEqual([{id:1}, {id:2}]); + expect(callback).wasCalledWith(ccs); + }); + + it('should delete resource', function(){ + xhr.expectDELETE("/CreditCard/123").respond({}); + + CreditCard.remove({id:123}, callback); + expect(callback).wasNotCalled(); + xhr.flush(); + nakedExpect(callback.mostRecentCall.args).toEqual([{}]); + }); + + it('should post charge verb', function(){ + xhr.expectPOST('/CreditCard/123!charge?amount=10').data({auth:'abc'}).respond({success:'ok'}); + + CreditCard.charge({id:123, amount:10},{auth:'abc'}, callback); + }); + + it('should create on save', function(){ + xhr.expectPOST('/CreditCard').data({name:'misko'}).respond({id:123}); + var cc = new CreditCard(); + expect(cc.$get).not.toBeDefined(); + expect(cc.$query).not.toBeDefined(); + expect(cc.$remove).toBeDefined(); + expect(cc.$save).toBeDefined(); + + cc.name = 'misko'; + cc.$save(callback); + nakedExpect(cc).toEqual({name:'misko'}); + xhr.flush(); + nakedExpect(cc).toEqual({id:123}); + expect(callback).wasCalledWith(cc); + }); + + +}); diff --git a/test/testabilityPatch.js b/test/testabilityPatch.js index 293553da..e7ebb386 100644 --- a/test/testabilityPatch.js +++ b/test/testabilityPatch.js @@ -10,6 +10,11 @@ msie = jQuery.browser.msie; function noop(){} jstd = jstestdriver; +dump = _(jstd.console.log).bind(jstd.console); + +function nakedExpect(obj) { + return expect(angular.fromJson(angular.toJson(obj))); +}; swfobject = { createSwf:function() { @@ -137,4 +142,4 @@ function assertThrows(error, fn){ } log = noop; -error = noop;
\ No newline at end of file +error = noop; |
