angular.js

This commit is contained in:
Adam Abrons 2010-01-05 16:36:58 -08:00
commit c9c176a53b
60 changed files with 14559 additions and 0 deletions

2
LICENSE Normal file
View file

@ -0,0 +1,2 @@
TODO
apache or MIT probably

3
README.md Normal file
View file

@ -0,0 +1,3 @@
Angular
======
TODO

74
Rakefile Normal file
View file

@ -0,0 +1,74 @@
include FileUtils
desc 'Compile JavaScript'
task :compile do
compiled = %x(java -jar lib/shrinksafe/shrinksafe.jar \
lib/webtoolkit/webtoolkit.base64.js \
lib/underscore/underscore.js \
src/Loader.js \
src/API.js \
src/Binder.js \
src/ControlBar.js \
src/DataStore.js \
src/Filters.js \
src/JSON.js \
src/Model.js \
src/Parser.js \
src/Scope.js \
src/Server.js \
src/Users.js \
src/Validators.js \
src/Widgets.js \
src/angular-bootstrap.js \
)
f = File.new("angular.js", 'w')
f.write(compiled)
f.close
end
desc 'Compile JavaScript with Google Closure Compiler'
task :compileclosure do
%x(java -jar lib/compiler-closure/compiler.jar \
--compilation_level ADVANCED_OPTIMIZATIONS \
--js lib/webtoolkit/webtoolkit.base64.js \
--js lib/underscore/underscore.js \
--js src/Loader.js \
--js src/API.js \
--js src/Binder.js \
--js src/ControlBar.js \
--js src/DataStore.js \
--js src/Filters.js \
--js src/JSON.js \
--js src/Model.js \
--js src/Parser.js \
--js src/Scope.js \
--js src/Server.js \
--js src/Users.js \
--js src/Validators.js \
--js src/Widgets.js \
--js src/angular-bootstrap.js \
--js_output_file angular.js)
end
namespace :server do
desc 'Run JsTestDriver Server'
task :start do
sh %x(java -jar lib/jstestdriver/JsTestDriver.jar --browser open --port 9876)
end
desc "Run JavaScript tests against the server"
task :test do
sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all)
end
end
desc "Run JavaScript tests"
task :test do
sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all --browser open --port 9876)
end
desc 'Lint'
task :lint do
out = %x(lib/jsl/jsl -conf lib/jsl/jsl.default.conf)
print out
end

128
angular.js vendored Normal file
View file

@ -0,0 +1,128 @@
function r(){return function(a){return a}}function v(){return function(){}}function x(a){return function(b){this[a]=b}}function z(a){return function(){return a}}var A;
function aa(a){var b="",c,e,d,f,i,j,n=0;a=a;a=a.replace(/\r\n/g,"\n");e="";for(d=0;d<a.length;d++){f=a.charCodeAt(d);if(f<128)e+=String.fromCharCode(f);else{if(f>127&&f<2048)e+=String.fromCharCode(f>>6|192);else{e+=String.fromCharCode(f>>12|224);e+=String.fromCharCode(f>>6&63|128)}e+=String.fromCharCode(f&63|128)}}for(a=e;n<a.length;){c=a.charCodeAt(n++);e=a.charCodeAt(n++);d=a.charCodeAt(n++);f=c>>2;c=(c&3)<<4|e>>4;i=(e&15)<<2|d>>6;j=d&63;if(isNaN(e))i=j=64;else if(isNaN(d))j=64;b=b+this.eb.charAt(f)+
this.eb.charAt(c)+this.eb.charAt(i)+this.eb.charAt(j)}return b};(function(){var a=this,b=a.cb;function c(g){this.da=g}var e=typeof StopIteration!=="undefined"?StopIteration:"__break__",d=a.cb=function(g){return new c(g)};if(typeof exports!=="undefined")exports.cb=d;var f=Array.prototype.slice,i=Array.prototype.unshift,j=Object.prototype.toString,n=Object.prototype.hasOwnProperty,m=Object.prototype.propertyIsEnumerable;d.oe="0.5.1";d.a=function(g,h,k){try{if(g.forEach)g.forEach(h,k);else if(d.z(g)||d.dc(g))for(var l=0,p=g.length;l<p;l++)h.call(k,g[l],l,g);else{var s=
d.la(g);p=s.length;for(l=0;l<p;l++)h.call(k,g[s[l]],s[l],g)}}catch(u){if(u!=e)throw u;}return g};d.map=function(g,h,k){if(g&&d.P(g.map))return g.map(h,k);var l=[];d.a(g,function(p,s,u){l.push(h.call(k,p,s,u))});return l};d.reduce=function(g,h,k,l){if(g&&d.P(g.reduce))return g.reduce(d.u(k,l),h);d.a(g,function(p,s,u){h=k.call(l,h,p,s,u)});return h};d.reduceRight=function(g,h,k,l){if(g&&d.P(g.reduceRight))return g.reduceRight(d.u(k,l),h);var p=d.fa(d.D(g)).reverse();d.a(p,function(s,u){h=k.call(l,h,
s,u,g)});return h};d.Ub=function(g,h,k){var l;d.a(g,function(p,s,u){if(h.call(k,p,s,u)){l=p;d.va()}});return l};d.select=function(g,h,k){if(g&&d.P(g.filter))return g.filter(h,k);var l=[];d.a(g,function(p,s,u){h.call(k,p,s,u)&&l.push(p)});return l};d.vf=function(g,h,k){var l=[];d.a(g,function(p,s,u){!h.call(k,p,s,u)&&l.push(p)});return l};d.all=function(g,h,k){h=h||d.ka;if(g&&d.P(g.every))return g.every(h,k);var l=true;d.a(g,function(p,s,u){(l=l&&h.call(k,p,s,u))||d.va()});return l};d.Uc=function(g,
h,k){h=h||d.ka;if(g&&d.P(g.some))return g.some(h,k);var l=false;d.a(g,function(p,s,u){if(l=h.call(k,p,s,u))d.va()});return l};d.Aa=function(g,h){if(d.z(g))return d.indexOf(g,h)!=-1;var k=false;d.a(g,function(l){if(k=l===h)d.va()});return k};d.Ze=function(g,h){var k=d.Q(arguments,2);return d.map(g,function(l){return(h?l[h]:l).apply(l,k)})};d.vb=function(g,h){return d.map(g,function(k){return k[h]})};d.max=function(g,h,k){if(!h&&d.z(g))return Math.max.apply(Math,g);var l={ga:-Infinity};d.a(g,function(p,
s,u){s=h?h.call(k,p,s,u):p;s>=l.ga&&(l={value:p,ga:s})});return l.value};d.min=function(g,h,k){if(!h&&d.z(g))return Math.min.apply(Math,g);var l={ga:Infinity};d.a(g,function(p,s,u){s=h?h.call(k,p,s,u):p;s<l.ga&&(l={value:p,ga:s})});return l.value};d.Rd=function(g,h,k){return d.vb(d.map(g,function(l,p,s){return{value:l,Sb:h.call(k,l,p,s)}}).sort(function(l,p){l=l.Sb;p=p.Sb;return l<p?-1:l>p?1:0}),"value")};d.yf=function(g,h,k){k=k||d.ka;for(var l=0,p=g.length;l<p;){var s=l+p>>1;k(g[s])<k(h)?(l=s+1):
(p=s)}return l};d.D=function(g){if(!g)return[];if(g.D)return g.D();if(d.z(g))return g;if(d.dc(g))return f.call(g);return d.map(g,r())};d.size=function(g){return d.D(g).length};d.jd=function(g,h,k){return h&&!k?f.call(g,0,h):g[0]};d.Q=function(g,h,k){return f.call(g,d.K(h)||k?1:h)};d.ud=function(g){return g[g.length-1]};d.compact=function(g){return d.select(g,function(h){return!!h})};d.kd=function(g){return d.reduce(g,[],function(h,k){if(d.z(k))return h.concat(d.kd(k));h.push(k);return h})};d.Mf=function(g){var h=
d.Q(arguments);return d.select(g,function(k){return!d.Aa(h,k)})};d.Wd=function(g,h){return d.reduce(g,[],function(k,l,p){if(0==p||(h===true?d.ud(k)!=l:!d.Aa(k,l)))k.push(l);return k})};d.Ye=function(g){var h=d.Q(arguments);return d.select(d.Wd(g),function(k){return d.all(h,function(l){return d.indexOf(l,k)>=0})})};d.Pf=function(){for(var g=d.D(arguments),h=d.max(d.vb(g,"length")),k=new Array(h),l=0;l<h;l++)k[l]=d.vb(g,String(l));return k};d.indexOf=function(g,h){if(g.indexOf)return g.indexOf(h);for(var k=
0,l=g.length;k<l;k++)if(g[k]===h)return k;return-1};d.lastIndexOf=function(g,h){if(g.lastIndexOf)return g.lastIndexOf(h);for(var k=g.length;k--;)if(g[k]===h)return k;return-1};d.Md=function(g,h,k){var l=d.D(arguments),p=l.length<=1;g=p?0:l[0];h=p?l[0]:l[1];k=l[2]||1;l=Math.ceil((h-g)/k);if(l<=0)return[];l=new Array(l);p=g;for(var s=0;;p+=k){if((k>0?p-h:h-p)>=0)return l;l[s++]=p}};d.u=function(g,h){var k=d.Q(arguments,2);return function(){return g.apply(h||a,k.concat(d.D(arguments)))}};d.we=function(g){var h=
d.Q(arguments);if(h.length==0)h=d.nb(g);d.a(h,function(k){g[k]=d.u(g[k],g)});return g};d.lb=function(g,h){var k=d.Q(arguments,2);return setTimeout(function(){return g.apply(g,k)},h)};d.defer=function(g){return d.lb.apply(d,[g,1].concat(d.Q(arguments)))};d.Of=function(g,h){return function(){var k=[g].concat(d.D(arguments));return h.apply(h,k)}};d.Ie=function(){var g=d.D(arguments);return function(){for(var h=d.D(arguments),k=g.length-1;k>=0;k--)h=[g[k].apply(this,h)];return h[0]}};d.la=function(g){if(d.z(g))return d.Md(0,
g.length);var h=[];for(var k in g)n.call(g,k)&&h.push(k);return h};d.Ab=function(g){return d.map(g,d.ka)};d.nb=function(g){return d.select(d.la(g),function(h){return d.P(g[h])}).sort()};d.extend=function(g,h){for(var k in h)g[k]=h[k];return g};d.fa=function(g){if(d.z(g))return g.slice(0);return d.extend({},g)};d.isEqual=function(g,h){if(g===h)return true;var k=typeof g;if(k!=typeof h)return false;if(g==h)return true;if(!g&&h||g&&!h)return false;if(g.isEqual)return g.isEqual(h);if(d.pd(g)&&d.pd(h))return g.getTime()===
h.getTime();if(d.fc(g)&&d.fc(h))return true;if(d.qd(g)&&d.qd(h))return g.source===h.source&&g.global===h.global&&g.ignoreCase===h.ignoreCase&&g.multiline===h.multiline;if(k!=="object")return false;if(g.length&&g.length!==h.length)return false;k=d.la(g);var l=d.la(h);if(k.length!=l.length)return false;for(var p in g)if(!d.isEqual(g[p],h[p]))return false;return true};d.bf=function(g){return d.la(g).length==0};d.af=function(g){return!!(g&&g.nodeType==1)};d.dc=function(g){return g&&"0"<=g.length&&g.length<=
"9"&&!d.z(g)&&!m.call(g,"length")};d.fc=function(g){return"0"<=g&&g<="9"&&isNaN(g)};d.cf=function(g){return g===null};d.K=function(g){return typeof g=="undefined"};for(var o=["Array","Date","Function","Number","RegExp","String"],q=0,w=o.length;q<w;q++)(function(){var g="[object "+o[q]+"]";d["is"+o[q]]=function(h){return j.call(h)==g}})();d.Id=function(){a.cb=b;return this};d.ka=r();d.va=function(){throw e;};var t=0;d.Hf=function(g){var h=t++;return g?g+h:h};d.Z=function(g,h){g=new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+g.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return h?g(h):g};d.forEach=d.a;d.Se=d.We=d.reduce;d.Te=d.reduceRight;d.filter=d.select;d.every=d.all;d.some=d.Uc;d.ac=d.jd;d.Cf=d.Q;d.hf=d.nb;function y(g,h){return h?d(g).Ob():g}d.a(d.nb(d),function(g){var h=
d[g];c.prototype[g]=function(){i.call(arguments,this.da);return y(h.apply(d,arguments),this.db)}});d.a(["pop","push","reverse","shift","sort","splice","unshift"],function(g){var h=Array.prototype[g];c.prototype[g]=function(){h.apply(this.da,arguments);return y(this.da,this.db)}});d.a(["concat","join","slice"],function(g){var h=Array.prototype[g];c.prototype[g]=function(){return y(h.apply(this.da,arguments),this.db)}});c.prototype.Ob=function(){this.db=true;return this};c.prototype.value=function(){return this.da}})();if(typeof document.getAttribute=="undefined")document.getAttribute=v();if(typeof Node=="undefined")Node={ELEMENT_NODE:1,ATTRIBUTE_NODE:2,TEXT_NODE:3,CDATA_SECTION_NODE:4,ENTITY_REFERENCE_NODE:5,ENTITY_NODE:6,PROCESSING_INSTRUCTION_NODE:7,COMMENT_NODE:8,DOCUMENT_NODE:9,DOCUMENT_TYPE_NODE:10,DOCUMENT_FRAGMENT_NODE:11,NOTATION_NODE:12};if(_.K(window.jf))nglr={};if(_.K(window.Lb))angular={};if(_.K(angular.k))angular.k={};if(_.K(angular.filter))angular.filter={};
if(_.K(window.console))window.console={log:v(),error:v()};if(_.K(nglr.alert))nglr.alert=function(){console.log(arguments);window.alert.apply(window,arguments)};nglr.Qb=function(a,b){var c=document.createElement("div");c.className=a;for(var e=a="",d=0;d<b.length;d++){var f=b[d];a+=e+(typeof f=="string"?f:nglr.m(f));e=" "}c.appendChild(document.createTextNode(a));nglr.hb.appendChild(c)};nglr.gc=function(a){return a&&a.tagName&&a.nodeName&&a.ownerDocument&&a.removeAttribute};
nglr.ec=function(a){switch(a.nodeName){case "OPTION":case "PRE":case "TITLE":return true;default:return false}};nglr.L=v();nglr.yc=function(a,b){if(nglr.ec(a))if(nglr.Fa)a.innerText=b;else a.textContent=b;else a.innerHTML=b};nglr.mb=function(a){if(!a||!a.replace)return a;return a.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")};nglr.hd=function(a){if(!a||!a.replace)return a;return a.replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\"/g,"&quot;")};
nglr.u=function(a,b){if(!a)throw"Missing this";if(!_.P(b))throw"Missing function";return function(){return b.apply(a,arguments)}};nglr.Qd=function(a,b){return function(){for(var c=[this],e=0;e<arguments.length;e++)c.push(arguments[e]);return b.apply(a,c)}};nglr.outerHTML=function(a){var b=document.createElement("div");b.appendChild(a);var c=b.innerHTML;b.removeChild(a);return c};nglr.Td=function(a){return a.replace(/^ */,"").replace(/ *$/,"")};
nglr.xb=function(a){var b=(""+a).toLowerCase();if(b=="f"||b=="0"||b=="false"||b=="no")a=false;return!!a};nglr.Ea=function(a,b){for(var c in a){var e=b[c],d=typeof e;if(d=="undefined")b[c]=nglr.za(nglr.m(a[c]));else d=="object"&&e.constructor!=nglr.Mb&&c.substring(0,1)!="$"&&nglr.Ea(a[c],e)}};nglr.Va=function(a,b,c){this.document=jQuery(a);this.ac=jQuery(b);this.w=c;this.location=window.location};
nglr.Va.prototype.load=function(){ba(this);B(this,"/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css");B(this,"/stylesheets/nglr.css");console.log("Server: "+this.w.C);jQuery.Id();nglr.Fa=jQuery.xe.Fa;ca(this);var a=this.w;if(!a.X){var b=a.C.match(/https?:\/\/([\w]*)/);a.X=b?b[1]:"$MEMORY"}da(this)};
function ca(){console.log("Loader.configureJQueryPlugins()");jQuery.b.removeNode=function(){var a=this.i(0);a.parentNode.removeChild(a)};jQuery.b.scope=function(){for(var a=this;a&&a.i(0);){var b=a.data("scope");if(b)return b;a=a.parent()}return null};jQuery.b.Je=function(){return this.data("controller")||nglr.ba.od}}nglr.Va.prototype.Vd=function(){return""+(new Date).getTime()};
function da(a){function b(){j.Yb(function(q){!q&&e.find("[ng-auth=eager]").length&&j.Da()})}console.log("Loader.bindHtml()");var c=new nglr.Hb(a.location),e=a.document,d=new nglr.Qc(a.w.C,a.w.X),f=new nglr.g(e[0],d,c,a.w);d.Jd=nglr.Qd(f,f.j);d=new nglr.M(e.find("body"),a.w.C);var i=a.w.X=="$MEMORY"?new nglr.ta(a.window):new nglr.Gb(a.w.C,jQuery.$b);i=new nglr.Jb(i,new nglr.$a(jQuery(e.body)),function(){f.c()});var j=new nglr.Ib(i,d),n="/data/"+a.w.X,m=new nglr.p(function(q,w){i.oa("POST",n,q,w)},
j,f.anchor);f.Cc.push(function(){C(m)});var o=new nglr.h({ce:f.anchor,de:f,ee:a.w,fe:window.console,ge:m,sa:function(q){ea(m,o.I,q,f.anchor)},me:window,ke:a.Vd,le:j},"ROOT");jQuery.a(["get","set","eval","addWatchListener","updateView"],function(q,w){angular[w]=nglr.u(o,o[w])});e.data("scope",o);console.log("$binder.entity()");f.ha(o);console.log("$binder.compile()");f.compile();console.log("ControlBar.bind()");d.u();console.log("$users.fetchCurrentUser()");b();console.log("PopUp.bind()");(new nglr.A(e)).u();
console.log("$binder.parseAnchor()");D(f);console.log("$binder.executeInit()");fa(f);console.log("$binder.updateView()");f.c();c.ic=nglr.u(f,f.Ld,c);c.kf=function(){nglr.alert("update")};c.watch();e.find("body").wb();console.log("ready()")}
function ba(){var a=window.location.href+"#";a=a.split("#")[1];var b={dd:null};a=a.split("&");for(var c=0;c<a.length;c++){var e=(a[c]+"=").split("=");b[e[0]]=e[1]}if(b.dd=="console"){nglr.hb=document.createElement("div");nglr.hb.id="ng-console";document.getElementsByTagName("body")[0].appendChild(nglr.hb);console.log=function(){nglr.Qb("ng-console-info",arguments)};console.error=function(){nglr.Qb("ng-console-error",arguments)}}}
function B(a,b){var c=document.createElement("link");c.rel="stylesheet";c.type="text/css";b.match(/^http:/)||(b=a.w.C+b);c.href=b;a.ac[0].appendChild(c)}nglr.Hb=function(a){this.location=a;this.lb=25;this.setTimeout=function(b,c){window.setTimeout(b,c)};this.ic=r();this.ia=a.href};
nglr.Hb.prototype.watch=function(){var a=this;function b(){if(a.ia!==a.location.href){var c=a.location.hash.match(/^#\$iframe_notify=(.*)$/);if(c){a.ia.match(/#/)||(a.ia+="#");a.location.href=a.ia;c="_iframe_notify_"+c[1];var e=nglr[c];delete nglr[c];try{(e||nglr.L)()}catch(d){nglr.alert(d)}}else{a.ic(a.location.href);a.ia=a.location.href}}a.setTimeout(b,a.lb)}b()};angular.aa={Ud:function(a){var b=typeof a;switch(b){case "object":if(a===null)return"null";if(a instanceof Array)return"array";if(a instanceof Date)return"date";if(a.nodeType==1)return"element"}return b}};angular.Sa={};angular.Nc={};
angular.La={md:function(a,b,c){var e=_.indexOf(a,b);if(c)e==-1&&a.push(b);else a.splice(e,1);return a},Bf:function(a,b){b=angular.$.compile(b);for(var c=0,e=0;e<a.length;e++){var d=1*b(a[e]);isNaN(d)||(c+=d)}return c},remove:function(a,b){var c=_.indexOf(a,b);c>=0&&a.splice(c,1);return b},find:function(a,b,c){if(b){var e=angular.$.compile(b);_.Ub(a,function(d){if(e(d)){c=d;return true}});return c}},Pe:function(a,b){return angular.La.find(a,function(c){return c.R==b},null)},filter:function(a,b){var c=
[];c.Zc=function(m){for(var o=0;o<c.length;o++)if(!c[o](m))return false;return true};var e=nglr.h.ja;function d(m,o){if(o.charAt(0)==="!")return!d(m,o.substr(1));switch(typeof m){case "boolean":case "number":case "string":return(""+m).toLowerCase().indexOf(o)>-1;case "object":for(var q in m)if(q.charAt(0)!=="$"&&d(m[q],o))return true;return false;case "array":for(q=0;q<m.length;q++)if(d(m[q],o))return true;return false;default:return false}}switch(typeof b){case "boolean":case "number":case "string":b=
{be:b};case "object":for(var f in b)f=="$"?function(){var m=(""+b[f]).toLowerCase();m&&c.push(function(o){return d(o,m)})}():function(){var m=f,o=(""+b[f]).toLowerCase();o&&c.push(function(q){return d(e(q,m),o)})}();break;case "function":c.push(b);break;default:return a}for(var i=[],j=0;j<a.length;j++){var n=a[j];c.Zc(n)&&i.push(n)}return i},add:function(a,b){a.push(_.K(b)?{}:b);return a},Ke:function(a,b){if(!b)return a.length;var c=angular.$.compile(b);return _.reduce(a,0,function(e,d){return e+
(c(d)?1:0)})},lf:function(a,b,c){function e(f,i){return nglr.xb(i)?function(j,n){return f(n,j)}:f}function d(f,i){var j=typeof f,n=typeof i;if(j==n){if(j=="string")f=f.toLowerCase();if(j=="string")i=i.toLowerCase();if(f===i)return 0;return f<i?-1:1}else return j<n?-1:1}b=_.z(b)?b:[b];b=_.map(b,function(f){var i=false;if(typeof f=="string"&&(f.charAt(0)=="+"||f.charAt(0)=="-")){i=f.charAt(0)=="-";f=f.substring(1)}var j=f?angular.$.compile(f):_.ka;return e(function(n,m){return d(j(n),j(m))},i)});return _.fa(a).sort(e(function(f,
i){for(var j=0;j<b.length;j++){var n=b[j](f,i);if(n!=0)return n}return 0},c))},nf:function(a,b){var c=false,e=-1;_.Ub(a,function(d,f){if(d==b){c=true;e=f;return true}if((d.charAt(0)=="+"||d.charAt(0)=="-")&&d.substring(1)==b){c=d.charAt(0)=="+";e=f;return true}});e>=0&&a.splice(e,1);a.unshift((c?"-":"+")+b);return a},mf:function(a,b,c,e){c=c||"ng-ascend";e=e||"ng-descend";a=a[0]||"";var d=true;if(a.charAt(0)=="-"){a=a.substring(1);d=false}else if(a.charAt(0)=="+")a=a.substring(1);return a==b?d?c:
e:""},Ea:function(a,b,c){var e=a[b];if(!e){e={};a[b]=e}nglr.Ea(c,e);return a}};
angular.U={quote:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"').replace(/\n/g,"\\n").replace(/\f/g,"\\f").replace(/\r/g,"\\r").replace(/\t/g,"\\t").replace(/\v/g,"\\v")+'"'},tc:function(a){a=angular.U.quote(a);for(var b=[],c=0;c<a.length;c++){var e=a.charCodeAt(c);if(e<128)b.push(a.charAt(c));else{e="000"+e.toString(16);b.push("\\u"+e.substring(e.length-4))}}return b.join("")},Sd:function(a){var b;if(typeof a=="string"&&(b=a.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){a=
new Date(0);a.setUTCFullYear(b[1],b[2]-1,b[3]);a.setUTCHours(b[4],b[5],b[6],0);return a}return a}};angular.Jc={toString:function(a){function b(c){return c<10?"0"+c:c}return a.getUTCFullYear()+"-"+b(a.getUTCMonth()+1)+"-"+b(a.getUTCDate())+"T"+b(a.getUTCHours())+":"+b(a.getUTCMinutes())+":"+b(a.getUTCSeconds())+"Z"}};angular.$={compile:function(a){if(_.P(a))return a;else if(a){var b=new nglr.h;return function(c){b.I=c;return b.eval(a)}}else return r()}};
(function(){function a(b,c,e){_.extend(b,c);_.a(e||[],function(d){b[d]=_[d]})}a(angular.aa,{},["extend","clone","isEqual","isElement","isArray","isFunction","isUndefined"]);a(angular.Sa,angular.aa,["each","map","reduce","reduceRight","detect","select","reject","all","any","include","invoke","pluck","max","min","sortBy","sortedIndex","toArray","size"]);a(angular.La,angular.Sa,["first","last","compact","flatten","without","uniq","intersect","zip","indexOf","lastIndexOf"]);a(angular.Nc,angular.Sa,["keys",
"values"]);a(angular.U,angular.aa);a(angular.$,angular.aa,["bind","bindAll","delay","defer","wrap","compose"])})();nglr.g=function(a,b,c,e){this.F=a;this.Lf=c;this.anchor={};this.$d=b;this.w=e||{};this.Cc=[]};nglr.g.Ha=function(a){for(var b=[],c=0,e;(e=a.indexOf("{{",c))>-1;){c<e&&b.push(a.substr(c,e-c));c=e;e=a.indexOf("}}",e);e=e<0?a.length:e+2;b.push(a.substr(c,e-c));c=e}c!=a.length&&b.push(a.substr(c,a.length-c));return b.length===0?[a]:b};nglr.g.ld=function(a){a=nglr.g.Ha(a);return a.length>1||nglr.g.ea(a[0])!==null};nglr.g.ea=function(a){return(a=a.replace(/\n/gm," ").match(/^\{\{(.*)\}\}$/))?a[1]:null};
function ga(a,b){var c={};b.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,function(e,d,f){if(d)c[decodeURIComponent(d)]=decodeURIComponent(f)});return c}function D(a,b){b=b||window.location.href;var c=b.indexOf("#");if(!(c<0)){b=b.substring(c+1);b=ga(a,b);jQuery.a(a.anchor,function(e){delete a.anchor[e]});jQuery.a(b,function(e,d){a.anchor[e]=d})}}nglr.g.prototype.Ld=function(a){console.log("URL change detected",a);D(this,a);this.c()};
function ha(a){var b=window.location.href,c=b.indexOf("#");if(c>-1)b=b.substring(0,c);b+="#";c="";for(var e in a.anchor){var d=a.anchor[e];if(typeof d==="undefined"||d===null)delete a.anchor[e];else{b+=c+encodeURIComponent(e);if(d!==true)b+="="+encodeURIComponent(d);c="&"}}a=b;e=window.location.href;e.match(/#/)||(e+="#");if(e!=a)window.location.href=a;self.Oe=a;return b}
nglr.g.prototype.c=function(){(new Date).getTime();var a=jQuery(this.F).scope();E(a,"$invalidWidgets",[]);a.c();(new Date).getTime();ha(this);_.a(this.Cc,function(b){b()})};function fa(a){jQuery("[ng-init]",a.F).a(function(){var b=jQuery(this),c=b.scope();try{c.eval(b.e("ng-init"))}catch(e){nglr.alert("EVAL ERROR:\n"+b.e("ng-init")+"\n"+nglr.m(e,true))}})}
nglr.g.prototype.ha=function(a){jQuery("[ng-entity]",this.F).e("ng-watch",function(){try{var b=jQuery(this);return a.ha(b.e("ng-entity"))+(b.e("ng-watch")||"")}catch(c){nglr.alert(c)}})};
nglr.g.prototype.compile=function(){var a=jQuery(this.F),b=this;if(this.w.Xc){var c=jQuery(":submit",this.F).rc("[ng-action]");c.e("ng-action","$save()");c.rc(":disabled").rc("ng-bind-attr").e("ng-bind-attr",'{disabled:"{{$invalidWidgets}}"}')}F(this,this.F)(this.F,a.scope(),"");jQuery("a[ng-action]",this.F).vd("click",function(){var e=jQuery(this);try{e.scope().eval(e.e("ng-action"));e.vc("ng-error");e.wc("ng-exception")}catch(d){e.fb("ng-exception");e.e("ng-error",nglr.m(d,true))}b.c();return false})};
function ia(a,b,c,e){a=c.concat();c=a.pop();var d=nglr.g.Ha(b.nodeValue);if(d.length>1||nglr.g.ea(d[0])){var f=b.parentNode;if(nglr.ec(f)){f.setAttribute("ng-bind-template",b.nodeValue);e.push({path:a,b:function(o){return new nglr.S(o,o.getAttribute("ng-bind-template"))}})}else for(var i=0;i<d.length;i++){var j=d[i],n=nglr.g.ea(j),m;if(n){m=document.createElement("span");jQuery(m).e("ng-bind",n);i===0&&e.push({path:a.concat(c+i),b:nglr.g.prototype.qc})}else if(nglr.Fa&&j.charAt(0)==" "){m=document.createElement("span");
m.innerHTML="&nbsp;"+j.substring(1)}else m=document.createTextNode(j);f.insertBefore(m,b)}f.removeChild(b)}}function F(a,b){var c=[];G(a,b,[],c);return function(e,d,f){for(var i=c.length,j=0;j<i;j++){for(var n=c[j],m=e,o=n.path,q=0;q<o.length;q++)m=m.childNodes[o[q]];try{var w=n.b(m,d,f);w&&d.Ec.push(w)}catch(t){nglr.alert(t)}}}}
function G(a,b,c,e){var d=b.nodeType;if(d==Node.TEXT_NODE)ia(a,b,c,e);else if(!(d!=Node.ELEMENT_NODE&&d!=Node.DOCUMENT_NODE))if(b.getAttribute){d=b.getAttribute("ng-non-bindable");if(!(d||d==="")){if(d=b.attributes){var f=b.getAttribute("ng-bind-attr");b.removeAttribute("ng-bind-attr");f=f?nglr.za(f):{};for(var i=d.length,j=0;j<i;j++){var n=d[j],m=n.name;n=nglr.Fa&&m=="href"?decodeURI(b.getAttribute(m,2)):n.value;if(nglr.g.ld(n))f[m]=n}d=nglr.m(f);d.length>2&&b.setAttribute("ng-bind-attr",d)}b.getAttribute||
console.log(b);var o=b.getAttribute("ng-repeat");if(o){b.removeAttribute("ng-repeat");var q=F(a,b);d=document.createComment("ng-repeat: "+o);f=b.parentNode;f.insertBefore(d,b);f.removeChild(b);var w=function(t,y,g){var h=jQuery(b).fa();h.kb("display","");h.e("ng-repeat-index",""+g);h.data("scope",t);q(h[0],t,y+g+":");return h};e.push({path:c,b:function(t,y,g){return new nglr.Xa(jQuery(t),o,w,g)}})}else{b.getAttribute("ng-eval")&&e.push({path:c,b:a.Dd});b.getAttribute("ng-bind")&&e.push({path:c,b:a.qc});
b.getAttribute("ng-bind-attr")&&e.push({path:c,b:a.zd});b.getAttribute("ng-hide")&&e.push({path:c,b:a.Ed});b.getAttribute("ng-show")&&e.push({path:c,b:a.Fd});b.getAttribute("ng-class")&&e.push({path:c,b:a.Ad});b.getAttribute("ng-class-odd")&&e.push({path:c,b:a.Cd});b.getAttribute("ng-class-even")&&e.push({path:c,b:a.Bd});b.getAttribute("ng-style")&&e.push({path:c,b:a.Gd});b.getAttribute("ng-watch")&&e.push({path:c,b:a.Hd});d=b.nodeName;if(d=="INPUT"||d=="TEXTAREA"||d=="SELECT"||d=="BUTTON")e.push({path:c,
b:function(t,y,g){t.name=g+t.name.split(":").pop();return ja(a.$d,jQuery(t),y)}});if(d=="OPTION")if(!jQuery("<select/>").append(jQuery(b).fa()).B().match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi))b.value=b.text;d=b.childNodes;for(f=0;f<d.length;f++)G(a,d[f],c.concat(f),e)}}}}A=nglr.g.prototype;A.Dd=function(a){return new nglr.Ta(a,a.getAttribute("ng-eval"))};A.qc=function(a){return new nglr.S(a,"{{"+a.getAttribute("ng-bind")+"}}")};A.zd=function(a){return new nglr.Ma(a,nglr.za(a.getAttribute("ng-bind-attr")))};
A.Ed=function(a){return new nglr.Ua(a,a.getAttribute("ng-hide"))};A.Fd=function(a){return new nglr.Za(a,a.getAttribute("ng-show"))};A.Ad=function(a){return new nglr.Ra(a,a.getAttribute("ng-class"))};A.Bd=function(a){return new nglr.Pa(a,a.getAttribute("ng-class-even"))};A.Cd=function(a){return new nglr.Qa(a,a.getAttribute("ng-class-odd"))};A.Gd=function(a){return new nglr.ab(a,a.getAttribute("ng-style"))};A.Hd=function(a,b){b.watch(a.getAttribute("ng-watch"))};nglr.M=function(a,b){this.document=a;this.Ja=b;this.window=window;this.W=[]};nglr.M.prototype.u=v();nglr.M.ne='<div><div class="ui-widget-overlay"></div><div id="ng-login" ng-non-bindable="true"><div class="ng-login-container"></div></div></div>';nglr.M.prototype.Da=function(a){this.W.push(a);this.W.length==1&&H(this,"/user_session/new.mini?return_url="+encodeURIComponent(this.window.location.href.split("#")[0]))};nglr.M.prototype.oc=function(a){this.W.push(a);this.W.length==1&&H(this,"/user_session/do_destroy.mini")};
function H(a,b){var c=(new Date).getTime(),e=a.window.location.href.split("#")[0];e+="#$iframe_notify="+c;var d=jQuery('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+e+'" src="'+a.Ja+b+'" width="500" height="330"/></div>');a.document.append(d);d.Vb({height:363,width:500,wf:false,wd:true,title:'Authentication: <a href="http://www.getangular.com"><tt>&lt;angular/&gt;</tt></a>'});nglr["_iframe_notify_"+c]=function(){d.Vb("destroy");d.remove();jQuery.a(a.W,function(f,i){i()});a.W=[]}}
nglr.M.Lc='<div ng-non-bindable="true" title="Permission Error:">Sorry, you do not have permission for this!</div>';nglr.M.prototype.sb=function(){if(!this.Zb){this.Zb=jQuery(nglr.M.Lc);this.Zb.Vb({ve:true,height:70,wd:true})}};nglr.p=function(a,b,c){this.sc=a;this.Dc=b;this.V={qa:[]};this.anchor=c;this.wa=[]};function I(a,b){if(b.constructor!=nglr.N)throw"Parameter must be an instance of Entity! "+nglr.m(b);var c=b.ra+"/"+b.R,e=a.V[c];if(e)nglr.N.jb(b,e);else e=a.V[c]=b;return e}nglr.p.prototype.load=function(a,b,c,e){if(b&&b!=="*"){var d=this;J(this,["GET",a.ra+"/"+b],function(f){K(a,f);L(a);f=a.pa(a);I(d,f);(c||nglr.L)(a)},e)}return a};
nglr.p.prototype.pb=function(a,b,c){var e=this,d=[],f=0;jQuery.a(b,function(i,j){d.push(e.load(a(),j,function(){f++;if(f==b.length)(c||nglr.L)(d)}))});return d};nglr.p.prototype.kc=function(a,b,c){return this.load(a,b,c,function(e){if(e.Ka==404){a.R=b;(c||nglr.L)(a)}else throw e;})};function ka(a,b,c){var e=[];e.Cb=function(d){return d.ra==b.title};a.V.qa.push(e);J(a,["GET",b.title],function(d){for(var f=0;f<d.length;f++){var i=b();K(i,d[f]);e.push(I(a,i))}(c||nglr.L)(e)});return e}
nglr.p.prototype.save=function(a,b){var c=this,e={};M(a,e);J(this,["POST","",e],function(d){K(a,d);var f=I(c,a);_.a(c.V.qa,function(i){i.Cb(a)&&angular.La.md(i,f,true)});if(a.Db)c.anchor[a.Db]=a.R;b&&b(a)})};nglr.p.prototype.remove=function(a,b){var c=this,e={};M(a,e);J(this,["DELETE","",e],function(d){delete c.V[a.ra+"/"+a.R];_.a(c.V.qa,function(f){for(var i=0;i<f.length;i++)f[i].R==a.R&&f.splice(i,1)});(b||nglr.L)(d)})};function J(a,b,c,e){b.Fc=c;b.Hc=e||function(d){throw d;};a.wa.push(b)}
function C(a){function b(d,f){console.log("RESPONSE["+d+"]: ",f);if(f.Ka==401)c.Dc.Da(function(){c.sc(e,b)});else if(f.Ka)nglr.alert(nglr.m(f));else for(var i=0;i<f.length;i++){var j=f[i],n=e[i];if(d=j.Ka)d==403?c.Dc.sb():n.Hc(j);else n.Fc(j)}}if(a.wa.length!==0){var c=a,e=a.wa;a.wa=[];console.log("REQUEST:",e);a.sc(e,b)}}function ea(a,b,c){function e(){d--;d===0&&c&&c()}var d=1;for(var f in b)if((a=b[f])&&a.sa==nglr.N.prototype.sa){d++;a.sa(e)}e()}
nglr.p.prototype.ma=function(a,b,c,e){var d=this,f=[];f.Cb=z(false);this.V.qa.push(f);J(this,["GET",a.title+"/"+b+"="+c],function(i){for(var j=0;j<i.length;j++){var n=K(new a,i[j]);f.push(I(d,n))}e&&e(f)});return f};nglr.p.ca=v();nglr.p.ca.all=function(){return[]};nglr.p.ca.ma=function(){return[]};nglr.p.ca.load=function(){return{}};nglr.p.ca.title=undefined;
nglr.p.prototype.ha=function(a,b){if(!a)return nglr.p.ca;var c=this;function e(d){return new nglr.N(e,d)}e.title=a;e.Gc=true;e.Tb=this;e.ed=b||{};e.load=function(d,f){return c.load(e(),d,f)};e.pb=function(d,f){return c.pb(e,d,f)};e.kc=function(d,f){return c.kc(e(),d,f)};e.all=function(d){return ka(c,e,d)};e.ma=function(d,f,i){return c.ma(e,d,f,i)};e.tf=function(d){J(c,["GET",a+"/$properties"],d)};return e};
nglr.p.prototype.join=function(a){function b(){throw"Joined entities can not be instantiated into a document.";}var c=_(a).Ob().map(function(e,d){return d}).Rd(function(e){var d=[];do{if(_(d).Aa(e))throw"Infinite loop in join: "+d.join(" -> ");d.push(e);if(!a[e])throw _("Named entity '<%=name%>' is undefined.").Z({name:e});e=a[e].Ga?a[e].Ga.substring(0,a[e].Ga.indexOf(".")):undefined}while(e);return d.length}).value();if(_(c).select(function(e){return a[e].Ga}).length!=c.length-1)throw"Exactly one entity needs to be primary.";
b.ma=function(e,d){var f=[],i=e?e.substring(0,e.indexOf(".")):undefined;if(i!=c[0])throw _("Named entity '<%=name%>' is not a primary entity.").Z({name:i});var j=1;a[i].join.ma(e.substring(e.indexOf(".")+1),d,function(n){var m=c[j++],o=a[m],q=o.Ga,w={};_(n).a(function(t){var y={};f.push(y);y[i]=t;t=nglr.h.ja(y,q);w[t]=t});o.join.pb(_.D(w),function(t){var y={};_(t).a(function(g){y[g.R]=g});_(f).a(function(g){var h=nglr.h.ja(g,q);g[m]=y[h]})})});return f};return b};angular.filter.s=function(a){if(a)for(var b in a)this[b]=a[b]};angular.filter.s.i=function(a,b){b=b||"text";switch(typeof a){case "string":return b=="text"?a:undefined;case "object":if(a&&typeof a[b]!=="undefined")return a[b];return;default:return a}};angular.filter.Le=function(a){jQuery(this.element).zb("ng-format-negative",a<0);return"$"+angular.filter.tb.apply(this,[a,2])};
angular.filter.tb=function(a,b){if(isNaN(a)||!isFinite(a))return"";b=typeof b=="undefined"?2:b;var c=a<0;a=Math.abs(a);var e=Math.pow(10,b);a=""+Math.round(a*e);var d=a.substring(0,a.length-b);d=d||"0";e=a.substring(a.length-b);a=c?"-":"";for(c=0;c<d.length;c++){if((d.length-c)%3===0&&c!==0)a+=",";a+=d.charAt(c)}if(b>0){for(c=e.length;c<b;c++)e+="0";a+="."+e.substring(0,b)}return a};angular.filter.bd=v();angular.filter.rd=function(a){jQuery(this.element).fb("ng-monospace");return nglr.m(a,true)};
angular.filter.Ac=function(a,b){a=nglr.Td(a);for(var c=a.replace(/ /g,""),e=angular.filter.Ac.Mc,d=0;d<e.length;d++)for(var f=e[d],i=0;i<f.na.length;i++)if(f.na[i].test(c)){b=f.name+": "+a;f=f.url+a;return new angular.filter.s({text:b,url:f,B:'<a href="'+nglr.hd(f)+'">'+b+"</a>",Ef:a})}return a?b||new angular.filter.s({text:a+" is not recognized"}):null};
angular.filter.Ac.Mc=[{name:"UPS",url:"http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=en_US&track.x=0&track.y=0&InquiryNumber1=",na:[/^1Z[0-9A-Z]{16}$/i]},{name:"FedEx",url:"http://www.fedex.com/Tracking?tracknumbers=",na:[/^96\d{10}?$/i,/^96\d{17}?$/i,/^96\d{20}?$/i,/^\d{15}$/i,/^\d{12}$/i]},{name:"USPS",url:"http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?origTrackNum=",na:[/^(91\d{20})$/i,/^(91\d{18})$/i]}];
angular.filter.link=function(a,b){b=b||angular.filter.s.i(a);var c=angular.filter.s.i(a,"url")||angular.filter.s.i(a);if(c){if(angular.k.gd(c)===null)c="mailto:"+c;a='<a href="'+nglr.mb(c)+'">'+b+"</a>";return new angular.filter.s({text:b,url:c,B:a})}return a};angular.filter.gb=function(a){if(a===null)return"";for(var b=0;a>1E3;){a/=1024;b++}a=""+a;var c=a.indexOf(".");if(c>-1&&c+2<a.length)a=a.substring(0,c+2);return a+" "+angular.filter.gb.Pc[b]};
angular.filter.gb.Pc=["bytes","KB","MB","GB","TB","PB"];angular.filter.Ve=function(a,b,c){if(a&&a.url){var e="";if(b)e=' style="max-width: '+b+"px; max-height: "+(c||b)+'px;"';return new angular.filter.s({url:a.url,text:a.url,B:'<img src="'+a.url+'"'+e+"/>"})}return null};angular.filter.ff=function(a){return(a=angular.filter.s.i(a))?(""+a).toLowerCase():a};angular.filter.Kf=function(a){return(a=angular.filter.s.i(a))?(""+a).toUpperCase():a};
angular.filter.ef=function(a){a=angular.filter.s.i(a);if(a===""||!a)return 1;return a.split(/\n|\f/).length};angular.filter["if"]=function(a,b){return b?a:undefined};angular.filter.If=function(a,b){return b?undefined:a};angular.filter.l=function(a,b,c,e){b=b||{};var d=angular.filter.l;a={$c:a,ze:d.Pb(b,"color"),Fe:d.title(b),Be:d.Pb(b,"label"),Ae:d.Ab(b),Ce:"bg,s,FFFFFF00"};if(_.z(b.ae)){a.He="x";a.Ge="0:|"+b.ae.join("|")}return angular.filter.l.Wb(a,c,e)};
angular.filter.l.Ab=function(a){var b=[];_.a(a.Pd||[],function(c){var e=[];_.a(c.Ab||[],function(d){e.push(d)});b.push(e.join(","))});a=b.join("|");return a===""?null:"t:"+a};angular.filter.l.title=function(a){var b=[];a=a.title||[];_.a(_.z(a)?a:[a],function(c){b.push(encodeURIComponent(c))});return b.join("|")};
angular.filter.l.Pb=function(a,b){var c=[],e=0;_.a(a.Pd||[],function(d){var f=[];d=d[b]||[];_.a(_.z(d)?d:[d],function(i){f.push(encodeURIComponent(i));e++});c.push(f.join("|"))});return e?c.join(","):null};angular.filter.l.Wb=function(a,b,c){b=b||200;c=c||b;var e="http://chart.apis.google.com/chart?",d=[];a.Ee=b+"x"+c;for(var f in a){var i=a[f];i&&d.push(f+"="+i)}d.sort();e+=d.join("&");return new angular.filter.s({url:e,text:i,B:'<img width="'+b+'" height="'+c+'" src="'+e+'"/>'})};
angular.filter.uf=function(a,b,c){return angular.filter.l.Wb({$c:"qr",De:encodeURIComponent(a)},b,c)};
angular.filter.ye={qf:function(a,b,c){return angular.filter.l("p",a,b,c)},rf:function(a,b,c){return angular.filter.l("p3",a,b,c)},sf:function(a,b,c){return angular.filter.l("pc",a,b,c)},se:function(a,b,c){return angular.filter.l("bhs",a,b,c)},re:function(a,b,c){return angular.filter.l("bhg",a,b,c)},ue:function(a,b,c){return angular.filter.l("bvs",a,b,c)},te:function(a,b,c){return angular.filter.l("bvg",a,b,c)},df:function(a,b,c){return angular.filter.l("lc",a,b,c)},zf:function(a,b,c){return angular.filter.l("ls",
a,b,c)},xf:function(a,b,c){return angular.filter.l("s",a,b,c)}};angular.filter.B=function(a){return new angular.filter.s({B:a})};nglr.Mb=[].constructor;nglr.m=function(a,b){var c=[];nglr.yb(c,a,b?"\n ":null);return c.join("")};nglr.Df=function(a){return nglr.m(a,true)};nglr.za=function(a){try{var b=new nglr.q(a,true),c=N(b);O(b);return c()}catch(e){console.error("fromJson error: ",a,e);throw e;}};
nglr.yb=function(a,b,c){var e=typeof b;if(b===null)a.push("null");else if(e!=="function")if(e==="boolean")a.push(""+b);else if(e==="number")isNaN(b)?a.push("null"):a.push(""+b);else if(e==="string")return a.push(angular.U.tc(b));else if(e==="object")if(b instanceof Array){a.push("[");var d=b.length;e=false;for(var f=0;f<d;f++){var i=b[f];e&&a.push(",");typeof i=="function"||typeof i=="undefined"?a.push("null"):nglr.yb(a,i,c);e=true}a.push("]")}else if(b instanceof Date)a.push(angular.U.tc(angular.Jc.toString(b)));
else{a.push("{");c&&a.push(c);e=false;f=c?c+" ":false;i=[];for(var j in b)j.indexOf("$$")!==0&&i.push(j);i.sort();for(j=0;j<i.length;j++){var n=i[j];try{d=b[n];if(typeof d!="function"){if(e){a.push(",");c&&a.push(c)}a.push(angular.U.quote(n));a.push(":");nglr.yb(a,d,f);e=true}}catch(m){}}a.push("}")}};nglr.N=function(a,b){this.pa=a;K(this,b||{});this.ra=a.title;L(this)};nglr.N.jb=function(a,b){if(!(a===b||!a||!b)){var c=function(d,f,i){return i.substring(0,2)!=="$$"&&typeof d[i]!=="function"&&typeof f[i]!=="function"};for(var e in b)c(a,b,e)&&delete b[e];for(e in a)if(c(a,b,e))b[e]=a[e]}};function L(a){nglr.Ea(a.pa.ed,a);return a}nglr.N.prototype.sa=function(a){this.pa.Tb.save(this,a===true?undefined:a);a===true&&C(this.pa.Tb);return this};function K(a,b){nglr.N.jb(b,a);return a}
function M(a,b){nglr.N.jb(a,b);return a};nglr.J=function(a,b){this.text=a;this.cd=b?20:-1;this.o=[];this.index=0};
nglr.J.Eb={"null":z(null),"true":z(true),"false":z(false),"+":function(a,b,c){return(b||0)+(c||0)},"-":function(a,b,c){return(b||0)-(c||0)},"*":function(a,b,c){return b*c},"/":function(a,b,c){return b/c},"%":function(a,b,c){return b%c},"^":function(a,b,c){return b^c},"=":function(a,b,c){return E(a.scope,b,c)},"==":function(a,b,c){return b==c},"!=":function(a,b,c){return b!=c},"<":function(a,b,c){return b<c},">":function(a,b,c){return b>c},"<=":function(a,b,c){return b<=c},">=":function(a,b,c){return b>=
c},"&&":function(a,b,c){return b&&c},"||":function(a,b,c){return b||c},"&":function(a,b,c){return b&c},"|":function(a,b,c){return c(a,b)},"!":function(a,b){return!b}};nglr.J.prototype.Y=function(){return this.index+1<this.text.length?this.text.charAt(this.index+1):false};
nglr.J.prototype.parse=function(){for(var a=this.o,b=nglr.J.Eb,c=true;this.index<this.text.length;){var e=this.text.charAt(this.index);if(e=='"'||e=="'"){la(this,e);c=true}else if(e=="("||e=="["){a.push({index:this.index,text:e});this.index++}else if(e=="{"){c=this.Y();if(c==":"||c=="("){a.push({index:this.index,text:e+c});this.index++}else a.push({index:this.index,text:e});this.index++;c=true}else if(e==")"||e=="]"||e=="}"){a.push({index:this.index,text:e});this.index++;c=false}else if(e==":"||e==
"."||e==","||e==";"){a.push({index:this.index,text:e});this.index++;c=true}else if(c&&e=="/"){ma(this);c=false}else if("0"<=e&&e<="9"){na(this);c=false}else if(P(this,e)){Q(this);c=false}else if(e==" "||e=="\r"||e=="\t"||e=="\n"||e=="\u000b")this.index++;else{c=e+this.Y();var d=b[e],f=b[c];if(f){a.push({index:this.index,text:c,b:f});this.index+=2}else if(d){a.push({index:this.index,text:e,b:d});this.index+=1}else throw"Lexer Error: Unexpected next character ["+this.text.substring(this.index)+"] in expression '"+
this.text+"' at column '"+(this.index+1)+"'.";c=true}}return a};function P(a,b){return"a"<=b&&b<="z"||"A"<=b&&b<="Z"||"_"==b||b=="$"}function na(a){for(var b="",c=a.index;a.index<a.text.length;){var e=a.text.charAt(a.index);if(e=="."||"0"<=e&&e<="9")b+=e;else break;a.index++}b=1*b;a.o.push({index:c,text:b,b:function(){return b}})}
function Q(a){for(var b="",c=a.index;a.index<a.text.length;){var e=a.text.charAt(a.index);if(e=="."||P(a,e)||"0"<=e&&e<="9")b+=e;else break;a.index++}e=nglr.J.Eb[b];if(!e){e=function(d){return d.scope.i(b)};e.Ba=b}a.o.push({index:c,text:b,b:e})}nglr.J.Kc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'};
function la(a,b){var c=a.index,e=a.cd;a.index++;for(var d="",f=false;a.index<a.text.length;){var i=a.text.charAt(a.index);if(f){if(i=="u"){i=a.text.substring(a.index+1,a.index+5);a.index+=4;d+=String.fromCharCode(parseInt(i,16))}else{f=nglr.J.Kc[i];d+=f?f:i}f=false}else if(i=="\\")f=true;else if(i==b){a.index++;a.o.push({index:c,text:d,b:function(){return d.length==e?angular.U.Sd(d):d}});return}else d+=i;a.index++}throw"Lexer Error: Unterminated quote ["+a.text.substring(c)+"] starting at column '"+
(c+1)+"' in expression '"+a.text+"'.";}
function ma(a){var b=a.index;a.index++;for(var c="",e=false;a.index<a.text.length;){var d=a.text.charAt(a.index);if(e){c+=d;e=false}else if(d==="\\"){c+=d;e=true}else if(d==="/"){a.index++;e="";if(P(a,a.text.charAt(a.index))){Q(a);e=a.o.pop().text}var f=new RegExp(c,e);a.o.push({index:b,text:c,Qe:e,b:function(){return f}});return}else c+=d;a.index++}throw"Lexer Error: Unterminated RegExp ["+a.text.substring(b)+"] starting at column '"+(b+1)+"' in expression '"+a.text+"'.";}
nglr.q=function(a,b){this.text=a;this.o=(new nglr.J(a,b)).parse();this.index=0};nglr.q.Rc=z(0);nglr.q.prototype.error=function(a,b){throw"Token '"+b.text+"' is "+a+" at column='"+(b.index+1)+"' of expression '"+this.text+"' starting at '"+this.text.substring(b.index)+"'.";};function R(a){if(a.o.length===0)throw"Unexpected end of expression: "+a.text;return a.o[0]}nglr.q.prototype.Y=function(a,b,c,e){var d=this.o;if(d.length>0){d=d[0];var f=d.text;if(f==a||f==b||f==c||f==e||!a&&!b&&!c&&!e)return d}return false};
function S(a,b,c,e,d){if(b=a.Y(b,c,e,d)){a.o.shift();return a.Me=b}return false}function T(a,b){if(!S(a,b)){var c=a.Y();throw"Expecting '"+b+"' at column '"+(c.index+1)+"' in '"+a.text+"' got '"+a.text.substring(c.index)+"'.";}}function oa(a,b,c){var e=c.apply(a);return function(d){return b(d,e(d))}}function U(a,b,c,e){var d=e.apply(a);return function(f){return c(f,b(f),d(f))}}
function O(a){if(a.o.length!==0)throw"Did not understand '"+a.text.substring(a.o[0].index)+"' while evaluating '"+a.text+"'.";}function V(a){for(var b=[];;){a.o.length>0&&!a.Y("}",")",";","]")&&b.push(W(a));if(!S(a,";"))return function(c){for(var e,d=0;d<b.length;d++){var f=b[d];if(f)e=f(c)}return e}}}function W(a){for(var b=a.G(),c;;)if(c=S(a,"|"))b=U(a,b,c.b,a.filter);else return b}nglr.q.prototype.filter=function(){return X(this,angular.filter)};nglr.q.prototype.k=function(){return X(this,angular.k)};
function X(a,b){for(var c=pa(a,b),e=[];;)if(S(a,":"))e.push(a.G());else{var d=function(f,i){i=[i];for(var j=0;j<e.length;j++)i.push(e[j](f));return c.apply(f,i)};return function(){return d}}}nglr.q.prototype.G=function(){return qa(this)};function qa(a){if(S(a,"throw")){var b=Y(a);return function(c){throw b(c);}}else return Y(a)}
function Y(a){var b=a.nc(),c;if(c=S(a,"=")){if(!b.Ba)throw"Left hand side '"+a.text.substring(0,c.index)+"' of assignment '"+a.text.substring(c.index)+"' is not assignable.";return U(a,function(){return b.Ba},c.b,a.nc)}else return b}A=nglr.q.prototype;A.nc=function(){for(var a=this.mc(),b;;)if(b=S(this,"||"))a=U(this,a,b.b,this.mc);else return a};A.mc=function(){for(var a=this.pc(),b;;)if(b=S(this,"&&"))a=U(this,a,b.b,this.pc);else return a};
A.pc=function(){var a;return(a=S(this,"!"))?oa(this,a.b,this.Xb):this.Xb()};A.Xb=function(){for(var a=this.uc(),b;;)if(b=S(this,"==","!="))a=U(this,a,b.b,this.uc);else return a};A.uc=function(){for(var a=this.Kb(),b;;)if(b=S(this,"<",">","<=",">="))a=U(this,a,b.b,this.Kb);else return a};A.Kb=function(){for(var a=this.rb(),b;b=S(this,"+","-");)a=U(this,a,b.b,this.rb);return a};A.rb=function(){for(var a=this.Bc(),b;b=S(this,"*","/","%");)a=U(this,a,b.b,this.Bc);return a};
A.Bc=function(){var a;return S(this,"+")?N(this):(a=S(this,"-"))?U(this,nglr.q.Rc,a.b,this.rb):N(this)};function pa(a,b){var c=S(a),e=c.text.split(".");b=b;for(var d,f=0;f<e.length;f++){d=e[f];if(b)b=b[d]}if(typeof b!="function")throw"Function '"+c.text+"' at column '"+(c.index+1)+"' in '"+a.text+"' is not defined.";return b}
function N(a){var b;if(S(a,"(")){b=W(a);T(a,")");b=b}else if(S(a,"["))b=ra(a);else if(S(a,"{"))b=a.object();else if(S(a,"{:"))b=sa(a,false);else if(S(a,"{("))b=sa(a,true);else{var c=S(a);(b=c.b)||a.error("not a primary expression",c)}for(;c=S(a,"(","[",".");)if(c.text==="(")b=ta(a,b);else if(c.text==="[")b=ua(a,b);else if(c.text===".")b=va(a,b);else throw"IMPOSSIBLE";return b}
function sa(a,b){var c=[];if(b){if(!S(a,")")){for(c.push(S(a).text);S(a,",");)c.push(S(a).text);T(a,")")}T(a,":")}var e=V(a);T(a,"}");return function(d){return function(f){var i=new nglr.h(d.scope.I);E(i,"$",f);for(var j=0;j<c.length;j++)E(i,c[j],arguments[j]);return e({scope:i})}}}function va(a,b){var c=S(a).text;function e(d){return nglr.h.ja(b(d),c)}e.Ba=c;return e}
function ua(a,b){var c=a.G();T(a,"]");if(S(a,"=")){var e=a.G();return function(d){return b(d)[c(d)]=e(d)}}else return function(d){var f=b(d);d=c(d);return f?f[d]:undefined}}function ta(a,b){var c=[];if(R(a).text!=")"){do c.push(a.G());while(S(a,","))}T(a,")");return function(e){for(var d=[],f=0;f<c.length;f++)d.push(c[f](e));f=b(e);if(typeof f==="function")return f.apply(e,d);else throw"Expression '"+b.Ba+"' is not a function.";}}
function ra(a){var b=[];if(R(a).text!="]"){do b.push(a.G());while(S(a,","))}T(a,"]");return function(c){for(var e=[],d=0;d<b.length;d++)e.push(b[d](c));return e}}nglr.q.prototype.object=function(){var a=[];if(R(this).text!="}"){do{var b=S(this).text;T(this,":");var c=this.G();a.push({sd:b,value:c})}while(S(this,","))}T(this,"}");return function(e){for(var d={},f=0;f<a.length;f++){var i=a[f],j=i.value(e);d[i.sd]=j}return d}};
function wa(a){for(var b=[];a.o.length>0;){b.push(xa(a));S(a,";")||O(a)}return function(c){for(var e="",d=0;d<b.length;d++)e+=b[d](c);return e}}function xa(a){var b=S(a).text,c,e;if(S(a,"=")){c=b;b=S(a).text}if(S(a,":"))e=N(a)(null);return function(d){var f=d.scope.i("$datastore").ha(b,e);E(d.scope,b,f);if(c){f=f();f.Db=c;E(d.scope,c,f);return"$anchor."+c+":{"+c+"="+b+".load($anchor."+c+");"+c+".$$anchor="+angular.U.quote(c)+";};"}else return""}}
nglr.q.prototype.watch=function(){for(var a=[];this.o.length>0;){a.push(ya(this));S(this,";")||O(this)}O(this);return function(b){for(var c=0;c<a.length;c++){var e=a[c](b);b.Sc(e.name,e.b)}}};function ya(a){var b=S(a).text;T(a,":");var c;if(R(a).text=="{"){T(a,"{");c=V(a);T(a,"}")}else c=a.G();return function(){return{name:b,b:c}}};nglr.h=function(a,b){this.Ec=[];this.Bb={};this.name=b;a=a||{};function c(){}c.prototype=a;this.I=new c;this.I.ie=a;if(b=="ROOT")this.I.je=this.I};nglr.h.ya={};nglr.h.prototype.c=function(){var a=this;za(this);_.a(this.Ec,function(b){Z(a,b,"",{},function(){this.c(a)})})};function Aa(a,b){for(a=0;a<b.length;a++){var c=b.charAt(a);if(c!="."&&!P(nglr.J.prototype,c))return false}return true}
nglr.h.ja=function(a,b){if(!b)return a;for(var c=b.split("."),e,d=a,f=c.length,i=0;i<f;i++){e=c[i];if(!e.match(/^[\$\w][\$\w\d]*$/))throw"Expression '"+b+"' is not a valid expression for accesing variables.";if(a){d=a;a=a[e]}if(_.K(a)&&e.charAt(0)=="$"){var j=angular.aa.Ud(d);if(e=(j=angular[j.charAt(0).toUpperCase()+j.substring(1)])?j[[e.substring(1)]]:undefined)return a=_.u(e,d,d)}}if(typeof a==="function"&&!a.Gc)return nglr.u(d,a);return a};
nglr.h.prototype.i=function(a){return nglr.h.ja(this.I,a)};function E(a,b,c){b=b.split(".");a=a.I;for(var e=0;b.length>1;e++){var d=b.shift(),f=a[d];if(!f){f={};a[d]=f}a=f}return a[b.shift()]=c}function $(a,b,c){a.eval(b+"="+nglr.m(c))}nglr.h.prototype.eval=function(a,b){var c=nglr.h.ya[a];if(!c){var e=new nglr.q(a);c=V(e);O(e);nglr.h.ya[a]=c}b=b||{};b.scope=this;return c(b)};
function Z(a,b,c,e,d,f){try{var i=a.eval(c,e);if(b.O){b.O=false;jQuery(b.view).wc("ng-exception").vc("ng-error")}d&&d.apply(b,[i]);return true}catch(j){console.error("Eval Widget Error:",j);a=nglr.m(j,true);b.O=true;jQuery(b.view).fb("ng-exception").e("ng-error",a);f&&f.apply(b,[j,a]);return false}}nglr.h.prototype.ha=function(a){return wa(new nglr.q(a))({scope:this})};
nglr.h.prototype.watch=function(a){var b=this;(new nglr.q(a)).watch()({scope:this,Sc:function(c,e){Ba(b,c,function(d,f){try{return e({scope:b},d,f)}catch(i){nglr.alert(i)}})}})};function Ba(a,b,c){var e=a.Bb[b];if(!e){e={jc:[],G:b};a.Bb[b]=e}e.jc.push(c)}function za(a){var b=false;jQuery.a(a.Bb,function(c,e){var d=a.eval(e.G);if(d!==e.d){jQuery.a(e.jc,function(f,i){i(d,e.d);b=true});e.d=d}});return b};nglr.Gb=function(a,b){this.url=a;this.xd=0;this.$b=b;this.Yd="_"+(""+Math.random()).substr(2)+"_";this.qb=1800};function Ca(a,b){return aa(b)}nglr.Gb.prototype.oa=function(a,b,c,e){var d=this.Yd+this.xd++;nglr[d]=function(j){delete nglr[d];e(200,j)};a={Ff:b,gf:a,of:c};a=Ca(this,nglr.m(a));b=Math.ceil(a.length/this.qb);c=this.url+"/$/"+d+"/"+b+"/";for(var f=0;f<b;f++){var i=a.substr(f*this.qb,this.qb);this.$b(c+(f+1)+"?h="+i,nglr.L)}};nglr.ta=x("frame");nglr.ta.Oc="$DATASET:";
nglr.ta.prototype={write:function(){this.frame.name=nglr.ta.Oc+nglr.m(this.data)},oa:v()};nglr.Jb=function(a,b,c){this.fd=a;this.update=c;this.status=b};nglr.Jb.prototype={oa:function(a,b,c,e){var d=this;Da(this.status,c);this.fd.oa(a,b,c,function(){var f=d.status;f.Ia--;f.Ia===0&&f.lc.bc("fold");try{e.apply(this,arguments)}catch(i){nglr.alert(nglr.m(i))}d.update()})}};nglr.Ib=function(a,b){this.C=a;this.ib=b};nglr.Ib.prototype={Yb:function(a){var b=this;this.C.oa("GET","/account.json",{},function(c,e){b.ad=e.Xd;a(e.Xd)})},oc:function(a){var b=this;this.ib.oc(function(){delete b.ad;(a||nglr.L)()})},Da:function(a){var b=this;this.ib.Da(function(){b.Yb(function(){(a||nglr.L)()})})},sb:function(){this.ib.sb()}};angular.k.na=function(a,b,c){return a.match(b)?null:c||"Value does not match expected format "+b+"."};angular.k.tb=function(a,b,c){var e=1*a;if(e==a){if(typeof b!="undefined"&&e<b)return"Value can not be less than "+b+".";if(typeof b!="undefined"&&e>c)return"Value can not be greater than "+c+".";return null}else return"Value is not a number."};angular.k.Xe=function(a,b,c){b=angular.k.tb(a,b,c);if(b===null&&a!=Math.round(a))return"Value is not a whole number.";return b};
angular.k.bd=function(a){if(a.match(/^\d\d?\/\d\d?\/\d\d\d\d$/))return null;return"Value is not a date. (Expecting format: 12/31/2009)."};angular.k.Af=function(a){if(a.match(/^\d\d\d-\d\d-\d\d\d\d$/))return null;return"SSN needs to be in 999-99-9999 format."};angular.k.gd=function(a){if(a.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/))return null;return"Email needs to be in username@host.com format."};
angular.k.pf=function(a){if(a.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/))return null;if(a.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/))return null;return"Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly."};angular.k.url=function(a){if(a.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/))return null;return"URL needs to be in http://server[:port]/path format."};angular.k.rd=function(a){try{nglr.za(a);return null}catch(b){return b.toString()}};nglr.Qc=function(a,b){this.yd=0;this.Ja=a;this.X=b;this.Rb=swfobject.Rb;this.Jd=v()};
function ja(a,b,c){var e,d=b.e("type").toLowerCase(),f=b.e("name");if(f)f=f.split(":").pop();var i="change",j=true;if(d=="button"||d=="submit"||d=="reset"||d=="image"){e=new nglr.Na(b[0],f);i="click";j=false}else if(d=="text"||d=="textarea"||d=="hidden"||d=="password"){e=new nglr.bb(b[0],f);i="keyup change"}else if(d=="checkbox"){e=new nglr.Oa(b[0],f);i="click"}else if(d=="radio"){e=new nglr.Wa(b[0],f);i="click"}else if(d=="select-one")e=new nglr.Ya(b[0],f);else if(d=="select-multiple")e=new nglr.ua(b[0],
f);else if(d=="file")e=Ea(a,b,f);else throw"Unknown type: "+d;b.data("controller",e);var n=c.i("$binder");jQuery(e.view,":input").u(i,function(){if(e.j(c)){var m=jQuery(e.view).e("ng-action")||"";Z(c,e,m)&&n.c(c)}return j});return e}
function Ea(a,b){var c="__uploadWidget_"+a.yd++,e=nglr.T.Z(c);b.Tc(e);c=a.Rb({data:a.Ja+"/admin/ServerAPI.swf",width:"95",height:"20",align:"top",Nf:"transparent"},{Re:"uploadWidgetId="+c,pe:"always"},c);b.remove();a=new nglr.T(e,b[0].name,c,a.Ja+"/data/"+a.X);jQuery(c).data("controller",a);return a}nglr.T=function(a,b,c,e){this.view=a;this.Jf=c;this.xc=b;this.qe=e+"/_attachments";this.value=null;this.d=undefined};
nglr.T.dispatchEvent=function(a,b,c){a=document.getElementById(a);a=jQuery(a).data("controller");nglr.T.prototype["_on_"+b].apply(a,c)};nglr.T.Z=function(a){return jQuery('<span class="ng-upload-widget"><input type="checkbox" ng-non-bindable="true"/><object id="'+a+'" /><a></a><span/></span>')};nglr.T.prototype.j=function(a){var b=this.view.find("input").e("checked")?this.value:null;if(this.d===b)return false;else{E(a,this.xc,b);return true}};
nglr.T.prototype.c=function(a){if((a=a.i(this.xc))&&this.value!==a){this.value=a;this.view.find("a").e("href",this.value.url).text(this.value.text);this.view.find("span").text(angular.filter.gb(this.value.size))}this.view.find("input").e("checked",!!a)};nglr.ba=x("view");nglr.ba.prototype.j=z(true);nglr.ba.prototype.c=v();nglr.ba.od=new nglr.ba;nglr.Na=x("view");nglr.Na.prototype.j=z(true);nglr.Na.prototype.c=v();
nglr.bb=function(a,b){this.view=a;this.exp=b;this.k=a.getAttribute("ng-validate");this.Nd=typeof a.attributes["ng-required"]!="undefined";this.hc=null;this.d=undefined;this.H=a.value;a.getAttribute("ng-widget")==="datepicker"&&jQuery(a).Ne()};nglr.bb.prototype.j=function(a){var b=this.view.value;if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}};
nglr.bb.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}c=c?c:"";if(this.d!=c)this.d=b.value=c;var e=false;b.removeAttribute("ng-error");if(this.Nd)e=!(c&&c.length>0);var d=e?"Required Value":null;if(!e&&this.k&&c){e=this.k;c=c;d=nglr.h.ya[e];if(!d){d=(new nglr.q(e)).k();nglr.h.ya[e]=d}e={scope:a};d=d(e)(e,c);e=!!d}if(this.hc!==d){this.hc=e;if(d!==null){b.setAttribute("ng-error",d);a.I.he.push(this)}jQuery(b).zb("ng-validation-error",e)}};
nglr.Oa=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.H=a.checked?a.value:""};nglr.Oa.prototype.j=function(a){var b=this.view;b=b.checked?b.value:"";if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}};nglr.Oa.prototype.c=function(a){var b=this.view,c=a.eval(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}b.checked=b.value==""+c};nglr.Ya=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.H=a.value};
nglr.Ya.prototype.j=function(a){if(this.view.selectedIndex<0)$(a,this.exp,null);else{var b=this.view.value;if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}}};nglr.Ya.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}if(c!==this.d){b.value=c?c:"";this.d=c}};nglr.ua=function(a,b){this.view=a;this.exp=b;this.d=undefined;this.H=this.selected()};
nglr.ua.prototype.selected=function(){for(var a=[],b=this.view.options,c=0;c<b.length;c++){var e=b[c];e.selected&&a.push(e.value)}return a};nglr.ua.prototype.j=function(a){var b=this.selected();if(this.d===b)return false;else{$(a,this.exp,b);this.d=b;return true}};nglr.ua.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(typeof c==="undefined"){c=this.H;$(a,this.exp,c)}if(c!==this.d){a=b.options;for(b=0;b<a.length;b++){var e=a[b];e.selected=_.Aa(c,e.value)}this.d=c}};
nglr.Wa=function(a,b){this.view=a;this.exp=b;this.d=this.ob=undefined;this.cc=a.value;this.H=a.checked?a.value:null};nglr.Wa.prototype.j=function(a){var b=this.view;if(this.ob)return false;else{b.checked=true;this.d=$(a,this.exp,this.cc);return this.ob=true}};nglr.Wa.prototype.c=function(a){var b=this.view,c=a.i(this.exp);if(this.H&&typeof c==="undefined"){c=this.H;$(a,this.exp,c)}if(this.d!=c){this.ob=b.checked=this.cc==""+c;this.d=c}};
nglr.S=function(a,b){this.view=a;this.exp=nglr.g.Ha(b);this.O=false;this.Od={element:a}};
nglr.S.zc=function(a){var b=nglr.mb;switch(typeof a){case "string":case "boolean":case "number":return b(a);case "function":return nglr.S.zc(a());case "object":if(nglr.gc(a))return nglr.outerHTML(a);else if(a instanceof angular.filter.s){switch(typeof a.B){case "string":case "number":return a.B;case "function":return a.B();case "object":if(nglr.gc(a.B))return nglr.outerHTML(a.B);default:break}switch(typeof a.text){case "string":case "number":return b(a.text);case "function":return b(a.text());default:break}}if(a===
null)return"";return b(nglr.m(a,true));default:return""}};nglr.S.prototype.j=v();nglr.S.prototype.c=function(a){for(var b=[],c=this.exp,e=c.length,d=0;d<e;d++){var f=c[d],i=nglr.g.ea(f);if(i){Z(a,this,i,this.Od,function(j){b.push(nglr.S.zc(j))},function(j,n){nglr.yc(this.view,n)});if(this.O)return}else b.push(nglr.mb(f))}nglr.yc(this.view,b.join(""))};nglr.Ma=function(a,b){this.view=a;this.Vc=b};nglr.Ma.prototype.j=v();
nglr.Ma.prototype.c=function(a){var b=jQuery(this.view),c=this.Vc;if(this.O){this.O=false;b.wc("ng-exception").vc("ng-error")}var e=b.$e("img");for(var d in c){for(var f=nglr.g.Ha(c[d]),i=[],j=0;j<f.length;j++){var n=nglr.g.ea(f[j]);if(n)try{var m=a.eval(n,{element:b[0],attrName:d});if(m&&(m.constructor!==nglr.Mb||m.length!==0))i.push(m)}catch(o){this.O=true;console.error("BindAttrUpdater",o);n=nglr.m(o,true);i.push("["+n+"]");b.fb("ng-exception").e("ng-error",n)}else i.push(f[j])}f=i.length?i.join(""):
null;if(e&&d=="src"&&!f)f=a.i("config.server")+"/images/blank.gif";b.e(d,f)}};nglr.Ta=function(a,b){this.view=a;this.exp=b;this.O=false};nglr.Ta.prototype.j=v();nglr.Ta.prototype.c=function(a){Z(a,this,this.exp)};nglr.Ua=function(a,b){this.view=a;this.exp=b};nglr.Ua.prototype.j=v();nglr.Ua.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=jQuery(this.view);nglr.xb(b)?c.bc():c.wb()})};nglr.Za=function(a,b){this.view=a;this.exp=b};nglr.Za.prototype.j=v();
nglr.Za.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=jQuery(this.view);nglr.xb(b)?c.wb():c.bc()})};nglr.Ra=function(a,b){this.view=a;this.exp=b};nglr.Ra.prototype.j=v();nglr.Ra.prototype.c=function(a){Z(a,this,this.exp,{},function(b){if(b!==null&&b!==undefined)this.view.className=b})};nglr.Pa=function(a,b){this.view=a;this.exp=b};nglr.Pa.prototype.j=v();nglr.Pa.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=a.i("$index");jQuery(this.view).zb(b,c%2===1)})};
nglr.Qa=function(a,b){this.view=a;this.exp=b};nglr.Qa.prototype.j=v();nglr.Qa.prototype.c=function(a){Z(a,this,this.exp,{},function(b){var c=a.i("$index");jQuery(this.view).zb(b,c%2===0)})};nglr.ab=function(a,b){this.view=a;this.exp=b};nglr.ab.prototype.j=v();nglr.ab.prototype.c=function(a){Z(a,this,this.exp,{},function(b){jQuery(this.view).e("style","").kb(b)})};
nglr.Xa=function(a,b,c,e){this.view=a;this.Z=c;this.prefix=e;this.xa=[];a=b.match(/^\s*(.+)\s+in\s+(.*)\s*$/);if(!a)throw"Expected ng-repeat in form of 'item in collection' but got '"+b+"'.";b=a[1];this.Ca=a[2];a=b.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);if(!a)throw"'item' in 'item in collection' should be identifier or (key, value) but get '"+b+"'.";this.Zd=a[3]||a[1];this.td=a[2]};nglr.Xa.prototype.j=v();
nglr.Xa.prototype.c=function(a){Z(a,this,this.Ca,{},function(b){var c=this;if(!b){b=[];Aa(a,this.Ca)&&E(a,this.Ca,b)}var e=b.length,d=this.xa.length,f=this.view,i=0,j=null,n=this.td,m=this.Zd,o=0;jQuery.a(b,function(q,w){if(o<d){j=c.xa[o];E(j.scope,m,w)}else{var t=new nglr.h(a.I,c.prefix+m+" in "+c.Ca+"["+o+"]");E(t,"$index",o);n&&E(t,n,q);E(t,m,w);j={scope:t,element:c.Z(t,c.prefix,o)};f.Tc(j.element);c.xa.push(j)}f=j.element;q=(new Date).getTime();j.scope.c();i+=(new Date).getTime()-q;o++});for(b=
d;b>e;--b)this.xa.pop().element.removeNode();if(j&&j.element[0].nodeName==="OPTION")if(e=jQuery(j.element[0].parentNode).data("controller")){e.d=undefined;e.c(a)}})};nglr.A=x("F");nglr.A.Fb="mouseleave mouseout click dblclick keypress keyup";nglr.A.prototype.u=function(){this.F.find(".ng-validation-error,.ng-exception").vd("mouseover",nglr.A.Kd)};
nglr.A.Kd=function(){nglr.A.ub();var a=jQuery(this);a.u(nglr.A.Fb,nglr.A.ub);var b=a.position(),c=document.documentElement,e=(self.innerWidth||c&&c.clientWidth||document.body.clientWidth)-b.left;c=a.Ue("ng-exception")?"EXCEPTION:":"Validation error...";a=a.e("ng-error");e=e>375?"left":"right";c=jQuery("<div id='ng-callout' style='width:300px'><div class='ng-arrow-"+e+"'/><div class='ng-title'>"+c+"</div><div class='ng-content'>"+a+"</div></div>");jQuery("body").append(c);if(e==="left")a=b.left+this.offsetWidth+
11;else{a=b.left-315;c.find(".ng-arrow-right").kb({left:301})}c.kb({left:a+"px",top:b.top-3+"px"});return true};nglr.A.ub=function(){jQuery("#ng-callout").Gf(nglr.A.Fb,nglr.A.ub).remove();return true};nglr.$a=function(a){this.lc=a.append(nglr.$a.Ic).find("#ng-loading");this.Ia=0};nglr.$a.Ic='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>';function Da(a){a.Ia===0&&a.lc.wb();a.Ia++};(function(a){for(var b=/(.*)\/angular-(.*).js(#(.*))?/,c=document.getElementsByTagName("script"),e={Xc:true,Wc:true,Nb:false},d=0;d<c.length;d++){var f=c[d].src;if(f&&f.match(b)){f=f.match(b);if(f[2]=="bootstrap")e.Nb=true;e.C=f[1]||"";if(!e.C)e.C=window.location.toString().split(window.location.pathname)[0];if(f[4]){f=f[4].split("&");for(var i=0;i<f.length;i++){var j=f[i].split("="),n=j[0];j=j.length==1?true:j[1];if(j=="false")j=false;if(j=="true")j=true;e[n]=j}}}}function m(o,q){q=q||e.C;document.write('<script type="text/javascript" src="'+
q+o+'"><\/script>')}if(e.Nb){m("/javascripts/webtoolkit.base64.js");m("/javascripts/swfobject.js");m("/javascripts/jQuery/jquery-1.3.2.js");m("/javascripts/jQuery/jquery-ui-1.7.1.custom.min.js");m("/javascripts/underscore/underscore.js");m("/javascripts/nglr/Loader.js");m("/javascripts/nglr/API.js");m("/javascripts/nglr/Binder.js");m("/javascripts/nglr/ControlBar.js");m("/javascripts/nglr/DataStore.js");m("/javascripts/nglr/Filters.js");m("/javascripts/nglr/JSON.js");m("/javascripts/nglr/Model.js");
m("/javascripts/nglr/Parser.js");m("/javascripts/nglr/Scope.js");m("/javascripts/nglr/Server.js");m("/javascripts/nglr/Users.js");m("/javascripts/nglr/Validators.js");m("/javascripts/nglr/Widgets.js")}else{m("/ajax/libs/swfobject/2.2/swfobject.js","http://ajax.googleapis.com");m("/ajax/libs/jquery/1.3.2/jquery.min.js","http://ajax.googleapis.com");m("/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js","http://ajax.googleapis.com")}window.onload=function(){window.Lb.nd=function(h,k){var l=_.fa(e||{});_.extend(l,
k);(new nglr.Va(h,jQuery("head"),l)).load()};var o=window.document;if(e.Yc){o=null;for(var q=e.Yc.split("|"),w=0;w<q.length&&!o;w++){var t=q[w].split("?"),y=t[0];if(t.length>1)if(!window.document.getElementById(t[1]))continue;o=window.document.getElementById(y)}}e.Wc&&o&&window.Lb.nd(o);if(typeof a==="function")try{a.apply(this,arguments)}catch(g){}}})(window.onload);

16
jsTestDriver.conf Normal file
View file

@ -0,0 +1,16 @@
server: http://localhost:9876
load:
- lib/swfobject/swfobject.js
- lib/webtoolkit/webtoolkit.base64.js
- lib/jquery/jquery-1.3.2.js
- lib/jquery/jquery-ui-1.7.1.custom.min.js
- lib/underscore/underscore.js
- src/Loader.js
- src/*.js
- src/test/_namespace.js
- src/test/*.js
- test/testabilityPatch.js
- test/test/*.js
- test/*.js

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

193
lib/compiler-closure/README Normal file
View file

@ -0,0 +1,193 @@
/*
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//
// Contents
//
The Closure Compiler performs checking, instrumentation, and
optimizations on JavaScript code. The purpose of this README is to
explain how to build and run the Closure Compiler.
The Closure Compiler requires Java 6 or higher.
http://www.java.com/
//
// Building The Closure Compiler
//
There are three ways to get a Closure Compiler executable.
1) Use one we built for you.
Pre-built Closure binaries can be found at
http://code.google.com/p/closure-compiler/downloads/list
2) Check out the source and build it with Apache Ant.
First, check out the full source tree of the Closure Compiler. There
are instructions on how to do this at the project site.
http://code.google.com/p/closure-compiler/source/checkout
Apache Ant is a cross-platform build tool.
http://ant.apache.org/
At the root of the source tree, there is an Ant file named
build.xml. To use it, navigate to the same directory and type the
command
ant jar
This will produce a jar file called "build/compiler.jar".
3) Check out the source and build it with Eclipse.
Eclipse is a cross-platform IDE.
http://www.eclipse.org/
Under Eclipse's File menu, click "New > Project ..." and create a
"Java Project." You will see an options screen. Give the project a
name, select "Create project from existing source," and choose the
root of the checked-out source tree as the existing directory. Verify
that you are using JRE version 6 or higher.
Eclipse can use the build.xml file to discover rules. When you
navigate to the build.xml file, you will see all the build rules in
the "Outline" pane. Run the "jar" rule to build the compiler in
build/compiler.jar.
//
// Running The Closure Compiler
//
Once you have the jar binary, running the Closure Compiler is straightforward.
On the command line, type
java -jar compiler.jar
This starts the compiler in interactive mode. Type
var x = 17 + 25;
then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux)
and "Enter" again. The Compiler will respond:
var x=42;
The Closure Compiler has many options for reading input from a file,
writing output to a file, checking your code, and running
optimizations. To learn more, type
java -jar compiler.jar --help
You can read more detailed documentation about the many flags at
http://code.google.com/closure/compiler/docs/gettingstarted_app.html
//
// Compiling Multiple Scripts
//
If you have multiple scripts, you should compile them all together with
one compile command.
java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js
The Closure Compiler will concatenate the files in the order they're
passed at the command line.
If you need to compile many, many scripts together, you may start to
run into problems with managing dependencies between scripts. You
should check out the Closure Library. It contains functions for
enforcing dependencies between scripts, and a tool called calcdeps.py
that knows how to give scripts to the Closure Compiler in the right
order.
http://code.google.com/p/closure-library/
//
// Licensing
//
Unless otherwise stated, all source files are licensed under
the Apache License, Version 2.0.
-----
Code under:
src/com/google/javascript/rhino
test/com/google/javascript/rhino
URL: http://www.mozilla.org/rhino
Version: 1.5R3, with heavy modifications
License: Netscape Public License and MPL / GPL dual license
Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an
implementation of JavaScript for the JVM. The JavaScript parser and
the parse tree data structures were extracted and modified
significantly for use by Google's JavaScript compiler.
Local Modifications: The packages have been renamespaced. All code not
relavant to parsing has been removed. A JSDoc parser and static typing
system have been added.
-----
Code in:
lib/libtrunk_rhino_parser_jarjared.jar
URL: http://www.mozilla.org/rhino
Version: Trunk
License: Netscape Public License and MPL / GPL dual license
Description: Mozilla Rhino is an implementation of JavaScript for the JVM.
Local Modifications: None. We've used JarJar to renamespace the code
post-compilation. See:
http://code.google.com/p/jarjar/
-----
Code in:
lib/google_common.jar
URL: http://code.google.com/p/guava-libraries/
Version: Trunk
License: Apache License 2.0
Description: Google's core Java libraries.
Local Modifications: None.
----
Code in:
lib/junit.jar
URL: http://sourceforge.net/projects/junit/
Version: 3.8.1
License: Common Public License 1.0
Description: A framework for writing and running automated tests in Java.
Local Modifications: None.

Binary file not shown.

4376
lib/jquery/jquery-1.3.2.js vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

BIN
lib/jsl/jsl Executable file

Binary file not shown.

128
lib/jsl/jsl.default.conf Executable file
View file

@ -0,0 +1,128 @@
#
# Configuration File for JavaScript Lint 0.3.0
# Developed by Matthias Miller (http://www.JavaScriptLint.com)
#
# This configuration file can be used to lint a collection of scripts, or to enable
# or disable warnings for scripts that are linted via the command line.
#
### Warnings
# Enable or disable warnings based on requirements.
# Use "+WarningName" to display or "-WarningName" to suppress.
#
-no_return_value # function {0} does not always return a value
+duplicate_formal # duplicate formal argument {0}
-equal_as_assign # test for equality (==) mistyped as assignment (=)?{0}
+var_hides_arg # variable {0} hides argument
+redeclared_var # redeclaration of {0} {1}
-anon_no_return_value # anonymous function does not always return a value
+missing_semicolon # missing semicolon
+meaningless_block # meaningless block; curly braces have no impact
+comma_separated_stmts # multiple statements separated by commas (use semicolons?)
+unreachable_code # unreachable code
-missing_break # missing break statement
+missing_break_for_last_case # missing break statement for last case in switch
+comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==)
+inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement
+useless_void # use of the void type may be unnecessary (void is always undefined)
+multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs
+use_of_label # use of label
-block_without_braces # block statement without curly braces
+leading_decimal_point # leading decimal point may indicate a number or an object member
+trailing_decimal_point # trailing decimal point may indicate a number or an object member
+octal_number # leading zeros make an octal number
+nested_comment # nested comment
-misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma
+ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement
+empty_statement # empty statement or extra semicolon
-missing_option_explicit # the "option explicit" control comment is missing
+partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag
+dup_option_explicit # duplicate "option explicit" control comment
+useless_assign # useless assignment
+ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity
+ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent)
+missing_default_case # missing default case in switch statement
+duplicate_case_in_switch # duplicate case in switch statements
+default_not_at_end # the default case is not at the end of the switch statement
+legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax
+jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax
+useless_comparison # useless comparison; comparing identical expressions
+with_statement # with statement hides undeclared variables; use temporary variable instead
+trailing_comma_in_array # extra comma is not recommended in array initializers
+assign_to_function_call # assignment to a function call
+parseint_missing_radix # parseInt missing radix parameter
### Output format
# Customize the format of the error message.
# __FILE__ indicates current file path
# __FILENAME__ indicates current file name
# __LINE__ indicates current line
# __ERROR__ indicates error message
#
# Visual Studio syntax (default):
+output-format __FILE__(__LINE__): __ERROR__
# Alternative syntax:
#+output-format __FILE__:__LINE__: __ERROR__
### Context
# Show the in-line position of the error.
# Use "+context" to display or "-context" to suppress.
#
+context
### Semicolons
# By default, assignments of an anonymous function to a variable or
# property (such as a function prototype) must be followed by a semicolon.
#
+lambda_assign_requires_semicolon
### Control Comments
# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for
# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is
# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason,
# although legacy control comments are enabled by default for backward compatibility.
#
+legacy_control_comments
### JScript Function Extensions
# JScript allows member functions to be defined like this:
# function MyObj() { /*constructor*/ }
# function MyObj.prototype.go() { /*member function*/ }
#
# It also allows events to be attached like this:
# function window::onload() { /*init page*/ }
#
# This is a Microsoft-only JavaScript extension. Enable this setting to allow them.
#
-jscript_function_extensions
### Defining identifiers
# By default, "option explicit" is enabled on a per-file basis.
# To enable this for all files, use "+always_use_option_explicit"
-always_use_option_explicit
# Define certain identifiers of which the lint is not aware.
# (Use this in conjunction with the "undeclared identifier" warning.)
#
# Common uses for webpages might be:
#+define window
#+define document
### Files
# Specify which files to lint
# Use "+recurse" to enable recursion (disabled by default).
# To add a set of files, use "+process FileName", "+process Folder\Path\*.js",
# or "+process Folder\Path\*.htm".
#
+process src/*.js
+process src/test/*.js
+process test/*.js
+process test/test/*.js

Binary file not shown.

BIN
lib/shrinksafe/js.jar Normal file

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,609 @@
// Underscore.js
// (c) 2009 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the terms of the MIT license.
// Portions of Underscore are inspired by or borrowed from Prototype.js,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore/
(function() {
/*------------------------- Baseline setup ---------------------------------*/
// Establish the root object, "window" in the browser, or "global" on the server.
var root = this;
// Save the previous value of the "_" variable.
var previousUnderscore = root._;
// If Underscore is called as a function, it returns a wrapped object that
// can be used OO-style. This wrapper holds altered versions of all the
// underscore functions. Wrapped objects may be chained.
var wrapper = function(obj) { this._wrapped = obj; };
// Establish the object that gets thrown to break out of a loop iteration.
var breaker = typeof StopIteration !== 'undefined' ? StopIteration : '__break__';
// Create a safe reference to the Underscore object for reference below.
var _ = root._ = function(obj) { return new wrapper(obj); };
// Export the Underscore object for CommonJS.
if (typeof exports !== 'undefined') exports._ = _;
// Create quick reference variables for speed access to core prototypes.
var slice = Array.prototype.slice,
unshift = Array.prototype.unshift,
toString = Object.prototype.toString,
hasOwnProperty = Object.prototype.hasOwnProperty,
propertyIsEnumerable = Object.prototype.propertyIsEnumerable;
// Current version.
_.VERSION = '0.5.1';
/*------------------------ Collection Functions: ---------------------------*/
// The cornerstone, an each implementation.
// Handles objects implementing forEach, arrays, and raw objects.
_.each = function(obj, iterator, context) {
var index = 0;
try {
if (obj.forEach) {
obj.forEach(iterator, context);
} else if (_.isArray(obj) || _.isArguments(obj)) {
for (var i=0, l=obj.length; i<l; i++) iterator.call(context, obj[i], i, obj);
} else {
var keys = _.keys(obj), l = keys.length;
for (var i=0; i<l; i++) iterator.call(context, obj[keys[i]], keys[i], obj);
}
} catch(e) {
if (e != breaker) throw e;
}
return obj;
};
// Return the results of applying the iterator to each element. Use JavaScript
// 1.6's version of map, if possible.
_.map = function(obj, iterator, context) {
if (obj && _.isFunction(obj.map)) return obj.map(iterator, context);
var results = [];
_.each(obj, function(value, index, list) {
results.push(iterator.call(context, value, index, list));
});
return results;
};
// Reduce builds up a single result from a list of values. Also known as
// inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible.
_.reduce = function(obj, memo, iterator, context) {
if (obj && _.isFunction(obj.reduce)) return obj.reduce(_.bind(iterator, context), memo);
_.each(obj, function(value, index, list) {
memo = iterator.call(context, memo, value, index, list);
});
return memo;
};
// The right-associative version of reduce, also known as foldr. Uses
// JavaScript 1.8's version of reduceRight, if available.
_.reduceRight = function(obj, memo, iterator, context) {
if (obj && _.isFunction(obj.reduceRight)) return obj.reduceRight(_.bind(iterator, context), memo);
var reversed = _.clone(_.toArray(obj)).reverse();
_.each(reversed, function(value, index) {
memo = iterator.call(context, memo, value, index, obj);
});
return memo;
};
// Return the first value which passes a truth test.
_.detect = function(obj, iterator, context) {
var result;
_.each(obj, function(value, index, list) {
if (iterator.call(context, value, index, list)) {
result = value;
_.breakLoop();
}
});
return result;
};
// Return all the elements that pass a truth test. Use JavaScript 1.6's
// filter(), if it exists.
_.select = function(obj, iterator, context) {
if (obj && _.isFunction(obj.filter)) return obj.filter(iterator, context);
var results = [];
_.each(obj, function(value, index, list) {
iterator.call(context, value, index, list) && results.push(value);
});
return results;
};
// Return all the elements for which a truth test fails.
_.reject = function(obj, iterator, context) {
var results = [];
_.each(obj, function(value, index, list) {
!iterator.call(context, value, index, list) && results.push(value);
});
return results;
};
// Determine whether all of the elements match a truth test. Delegate to
// JavaScript 1.6's every(), if it is present.
_.all = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (obj && _.isFunction(obj.every)) return obj.every(iterator, context);
var result = true;
_.each(obj, function(value, index, list) {
if (!(result = result && iterator.call(context, value, index, list))) _.breakLoop();
});
return result;
};
// Determine if at least one element in the object matches a truth test. Use
// JavaScript 1.6's some(), if it exists.
_.any = function(obj, iterator, context) {
iterator = iterator || _.identity;
if (obj && _.isFunction(obj.some)) return obj.some(iterator, context);
var result = false;
_.each(obj, function(value, index, list) {
if (result = iterator.call(context, value, index, list)) _.breakLoop();
});
return result;
};
// Determine if a given value is included in the array or object,
// based on '==='.
_.include = function(obj, target) {
if (_.isArray(obj)) return _.indexOf(obj, target) != -1;
var found = false;
_.each(obj, function(value) {
if (found = value === target) _.breakLoop();
});
return found;
};
// Invoke a method with arguments on every item in a collection.
_.invoke = function(obj, method) {
var args = _.rest(arguments, 2);
return _.map(obj, function(value) {
return (method ? value[method] : value).apply(value, args);
});
};
// Convenience version of a common use case of map: fetching a property.
_.pluck = function(obj, key) {
return _.map(obj, function(value){ return value[key]; });
};
// Return the maximum item or (item-based computation).
_.max = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
var result = {computed : -Infinity};
_.each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed >= result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Return the minimum element (or element-based computation).
_.min = function(obj, iterator, context) {
if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
var result = {computed : Infinity};
_.each(obj, function(value, index, list) {
var computed = iterator ? iterator.call(context, value, index, list) : value;
computed < result.computed && (result = {value : value, computed : computed});
});
return result.value;
};
// Sort the object's values by a criteria produced by an iterator.
_.sortBy = function(obj, iterator, context) {
return _.pluck(_.map(obj, function(value, index, list) {
return {
value : value,
criteria : iterator.call(context, value, index, list)
};
}).sort(function(left, right) {
var a = left.criteria, b = right.criteria;
return a < b ? -1 : a > b ? 1 : 0;
}), 'value');
};
// Use a comparator function to figure out at what index an object should
// be inserted so as to maintain order. Uses binary search.
_.sortedIndex = function(array, obj, iterator) {
iterator = iterator || _.identity;
var low = 0, high = array.length;
while (low < high) {
var mid = (low + high) >> 1;
iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
}
return low;
};
// Convert anything iterable into a real, live array.
_.toArray = function(iterable) {
if (!iterable) return [];
if (iterable.toArray) return iterable.toArray();
if (_.isArray(iterable)) return iterable;
if (_.isArguments(iterable)) return slice.call(iterable);
return _.map(iterable, function(val){ return val; });
};
// Return the number of elements in an object.
_.size = function(obj) {
return _.toArray(obj).length;
};
/*-------------------------- Array Functions: ------------------------------*/
// Get the first element of an array. Passing "n" will return the first N
// values in the array. Aliased as "head". The "guard" check allows it to work
// with _.map.
_.first = function(array, n, guard) {
return n && !guard ? slice.call(array, 0, n) : array[0];
};
// Returns everything but the first entry of the array. Aliased as "tail".
// Especially useful on the arguments object. Passing an "index" will return
// the rest of the values in the array from that index onward. The "guard"
//check allows it to work with _.map.
_.rest = function(array, index, guard) {
return slice.call(array, _.isUndefined(index) || guard ? 1 : index);
};
// Get the last element of an array.
_.last = function(array) {
return array[array.length - 1];
};
// Trim out all falsy values from an array.
_.compact = function(array) {
return _.select(array, function(value){ return !!value; });
};
// Return a completely flattened version of an array.
_.flatten = function(array) {
return _.reduce(array, [], function(memo, value) {
if (_.isArray(value)) return memo.concat(_.flatten(value));
memo.push(value);
return memo;
});
};
// Return a version of the array that does not contain the specified value(s).
_.without = function(array) {
var values = _.rest(arguments);
return _.select(array, function(value){ return !_.include(values, value); });
};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
_.uniq = function(array, isSorted) {
return _.reduce(array, [], function(memo, el, i) {
if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el);
return memo;
});
};
// Produce an array that contains every item shared between all the
// passed-in arrays.
_.intersect = function(array) {
var rest = _.rest(arguments);
return _.select(_.uniq(array), function(item) {
return _.all(rest, function(other) {
return _.indexOf(other, item) >= 0;
});
});
};
// Zip together multiple lists into a single array -- elements that share
// an index go together.
_.zip = function() {
var args = _.toArray(arguments);
var length = _.max(_.pluck(args, 'length'));
var results = new Array(length);
for (var i=0; i<length; i++) results[i] = _.pluck(args, String(i));
return results;
};
// If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
// we need this function. Return the position of the first occurence of an
// item in an array, or -1 if the item is not included in the array.
_.indexOf = function(array, item) {
if (array.indexOf) return array.indexOf(item);
for (var i=0, l=array.length; i<l; i++) if (array[i] === item) return i;
return -1;
};
// Provide JavaScript 1.6's lastIndexOf, delegating to the native function,
// if possible.
_.lastIndexOf = function(array, item) {
if (array.lastIndexOf) return array.lastIndexOf(item);
var i = array.length;
while (i--) if (array[i] === item) return i;
return -1;
};
// Generate an integer Array containing an arithmetic progression. A port of
// the native Python range() function. See:
// http://docs.python.org/library/functions.html#range
_.range = function(start, stop, step) {
var a = _.toArray(arguments);
var solo = a.length <= 1;
var start = solo ? 0 : a[0], stop = solo ? a[0] : a[1], step = a[2] || 1;
var len = Math.ceil((stop - start) / step);
if (len <= 0) return [];
var range = new Array(len);
for (var i = start, idx = 0; true; i += step) {
if ((step > 0 ? i - stop : stop - i) >= 0) return range;
range[idx++] = i;
}
};
/* ----------------------- Function Functions: -----------------------------*/
// Create a function bound to a given object (assigning 'this', and arguments,
// optionally). Binding with arguments is also known as 'curry'.
_.bind = function(func, obj) {
var args = _.rest(arguments, 2);
return function() {
return func.apply(obj || root, args.concat(_.toArray(arguments)));
};
};
// Bind all of an object's methods to that object. Useful for ensuring that
// all callbacks defined on an object belong to it.
_.bindAll = function(obj) {
var funcs = _.rest(arguments);
if (funcs.length == 0) funcs = _.functions(obj);
_.each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
return obj;
};
// Delays a function for the given number of milliseconds, and then calls
// it with the arguments supplied.
_.delay = function(func, wait) {
var args = _.rest(arguments, 2);
return setTimeout(function(){ return func.apply(func, args); }, wait);
};
// Defers a function, scheduling it to run after the current call stack has
// cleared.
_.defer = function(func) {
return _.delay.apply(_, [func, 1].concat(_.rest(arguments)));
};
// Returns the first function passed as an argument to the second,
// allowing you to adjust arguments, run code before and after, and
// conditionally execute the original function.
_.wrap = function(func, wrapper) {
return function() {
var args = [func].concat(_.toArray(arguments));
return wrapper.apply(wrapper, args);
};
};
// Returns a function that is the composition of a list of functions, each
// consuming the return value of the function that follows.
_.compose = function() {
var funcs = _.toArray(arguments);
return function() {
var args = _.toArray(arguments);
for (var i=funcs.length-1; i >= 0; i--) {
args = [funcs[i].apply(this, args)];
}
return args[0];
};
};
/* ------------------------- Object Functions: ---------------------------- */
// Retrieve the names of an object's properties.
_.keys = function(obj) {
if(_.isArray(obj)) return _.range(0, obj.length);
var keys = [];
for (var key in obj) if (hasOwnProperty.call(obj, key)) keys.push(key);
return keys;
};
// Retrieve the values of an object's properties.
_.values = function(obj) {
return _.map(obj, _.identity);
};
// Return a sorted list of the function names available in Underscore.
_.functions = function(obj) {
return _.select(_.keys(obj), function(key){ return _.isFunction(obj[key]); }).sort();
};
// Extend a given object with all of the properties in a source object.
_.extend = function(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
};
// Create a (shallow-cloned) duplicate of an object.
_.clone = function(obj) {
if (_.isArray(obj)) return obj.slice(0);
return _.extend({}, obj);
};
// Perform a deep comparison to check if two objects are equal.
_.isEqual = function(a, b) {
// Check object identity.
if (a === b) return true;
// Different types?
var atype = typeof(a), btype = typeof(b);
if (atype != btype) return false;
// Basic equality test (watch out for coercions).
if (a == b) return true;
// One is falsy and the other truthy.
if ((!a && b) || (a && !b)) return false;
// One of them implements an isEqual()?
if (a.isEqual) return a.isEqual(b);
// Check dates' integer values.
if (_.isDate(a) && _.isDate(b)) return a.getTime() === b.getTime();
// Both are NaN?
if (_.isNaN(a) && _.isNaN(b)) return true;
// Compare regular expressions.
if (_.isRegExp(a) && _.isRegExp(b))
return a.source === b.source &&
a.global === b.global &&
a.ignoreCase === b.ignoreCase &&
a.multiline === b.multiline;
// If a is not an object by this point, we can't handle it.
if (atype !== 'object') return false;
// Check for different array lengths before comparing contents.
if (a.length && (a.length !== b.length)) return false;
// Nothing else worked, deep compare the contents.
var aKeys = _.keys(a), bKeys = _.keys(b);
// Different object sizes?
if (aKeys.length != bKeys.length) return false;
// Recursive comparison of contents.
for (var key in a) if (!_.isEqual(a[key], b[key])) return false;
return true;
};
// Is a given array or object empty?
_.isEmpty = function(obj) {
return _.keys(obj).length == 0;
};
// Is a given value a DOM element?
_.isElement = function(obj) {
return !!(obj && obj.nodeType == 1);
};
// Is a given variable an arguments object?
_.isArguments = function(obj) {
return obj && _.isNumber(obj.length) && !_.isArray(obj) && !propertyIsEnumerable.call(obj, 'length');
};
// Is the given value NaN -- this one is interesting. NaN != NaN, and
// isNaN(undefined) == true, so we make sure it's a number first.
_.isNaN = function(obj) {
return _.isNumber(obj) && isNaN(obj);
};
// Is a given value equal to null?
_.isNull = function(obj) {
return obj === null;
};
// Is a given variable undefined?
_.isUndefined = function(obj) {
return typeof obj == 'undefined';
};
// Define the isArray, isDate, isFunction, isNumber, isRegExp, and isString
// functions based on their toString identifiers.
var types = ['Array', 'Date', 'Function', 'Number', 'RegExp', 'String'];
for (var i=0, l=types.length; i<l; i++) {
(function() {
var identifier = '[object ' + types[i] + ']';
_['is' + types[i]] = function(obj) { return toString.call(obj) == identifier; };
})();
}
/* -------------------------- Utility Functions: -------------------------- */
// Run Underscore.js in noConflict mode, returning the '_' variable to its
// previous owner. Returns a reference to the Underscore object.
_.noConflict = function() {
root._ = previousUnderscore;
return this;
};
// Keep the identity function around for default iterators.
_.identity = function(value) {
return value;
};
// Break out of the middle of an iteration.
_.breakLoop = function() {
throw breaker;
};
// Generate a unique integer id (unique within the entire client session).
// Useful for temporary DOM ids.
var idCounter = 0;
_.uniqueId = function(prefix) {
var id = idCounter++;
return prefix ? prefix + id : id;
};
// JavaScript templating a-la ERB, pilfered from John Resig's
// "Secrets of the JavaScript Ninja", page 83.
_.template = function(str, data) {
var fn = new Function('obj',
'var p=[],print=function(){p.push.apply(p,arguments);};' +
'with(obj){p.push(\'' +
str
.replace(/[\r\t\n]/g, " ")
.split("<%").join("\t")
.replace(/((^|%>)[^\t]*)'/g, "$1\r")
.replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');")
.split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
return data ? fn(data) : fn;
};
/*------------------------------- Aliases ----------------------------------*/
_.forEach = _.each;
_.foldl = _.inject = _.reduce;
_.foldr = _.reduceRight;
_.filter = _.select;
_.every = _.all;
_.some = _.any;
_.head = _.first;
_.tail = _.rest;
_.methods = _.functions;
/*------------------------ Setup the OOP Wrapper: --------------------------*/
// Helper function to continue chaining intermediate results.
var result = function(obj, chain) {
return chain ? _(obj).chain() : obj;
};
// Add all of the Underscore functions to the wrapper object.
_.each(_.functions(_), function(name) {
var method = _[name];
wrapper.prototype[name] = function() {
unshift.call(arguments, this._wrapped);
return result(method.apply(_, arguments), this._chain);
};
});
// Add all mutator Array functions to the wrapper.
_.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
var method = Array.prototype[name];
wrapper.prototype[name] = function() {
method.apply(this._wrapped, arguments);
return result(this._wrapped, this._chain);
};
});
// Add all accessor Array functions to the wrapper.
_.each(['concat', 'join', 'slice'], function(name) {
var method = Array.prototype[name];
wrapper.prototype[name] = function() {
return result(method.apply(this._wrapped, arguments), this._chain);
};
});
// Start chaining a wrapped Underscore object.
wrapper.prototype.chain = function() {
this._chain = true;
return this;
};
// Extracts the result from a wrapped and chained object.
wrapper.prototype.value = function() {
return this._wrapped;
};
})();

View file

@ -0,0 +1,142 @@
/**
*
* Base64 encode / decode
* http://www.webtoolkit.info/
*
**/
var Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",
// public method for encoding
encode : function (input) {
var output = "";
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = Base64._utf8_encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output +
this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
}
return output;
},
// public method for decoding
decode : function (input) {
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = this._keyStr.indexOf(input.charAt(i++));
enc2 = this._keyStr.indexOf(input.charAt(i++));
enc3 = this._keyStr.indexOf(input.charAt(i++));
enc4 = this._keyStr.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
output = Base64._utf8_decode(output);
return output;
},
// private method for UTF-8 encoding
_utf8_encode : function (string) {
string = string.replace(/\r\n/g,"\n");
var utftext = "";
for (var n = 0; n < string.length; n++) {
var c = string.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
}
else if((c > 127) && (c < 2048)) {
utftext += String.fromCharCode((c >> 6) | 192);
utftext += String.fromCharCode((c & 63) | 128);
}
else {
utftext += String.fromCharCode((c >> 12) | 224);
utftext += String.fromCharCode(((c >> 6) & 63) | 128);
utftext += String.fromCharCode((c & 63) | 128);
}
}
return utftext;
},
// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
var string = "";
var i = 0;
var c = c1 = c2 = 0;
while ( i < utftext.length ) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
}
else if((c > 191) && (c < 224)) {
c2 = utftext.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
}
else {
c2 = utftext.charCodeAt(i+1);
c3 = utftext.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
}
};

318
src/API.js Normal file
View file

@ -0,0 +1,318 @@
angular.Global = {
typeOf:function(obj){
var type = typeof obj;
switch(type) {
case "object":
if (obj === null) return "null";
if (obj instanceof Array) return "array";
if (obj instanceof Date) return "date";
if (obj.nodeType == 1) return "element";
}
return type;
}
};
angular.Collection = {};
angular.Object = {};
angular.Array = {
includeIf:function(array, value, condition) {
var index = _.indexOf(array, value);
if (condition) {
if (index == -1)
array.push(value);
} else {
array.splice(index, 1);
}
return array;
},
sum:function(array, expression) {
var fn = angular.Function.compile(expression);
var sum = 0;
for (var i = 0; i < array.length; i++) {
var value = 1 * fn(array[i]);
if (!isNaN(value)){
sum += value;
}
}
return sum;
},
remove:function(array, value) {
var index = _.indexOf(array, value);
if (index >=0)
array.splice(index, 1);
return value;
},
find:function(array, condition, defaultValue) {
if (!condition) return undefined;
var fn = angular.Function.compile(condition);
_.detect(array, function($){
if (fn($)){
defaultValue = $;
return true;
}
});
return defaultValue;
},
findById:function(array, id) {
return angular.Array.find(array, function($){return $.$id == id;}, null);
},
filter:function(array, expression) {
var predicates = [];
predicates.check = function(value) {
for (var j = 0; j < predicates.length; j++) {
if(!predicates[j](value)) {
return false;
}
}
return true;
};
var getter = nglr.Scope.getter;
var search = function(obj, text){
if (text.charAt(0) === '!') {
return !search(obj, text.substr(1));
}
switch (typeof obj) {
case "boolean":
case "number":
case "string":
return ('' + obj).toLowerCase().indexOf(text) > -1;
case "object":
for ( var objKey in obj) {
if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
return true;
}
}
return false;
case "array":
for ( var i = 0; i < obj.length; i++) {
if (search(obj[i], text)) {
return true;
}
}
return false;
default:
return false;
}
};
switch (typeof expression) {
case "boolean":
case "number":
case "string":
expression = {$:expression};
case "object":
for (var key in expression) {
if (key == '$') {
(function(){
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(value, text);
});
})();
} else {
(function(){
var path = key;
var text = (''+expression[key]).toLowerCase();
if (!text) return;
predicates.push(function(value) {
return search(getter(value, path), text);
});
})();
}
}
break;
case "function":
predicates.push(expression);
break;
default:
return array;
}
var filtered = [];
for ( var j = 0; j < array.length; j++) {
var value = array[j];
if (predicates.check(value)) {
filtered.push(value);
}
}
return filtered;
},
add:function(array, value) {
array.push(_.isUndefined(value)? {} : value);
return array;
},
count:function(array, condition) {
if (!condition) return array.length;
var fn = angular.Function.compile(condition);
return _.reduce(array, 0, function(count, $){return count + (fn($)?1:0);});
},
orderBy:function(array, expression, descend) {
function reverse(comp, descending) {
return nglr.toBoolean(descending) ?
function(a,b){return comp(b,a);} : comp;
}
function compare(v1, v2){
var t1 = typeof v1;
var t2 = typeof v2;
if (t1 == t2) {
if (t1 == "string") v1 = v1.toLowerCase();
if (t1 == "string") v2 = v2.toLowerCase();
if (v1 === v2) return 0;
return v1 < v2 ? -1 : 1;
} else {
return t1 < t2 ? -1 : 1;
}
}
expression = _.isArray(expression) ? expression: [expression];
expression = _.map(expression, function($){
var descending = false;
if (typeof $ == "string" && ($.charAt(0) == '+' || $.charAt(0) == '-')) {
descending = $.charAt(0) == '-';
$ = $.substring(1);
}
var get = $ ? angular.Function.compile($) : _.identity;
return reverse(function(a,b){
return compare(get(a),get(b));
}, descending);
});
var comparator = function(o1, o2){
for ( var i = 0; i < expression.length; i++) {
var comp = expression[i](o1, o2);
if (comp != 0) return comp;
}
return 0;
};
return _.clone(array).sort(reverse(comparator, descend));
},
orderByToggle:function(predicate, attribute) {
var STRIP = /^([+|-])?(.*)/;
var ascending = false;
var index = -1;
_.detect(predicate, function($, i){
if ($ == attribute) {
ascending = true;
index = i;
return true;
}
if (($.charAt(0)=='+'||$.charAt(0)=='-') && $.substring(1) == attribute) {
ascending = $.charAt(0) == '+';
index = i;
return true;
};
});
if (index >= 0) {
predicate.splice(index, 1);
}
predicate.unshift((ascending ? "-" : "+") + attribute);
return predicate;
},
orderByDirection:function(predicate, attribute, ascend, descend) {
ascend = ascend || 'ng-ascend';
descend = descend || 'ng-descend';
var att = predicate[0] || '';
var direction = true;
if (att.charAt(0) == '-') {
att = att.substring(1);
direction = false;
} else if(att.charAt(0) == '+') {
att = att.substring(1);
}
return att == attribute ? (direction ? ascend : descend) : "";
},
merge:function(array, index, mergeValue) {
var value = array[index];
if (!value) {
value = {};
array[index] = value;
}
nglr.merge(mergeValue, value);
return array;
}
};
angular.String = {
quote:function(string) {
return '"' + string.replace(/\\/g, '\\\\').
replace(/"/g, '\\"').
replace(/\n/g, '\\n').
replace(/\f/g, '\\f').
replace(/\r/g, '\\r').
replace(/\t/g, '\\t').
replace(/\v/g, '\\v') +
'"';
},
quoteUnicode:function(string) {
var str = angular.String.quote(string);
var chars = [];
for ( var i = 0; i < str.length; i++) {
var ch = str.charCodeAt(i);
if (ch < 128) {
chars.push(str.charAt(i));
} else {
var encode = "000" + ch.toString(16);
chars.push("\\u" + encode.substring(encode.length - 4));
}
}
return chars.join('');
},
toDate:function(string){
var match;
if (typeof string == 'string' &&
(match = string.match(/^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z$/))){
var date = new Date(0);
date.setUTCFullYear(match[1], match[2] - 1, match[3]);
date.setUTCHours(match[4], match[5], match[6], 0);
return date;
}
return string;
}
};
angular.Date = {
toString:function(date){
function pad(n) { return n < 10 ? "0" + n : n; }
return (date.getUTCFullYear()) + '-' +
pad(date.getUTCMonth() + 1) + '-' +
pad(date.getUTCDate()) + 'T' +
pad(date.getUTCHours()) + ':' +
pad(date.getUTCMinutes()) + ':' +
pad(date.getUTCSeconds()) + 'Z';
}
};
angular.Function = {
compile:function(expression) {
if (_.isFunction(expression)){
return expression;
} else if (expression){
var scope = new nglr.Scope();
return function($) {
scope.state = $;
return scope.eval(expression);
};
} else {
return function($){return $;};
}
}
};
(function(){
function extend(dst, src, names){
_.extend(dst, src);
_.each((names||[]), function(name){
dst[name] = _[name];
});
};
extend(angular.Global, {},
['extend', 'clone','isEqual',
'isElement', 'isArray', 'isFunction', 'isUndefined']);
extend(angular.Collection, angular.Global,
['each', 'map', 'reduce', 'reduceRight', 'detect',
'select', 'reject', 'all', 'any', 'include',
'invoke', 'pluck', 'max', 'min', 'sortBy',
'sortedIndex', 'toArray', 'size']);
extend(angular.Array, angular.Collection,
['first', 'last', 'compact', 'flatten', 'without',
'uniq', 'intersect', 'zip', 'indexOf', 'lastIndexOf']);
extend(angular.Object, angular.Collection,
['keys', 'values']);
extend(angular.String, angular.Global);
extend(angular.Function, angular.Global,
['bind', 'bindAll', 'delay', 'defer', 'wrap', 'compose']);
})();

341
src/Binder.js Normal file
View file

@ -0,0 +1,341 @@
// Copyright (C) 2009 BRAT Tech LLC
nglr.Binder = function(doc, widgetFactory, urlWatcher, config) {
this.doc = doc;
this.urlWatcher = urlWatcher;
this.anchor = {};
this.widgetFactory = widgetFactory;
this.config = config || {};
this.updateListeners = [];
};
nglr.Binder.parseBindings = function(string) {
var results = [];
var lastIndex = 0;
var index;
while((index = string.indexOf('{{', lastIndex)) > -1) {
if (lastIndex < index)
results.push(string.substr(lastIndex, index - lastIndex));
lastIndex = index;
index = string.indexOf('}}', index);
index = index < 0 ? string.length : index + 2;
results.push(string.substr(lastIndex, index - lastIndex));
lastIndex = index;
}
if (lastIndex != string.length)
results.push(string.substr(lastIndex, string.length - lastIndex));
return results.length === 0 ? [ string ] : results;
};
nglr.Binder.hasBinding = function(string) {
var bindings = nglr.Binder.parseBindings(string);
return bindings.length > 1 || nglr.Binder.binding(bindings[0]) !== null;
};
nglr.Binder.binding = function(string) {
var binding = string.replace(/\n/gm, ' ').match(/^\{\{(.*)\}\}$/);
return binding ? binding[1] : null;
};
nglr.Binder.prototype.parseQueryString = function(query) {
var params = {};
query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,
function (match, left, right) {
if (left) params[decodeURIComponent(left)] = decodeURIComponent(right);
});
return params;
};
nglr.Binder.prototype.parseAnchor = function(url) {
var self = this;
url = url || this.urlWatcher.getUrl();
var anchorIndex = url.indexOf('#');
if (anchorIndex < 0) return;
var anchor = url.substring(anchorIndex + 1);
var anchorQuery = this.parseQueryString(anchor);
jQuery.each(self.anchor, function(key, newValue) {
delete self.anchor[key];
});
jQuery.each(anchorQuery, function(key, newValue) {
self.anchor[key] = newValue;
});
};
nglr.Binder.prototype.onUrlChange = function (url) {
console.log("URL change detected", url);
this.parseAnchor(url);
this.updateView();
};
nglr.Binder.prototype.updateAnchor = function() {
var url = this.urlWatcher.getUrl();
var anchorIndex = url.indexOf('#');
if (anchorIndex > -1)
url = url.substring(0, anchorIndex);
url += "#";
var sep = '';
for (var key in this.anchor) {
var value = this.anchor[key];
if (typeof value === 'undefined' || value === null) {
delete this.anchor[key];
} else {
url += sep + encodeURIComponent(key);
if (value !== true)
url += "=" + encodeURIComponent(value);
sep = '&';
}
}
this.urlWatcher.setUrl(url);
return url;
};
nglr.Binder.prototype.updateView = function() {
var start = new Date().getTime();
var scope = jQuery(this.doc).scope();
scope.set("$invalidWidgets", []);
scope.updateView();
var end = new Date().getTime();
this.updateAnchor();
_.each(this.updateListeners, function(fn) {fn();});
};
nglr.Binder.prototype.executeInit = function() {
jQuery("[ng-init]", this.doc).each(function() {
var jThis = jQuery(this);
var scope = jThis.scope();
try {
scope.eval(jThis.attr('ng-init'));
} catch (e) {
nglr.alert("EVAL ERROR:\n" + jThis.attr('ng-init') + '\n' + nglr.toJson(e, true));
}
});
};
nglr.Binder.prototype.entity = function (scope) {
jQuery("[ng-entity]", this.doc).attr("ng-watch", function() {
try {
var jNode = jQuery(this);
var decl = scope.entity(jNode.attr("ng-entity"));
return decl + (jNode.attr('ng-watch') || "");
} catch (e) {
nglr.alert(e);
}
});
};
nglr.Binder.prototype.compile = function() {
var jNode = jQuery(this.doc);
var self = this;
if (this.config.autoSubmit) {
var submits = jQuery(":submit", this.doc).not("[ng-action]");
submits.attr("ng-action", "$save()");
submits.not(":disabled").not("ng-bind-attr").attr("ng-bind-attr", '{disabled:"{{$invalidWidgets}}"}');
}
this.precompile(this.doc)(this.doc, jNode.scope(), "");
jQuery("a[ng-action]", this.doc).live('click', function (event) {
var jNode = jQuery(this);
try {
jNode.scope().eval(jNode.attr('ng-action'));
jNode.removeAttr('ng-error');
jNode.removeClass("ng-exception");
} catch (e) {
jNode.addClass("ng-exception");
jNode.attr('ng-error', nglr.toJson(e, true));
}
self.updateView();
return false;
});
};
nglr.Binder.prototype.translateBinding = function(node, parentPath, factories) {
var path = parentPath.concat();
var offset = path.pop();
var parts = nglr.Binder.parseBindings(node.nodeValue);
if (parts.length > 1 || nglr.Binder.binding(parts[0])) {
var parent = node.parentNode;
if (nglr.isLeafNode(parent)) {
parent.setAttribute('ng-bind-template', node.nodeValue);
factories.push({path:path, fn:function(node, scope, prefix) {
return new nglr.BindUpdater(node, node.getAttribute('ng-bind-template'));
}});
} else {
for (var i = 0; i < parts.length; i++) {
var part = parts[i];
var binding = nglr.Binder.binding(part);
var newNode;
if (binding) {
newNode = document.createElement("span");
var jNewNode = jQuery(newNode);
jNewNode.attr("ng-bind", binding);
if (i === 0) {
factories.push({path:path.concat(offset + i), fn:nglr.Binder.prototype.ng_bind});
}
} else if (nglr.msie && part.charAt(0) == ' ') {
newNode = document.createElement("span");
newNode.innerHTML = '&nbsp;' + part.substring(1);
} else {
newNode = document.createTextNode(part);
}
parent.insertBefore(newNode, node);
}
}
parent.removeChild(node);
}
};
nglr.Binder.prototype.precompile = function(root) {
var factories = [];
this.precompileNode(root, [], factories);
return function (template, scope, prefix) {
var len = factories.length;
for (var i = 0; i < len; i++) {
var factory = factories[i];
var node = template;
var path = factory.path;
for (var j = 0; j < path.length; j++) {
node = node.childNodes[path[j]];
}
try {
scope.addWidget(factory.fn(node, scope, prefix));
} catch (e) {
nglr.alert(e);
}
}
};
};
nglr.Binder.prototype.precompileNode = function(node, path, factories) {
var nodeType = node.nodeType;
if (nodeType == Node.TEXT_NODE) {
this.translateBinding(node, path, factories);
return;
} else if (nodeType != Node.ELEMENT_NODE && nodeType != Node.DOCUMENT_NODE) {
return;
}
if (!node.getAttribute) return;
var nonBindable = node.getAttribute('ng-non-bindable');
if (nonBindable || nonBindable === "") return;
var attributes = node.attributes;
if (attributes) {
var bindings = node.getAttribute('ng-bind-attr');
node.removeAttribute('ng-bind-attr');
bindings = bindings ? nglr.fromJson(bindings) : {};
var attrLen = attributes.length;
for (var i = 0; i < attrLen; i++) {
var attr = attributes[i];
var attrName = attr.name;
// http://www.glennjones.net/Post/809/getAttributehrefbug.htm
var attrValue = nglr.msie && attrName == 'href' ?
decodeURI(node.getAttribute(attrName, 2)) : attr.value;
if (nglr.Binder.hasBinding(attrValue)) {
bindings[attrName] = attrValue;
}
}
var json = nglr.toJson(bindings);
if (json.length > 2) {
node.setAttribute("ng-bind-attr", json);
}
}
if (!node.getAttribute) console.log(node);
var repeaterExpression = node.getAttribute('ng-repeat');
if (repeaterExpression) {
node.removeAttribute('ng-repeat');
var precompiled = this.precompile(node);
var view = document.createComment("ng-repeat: " + repeaterExpression);
var parentNode = node.parentNode;
parentNode.insertBefore(view, node);
parentNode.removeChild(node);
var template = function(childScope, prefix, i) {
var clone = jQuery(node).clone();
clone.css('display', '');
clone.attr('ng-repeat-index', "" + i);
clone.data('scope', childScope);
precompiled(clone[0], childScope, prefix + i + ":");
return clone;
};
factories.push({path:path, fn:function(node, scope, prefix) {
return new nglr.RepeaterUpdater(jQuery(node), repeaterExpression, template, prefix);
}});
return;
}
if (node.getAttribute('ng-eval')) factories.push({path:path, fn:this.ng_eval});
if (node.getAttribute('ng-bind')) factories.push({path:path, fn:this.ng_bind});
if (node.getAttribute('ng-bind-attr')) factories.push({path:path, fn:this.ng_bind_attr});
if (node.getAttribute('ng-hide')) factories.push({path:path, fn:this.ng_hide});
if (node.getAttribute('ng-show')) factories.push({path:path, fn:this.ng_show});
if (node.getAttribute('ng-class')) factories.push({path:path, fn:this.ng_class});
if (node.getAttribute('ng-class-odd')) factories.push({path:path, fn:this.ng_class_odd});
if (node.getAttribute('ng-class-even')) factories.push({path:path, fn:this.ng_class_even});
if (node.getAttribute('ng-style')) factories.push({path:path, fn:this.ng_style});
if (node.getAttribute('ng-watch')) factories.push({path:path, fn:this.ng_watch});
var nodeName = node.nodeName;
if ((nodeName == 'INPUT' ) ||
nodeName == 'TEXTAREA' ||
nodeName == 'SELECT' ||
nodeName == 'BUTTON') {
var self = this;
factories.push({path:path, fn:function(node, scope, prefix) {
node.name = prefix + node.name.split(":").pop();
return self.widgetFactory.createController(jQuery(node), scope);
}});
}
if (nodeName == 'OPTION') {
var html = jQuery('<select/>').append(jQuery(node).clone()).html();
if (!html.match(/<option(\s.*\s|\s)value\s*=\s*.*>.*<\/\s*option\s*>/gi)) {
node.value = node.text;
}
}
var children = node.childNodes;
for (var k = 0; k < children.length; k++) {
this.precompileNode(children[k], path.concat(k), factories);
}
};
nglr.Binder.prototype.ng_eval = function(node) {
return new nglr.EvalUpdater(node, node.getAttribute('ng-eval'));
};
nglr.Binder.prototype.ng_bind = function(node) {
return new nglr.BindUpdater(node, "{{" + node.getAttribute('ng-bind') + "}}");
};
nglr.Binder.prototype.ng_bind_attr = function(node) {
return new nglr.BindAttrUpdater(node, nglr.fromJson(node.getAttribute('ng-bind-attr')));
};
nglr.Binder.prototype.ng_hide = function(node) {
return new nglr.HideUpdater(node, node.getAttribute('ng-hide'));
};
nglr.Binder.prototype.ng_show = function(node) {
return new nglr.ShowUpdater(node, node.getAttribute('ng-show'));
};
nglr.Binder.prototype.ng_class = function(node) {
return new nglr.ClassUpdater(node, node.getAttribute('ng-class'));
};
nglr.Binder.prototype.ng_class_even = function(node) {
return new nglr.ClassEvenUpdater(node, node.getAttribute('ng-class-even'));
};
nglr.Binder.prototype.ng_class_odd = function(node) {
return new nglr.ClassOddUpdater(node, node.getAttribute('ng-class-odd'));
};
nglr.Binder.prototype.ng_style = function(node) {
return new nglr.StyleUpdater(node, node.getAttribute('ng-style'));
};
nglr.Binder.prototype.ng_watch = function(node, scope) {
scope.watch(node.getAttribute('ng-watch'));
};

71
src/ControlBar.js Normal file
View file

@ -0,0 +1,71 @@
// Copyright (C) 2008,2009 BRAT Tech LLC
nglr.ControlBar = function (document, serverUrl) {
this.document = document;
this.serverUrl = serverUrl;
this.window = window;
this.callbacks = [];
};
nglr.ControlBar.prototype.bind = function () {
};
nglr.ControlBar.HTML =
'<div>' +
'<div class="ui-widget-overlay"></div>' +
'<div id="ng-login" ng-non-bindable="true">' +
'<div class="ng-login-container"></div>' +
'</div>' +
'</div>';
nglr.ControlBar.prototype.login = function (loginSubmitFn) {
this.callbacks.push(loginSubmitFn);
if (this.callbacks.length == 1) {
this.doTemplate("/user_session/new.mini?return_url=" + encodeURIComponent(this.urlWithoutAnchor()));
}
};
nglr.ControlBar.prototype.logout = function (loginSubmitFn) {
this.callbacks.push(loginSubmitFn);
if (this.callbacks.length == 1) {
this.doTemplate("/user_session/do_destroy.mini");
}
};
nglr.ControlBar.prototype.urlWithoutAnchor = function (path) {
return this.window.location.href.split("#")[0];
};
nglr.ControlBar.prototype.doTemplate = function (path) {
var self = this;
var id = new Date().getTime();
var url = this.urlWithoutAnchor();
url += "#$iframe_notify=" + id;
var iframeHeight = 330;
var loginView = jQuery('<div style="overflow:hidden; padding:2px 0 0 0;"><iframe name="'+ url +'" src="'+this.serverUrl + path + '" width="500" height="'+ iframeHeight +'"/></div>');
this.document.append(loginView);
loginView.dialog({
height:iframeHeight + 33, width:500,
resizable: false, modal:true,
title: 'Authentication: <a href="http://www.getangular.com"><tt>&lt;angular/&gt;</tt></a>'
});
nglr["_iframe_notify_" + id] = function() {
loginView.dialog("destroy");
loginView.remove();
jQuery.each(self.callbacks, function(i, callback){
callback();
});
self.callbacks = [];
};
};
nglr.ControlBar.FORBIDEN =
'<div ng-non-bindable="true" title="Permission Error:">' +
'Sorry, you do not have permission for this!'+
'</div>';
nglr.ControlBar.prototype.notAuthorized = function () {
if (this.forbidenView) return;
this.forbidenView = jQuery(nglr.ControlBar.FORBIDEN);
this.forbidenView.dialog({bgiframe:true, height:70, modal:true});
};

332
src/DataStore.js Normal file
View file

@ -0,0 +1,332 @@
// Copyright (C) 2009 BRAT Tech LLC
nglr.DataStore = function(post, users, anchor) {
this.post = post;
this.users = users;
this._cache = {$collections:[]};
this.anchor = anchor;
this.bulkRequest = [];
};
nglr.DataStore.prototype.cache = function(document) {
if (document.constructor != nglr.Model) {
throw "Parameter must be an instance of Entity! " + nglr.toJson(document);
}
var key = document.$entity + '/' + document.$id;
var cachedDocument = this._cache[key];
if (cachedDocument) {
nglr.Model.copyDirectFields(document, cachedDocument);
} else {
this._cache[key] = document;
cachedDocument = document;
}
return cachedDocument;
};
nglr.DataStore.prototype.load = function(instance, id, callback, failure) {
if (id && id !== '*') {
var self = this;
this._jsonRequest(["GET", instance.$entity + "/" + id], function(response) {
instance.$loadFrom(response);
instance.$migrate();
var clone = instance.$$entity(instance);
self.cache(clone);
(callback||nglr.noop)(instance);
}, failure);
}
return instance;
};
nglr.DataStore.prototype.loadMany = function(entity, ids, callback) {
var self=this;
var list = [];
var callbackCount = 0;
jQuery.each(ids, function(i, id){
list.push(self.load(entity(), id, function(){
callbackCount++;
if (callbackCount == ids.length) {
(callback||nglr.noop)(list);
}
}));
});
return list;
}
nglr.DataStore.prototype.loadOrCreate = function(instance, id, callback) {
var self=this;
return this.load(instance, id, callback, function(response){
if (response.$status_code == 404) {
instance.$id = id;
(callback||nglr.noop)(instance);
} else {
throw response;
}
});
};
nglr.DataStore.prototype.loadAll = function(entity, callback) {
var self = this;
var list = [];
list.$$accept = function(doc){
return doc.$entity == entity.title;
};
this._cache.$collections.push(list);
this._jsonRequest(["GET", entity.title], function(response) {
var rows = response;
for ( var i = 0; i < rows.length; i++) {
var document = entity();
document.$loadFrom(rows[i]);
list.push(self.cache(document));
}
(callback||nglr.noop)(list);
});
return list;
};
nglr.DataStore.prototype.save = function(document, callback) {
var self = this;
var data = {};
document.$saveTo(data);
this._jsonRequest(["POST", "", data], function(response) {
document.$loadFrom(response);
var cachedDoc = self.cache(document);
_.each(self._cache.$collections, function(collection){
if (collection.$$accept(document)) {
angular.Array.includeIf(collection, cachedDoc, true);
}
});
if (document.$$anchor) {
self.anchor[document.$$anchor] = document.$id;
}
if (callback)
callback(document);
});
};
nglr.DataStore.prototype.remove = function(document, callback) {
var self = this;
var data = {};
document.$saveTo(data);
this._jsonRequest(["DELETE", "", data], function(response) {
delete self._cache[document.$entity + '/' + document.$id];
_.each(self._cache.$collections, function(collection){
for ( var i = 0; i < collection.length; i++) {
var item = collection[i];
if (item.$id == document.$id) {
collection.splice(i, 1);
}
}
});
(callback||nglr.noop)(response);
});
};
nglr.DataStore.prototype._jsonRequest = function(request, callback, failure) {
request.$$callback = callback;
request.$$failure = failure||function(response){
throw response;
};
this.bulkRequest.push(request);
};
nglr.DataStore.prototype.flush = function() {
if (this.bulkRequest.length === 0) return;
var self = this;
var bulkRequest = this.bulkRequest;
this.bulkRequest = [];
console.log('REQUEST:', bulkRequest);
function callback(code, bulkResponse){
console.log('RESPONSE[' + code + ']: ', bulkResponse);
if(bulkResponse.$status_code == 401) {
self.users.login(function(){
self.post(bulkRequest, callback);
});
} else if(bulkResponse.$status_code) {
nglr.alert(nglr.toJson(bulkResponse));
} else {
for ( var i = 0; i < bulkResponse.length; i++) {
var response = bulkResponse[i];
var request = bulkRequest[i];
var code = response.$status_code;
if(code) {
if(code == 403) {
self.users.notAuthorized();
} else {
request.$$failure(response);
}
} else {
request.$$callback(response);
}
}
}
}
this.post(bulkRequest, callback);
};
nglr.DataStore.prototype.saveScope = function(scope, callback) {
var saveCounter = 1;
function onSaveDone() {
saveCounter--;
if (saveCounter === 0 && callback)
callback();
}
for(var key in scope) {
var item = scope[key];
if (item && item.$save == nglr.Model.prototype.$save) {
saveCounter++;
item.$save(onSaveDone);
}
}
onSaveDone();
};
nglr.DataStore.prototype.query = function(type, query, arg, callback){
var self = this;
var queryList = [];
queryList.$$accept = function(doc){
return false;
};
this._cache.$collections.push(queryList);
var request = type.title + '/' + query + '=' + arg;
this._jsonRequest(["GET", request], function(response){
var list = response;
for(var i = 0; i < list.length; i++) {
var document = new type().$loadFrom(list[i]);
queryList.push(self.cache(document));
}
if (callback)
callback(queryList);
});
return queryList;
};
nglr.DataStore.prototype.entities = function(callback) {
var entities = [];
var self = this;
this._jsonRequest(["GET", "$entities"], function(response) {
for (var entityName in response) {
entities.push(self.entity(entityName));
}
entities.sort(function(a,b){return a.title > b.title ? 1 : -1;});
if (callback) callback(entities);
});
return entities;
};
nglr.DataStore.prototype.documentCountsByUser = function(){
var counts = {};
var self = this;
self.post([["GET", "$users"]], function(code, response){
jQuery.each(response[0], function(key, value){
counts[key] = value;
});
});
return counts;
};
nglr.DataStore.prototype.userDocumentIdsByEntity = function(user){
var ids = {};
var self = this;
self.post([["GET", "$users/" + user]], function(code, response){
jQuery.each(response[0], function(key, value){
ids[key] = value;
});
});
return ids;
};
nglr.DataStore.NullEntity = function(){};
nglr.DataStore.NullEntity.all = function(){return [];};
nglr.DataStore.NullEntity.query = function(){return [];};
nglr.DataStore.NullEntity.load = function(){return {};};
nglr.DataStore.NullEntity.title = undefined;
nglr.DataStore.prototype.entity = function(name, defaults){
if (!name) {
return nglr.DataStore.NullEntity;
}
var self = this;
var entity = function(initialState){
return new nglr.Model(entity, initialState);
};
// entity.name does not work as name seems to be reserved for functions
entity.title = name;
entity.$$factory = true;
entity.datastore = this;
entity.defaults = defaults || {};
entity.load = function(id, callback){
return self.load(entity(), id, callback);
};
entity.loadMany = function(ids, callback){
return self.loadMany(entity, ids, callback);
};
entity.loadOrCreate = function(id, callback){
return self.loadOrCreate(entity(), id, callback);
};
entity.all = function(callback){
return self.loadAll(entity, callback);
};
entity.query = function(query, queryArgs, callback){
return self.query(entity, query, queryArgs, callback);
};
entity.properties = function(callback) {
self._jsonRequest(["GET", name + "/$properties"], callback);
};
return entity;
};
nglr.DataStore.prototype.join = function(join){
var fn = function(){
throw "Joined entities can not be instantiated into a document.";
};
function base(name){return name ? name.substring(0, name.indexOf('.')) : undefined;}
function next(name){return name.substring(name.indexOf('.') + 1);}
var joinOrder = _(join).chain().
map(function($, name){
return name;}).
sortBy(function(name){
var path = [];
do {
if (_(path).include(name)) throw "Infinite loop in join: " + path.join(" -> ");
path.push(name);
if (!join[name]) throw _("Named entity '<%=name%>' is undefined.").template({name:name});
name = base(join[name].on);
} while(name);
return path.length;
}).
value();
if (_(joinOrder).select(function($){return join[$].on;}).length != joinOrder.length - 1)
throw "Exactly one entity needs to be primary.";
fn.query = function(exp, value) {
var joinedResult = [];
var baseName = base(exp);
if (baseName != joinOrder[0]) throw _("Named entity '<%=name%>' is not a primary entity.").template({name:baseName});
var Entity = join[baseName].join;
var joinIndex = 1;
Entity.query(next(exp), value, function(result){
var nextJoinName = joinOrder[joinIndex++];
var nextJoin = join[nextJoinName];
var nextJoinOn = nextJoin.on;
var joinIds = {};
_(result).each(function(doc){
var row = {};
joinedResult.push(row);
row[baseName] = doc;
var id = nglr.Scope.getter(row, nextJoinOn);
joinIds[id] = id;
});
nextJoin.join.loadMany(_.toArray(joinIds), function(result){
var byId = {};
_(result).each(function(doc){
byId[doc.$id] = doc;
});
_(joinedResult).each(function(row){
var id = nglr.Scope.getter(row, nextJoinOn);
row[nextJoinName] = byId[id];
});
});
});
return joinedResult;
};
return fn;
};

290
src/Filters.js Normal file
View file

@ -0,0 +1,290 @@
// Copyright (C) 2009 BRAT Tech LLC
angular.filter.Meta = function(obj){
if (obj) {
for ( var key in obj) {
this[key] = obj[key];
}
}
};
angular.filter.Meta.get = function(obj, attr){
attr = attr || 'text';
switch(typeof obj) {
case "string":
return attr == "text" ? obj : undefined;
case "object":
if (obj && typeof obj[attr] !== "undefined") {
return obj[attr];
}
return undefined;
default:
return obj;
}
};
angular.filter.currency = function(amount){
jQuery(this.element).toggleClass('ng-format-negative', amount < 0);
return '$' + angular.filter.number.apply(this, [amount, 2]);
};
angular.filter.number = function(amount, fractionSize){
if (isNaN(amount) || !isFinite(amount)) {
return '';
}
fractionSize = typeof fractionSize == 'undefined' ? 2 : fractionSize;
var isNegative = amount < 0;
amount = Math.abs(amount);
var pow = Math.pow(10, fractionSize);
var text = "" + Math.round(amount * pow);
var whole = text.substring(0, text.length - fractionSize);
whole = whole || '0';
var frc = text.substring(text.length - fractionSize);
text = isNegative ? '-' : '';
for (var i = 0; i < whole.length; i++) {
if ((whole.length - i)%3 === 0 && i !== 0) {
text += ',';
}
text += whole.charAt(i);
}
if (fractionSize > 0) {
for (var j = frc.length; j < fractionSize; j++) {
frc += '0';
}
text += '.' + frc.substring(0, fractionSize);
}
return text;
};
angular.filter.date = function(amount) {
};
angular.filter.json = function(object) {
jQuery(this.element).addClass("ng-monospace");
return nglr.toJson(object, true);
};
angular.filter.trackPackage = function(trackingNo, noMatch) {
trackingNo = nglr.trim(trackingNo);
var tNo = trackingNo.replace(/ /g, '');
var MATCHERS = angular.filter.trackPackage.MATCHERS;
for ( var i = 0; i < MATCHERS.length; i++) {
var carrier = MATCHERS[i];
for ( var j = 0; j < carrier.regexp.length; j++) {
var regexp = carrier.regexp[j];
if (regexp.test(tNo)) {
var text = carrier.name + ": " + trackingNo;
var url = carrier.url + trackingNo;
return new angular.filter.Meta({
text:text,
url:url,
html: '<a href="' + nglr.escapeAttr(url) + '">' + text + '</a>',
trackingNo:trackingNo});
}
}
}
if (trackingNo)
return noMatch ||
new angular.filter.Meta({text:trackingNo + " is not recognized"});
else
return null;
};
angular.filter.trackPackage.MATCHERS = [
{ name: "UPS",
url: "http://wwwapps.ups.com/WebTracking/processInputRequest?sort_by=status&tracknums_displayed=1&TypeOfInquiryNumber=T&loc=en_US&track.x=0&track.y=0&InquiryNumber1=",
regexp: [
/^1Z[0-9A-Z]{16}$/i]},
{ name: "FedEx",
url: "http://www.fedex.com/Tracking?tracknumbers=",
regexp: [
/^96\d{10}?$/i,
/^96\d{17}?$/i,
/^96\d{20}?$/i,
/^\d{15}$/i,
/^\d{12}$/i]},
{ name: "USPS",
url: "http://trkcnfrm1.smi.usps.com/PTSInternetWeb/InterLabelInquiry.do?origTrackNum=",
regexp: [
/^(91\d{20})$/i,
/^(91\d{18})$/i]}];
angular.filter.link = function(obj, title) {
var text = title || angular.filter.Meta.get(obj);
var url = angular.filter.Meta.get(obj, "url") || angular.filter.Meta.get(obj);
if (url) {
if (angular.validator.email(url) === null) {
url = "mailto:" + url;
}
var html = '<a href="' + nglr.escapeHtml(url) + '">' + text + '</a>';
return new angular.filter.Meta({text:text, url:url, html:html});
}
return obj;
};
angular.filter.bytes = function(size) {
if(size === null) return "";
var suffix = 0;
while (size > 1000) {
size = size / 1024;
suffix++;
}
var txt = "" + size;
var dot = txt.indexOf('.');
if (dot > -1 && dot + 2 < txt.length) {
txt = txt.substring(0, dot + 2);
}
return txt + " " + angular.filter.bytes.SUFFIX[suffix];
};
angular.filter.bytes.SUFFIX = ['bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
angular.filter.image = function(obj, width, height) {
if (obj && obj.url) {
var style = "";
if (width) {
style = ' style="max-width: ' + width +
'px; max-height: ' + (height || width) + 'px;"';
}
return new angular.filter.Meta({url:obj.url, text:obj.url,
html:'<img src="'+obj.url+'"' + style + '/>'});
}
return null;
};
angular.filter.lowercase = function (obj) {
var text = angular.filter.Meta.get(obj);
return text ? ("" + text).toLowerCase() : text;
};
angular.filter.uppercase = function (obj) {
var text = angular.filter.Meta.get(obj);
return text ? ("" + text).toUpperCase() : text;
};
angular.filter.linecount = function (obj) {
var text = angular.filter.Meta.get(obj);
if (text==='' || !text) return 1;
return text.split(/\n|\f/).length;
};
angular.filter['if'] = function (result, expression) {
return expression ? result : undefined;
};
angular.filter.unless = function (result, expression) {
return expression ? undefined : result;
};
angular.filter.googleChartApi = function(type, data, width, height) {
data = data || {};
var api = angular.filter.googleChartApi;
var chart = {
cht:type,
chco:api.collect(data, 'color'),
chtt:api.title(data),
chdl:api.collect(data, 'label'),
chd:api.values(data),
chf:'bg,s,FFFFFF00'
};
if (_.isArray(data.xLabels)) {
chart.chxt='x';
chart.chxl='0:|' + data.xLabels.join('|');
}
return angular.filter.googleChartApi.encode(chart, width, height);
};
angular.filter.googleChartApi.values = function(data){
var seriesValues = [];
_.each(data.series||[], function(serie){
var values = [];
_.each(serie.values||[], function(value){
values.push(value);
});
seriesValues.push(values.join(','));
});
var values = seriesValues.join('|');
return values === "" ? null : "t:" + values;
};
angular.filter.googleChartApi.title = function(data){
var titles = [];
var title = data.title || [];
_.each(_.isArray(title)?title:[title], function(text){
titles.push(encodeURIComponent(text));
});
return titles.join('|');
};
angular.filter.googleChartApi.collect = function(data, key){
var outterValues = [];
var count = 0;
_.each(data.series||[], function(serie){
var innerValues = [];
var value = serie[key] || [];
_.each(_.isArray(value)?value:[value], function(color){
innerValues.push(encodeURIComponent(color));
count++;
});
outterValues.push(innerValues.join('|'));
});
return count?outterValues.join(','):null;
};
angular.filter.googleChartApi.encode= function(params, width, height) {
width = width || 200;
height = height || width;
var url = "http://chart.apis.google.com/chart?";
var urlParam = [];
params.chs = width + "x" + height;
for ( var key in params) {
var value = params[key];
if (value) {
urlParam.push(key + "=" + value);
}
}
urlParam.sort();
url += urlParam.join("&");
return new angular.filter.Meta({url:url, text:value,
html:'<img width="' + width + '" height="' + height + '" src="'+url+'"/>'});
};
angular.filter.qrcode = function(value, width, height) {
return angular.filter.googleChartApi.encode({cht:'qr', chl:encodeURIComponent(value)}, width, height);
};
angular.filter.chart = {
pie:function(data, width, height) {
return angular.filter.googleChartApi('p', data, width, height);
},
pie3d:function(data, width, height) {
return angular.filter.googleChartApi('p3', data, width, height);
},
pieConcentric:function(data, width, height) {
return angular.filter.googleChartApi('pc', data, width, height);
},
barHorizontalStacked:function(data, width, height) {
return angular.filter.googleChartApi('bhs', data, width, height);
},
barHorizontalGrouped:function(data, width, height) {
return angular.filter.googleChartApi('bhg', data, width, height);
},
barVerticalStacked:function(data, width, height) {
return angular.filter.googleChartApi('bvs', data, width, height);
},
barVerticalGrouped:function(data, width, height) {
return angular.filter.googleChartApi('bvg', data, width, height);
},
line:function(data, width, height) {
return angular.filter.googleChartApi('lc', data, width, height);
},
sparkline:function(data, width, height) {
return angular.filter.googleChartApi('ls', data, width, height);
},
scatter:function(data, width, height) {
return angular.filter.googleChartApi('s', data, width, height);
}
};
angular.filter.html = function(html){
return new angular.filter.Meta({html:html});
};

92
src/JSON.js Normal file
View file

@ -0,0 +1,92 @@
nglr.array = [].constructor;
nglr.toJson = function(obj, pretty){
var buf = [];
nglr.toJsonArray(buf, obj, pretty ? "\n " : null);
return buf.join('');
};
nglr.toPrettyJson = function(obj) {
return nglr.toJson(obj, true);
};
nglr.fromJson = function(json) {
try {
var parser = new nglr.Parser(json, true);
var expression = parser.primary();
parser.assertAllConsumed();
return expression();
} catch (e) {
console.error("fromJson error: ", json, e);
throw e;
}
};
nglr.toJsonArray = function(buf, obj, pretty){
var type = typeof obj;
if (obj === null) {
buf.push("null");
} else if (type === 'function') {
return;
} else if (type === 'boolean') {
buf.push('' + obj);
} else if (type === 'number') {
if (isNaN(obj)) {
buf.push('null');
} else {
buf.push('' + obj);
}
} else if (type === 'string') {
return buf.push(angular.String.quoteUnicode(obj));
} else if (type === 'object') {
if (obj instanceof Array) {
buf.push("[");
var len = obj.length;
var sep = false;
for(var i=0; i<len; i++) {
var item = obj[i];
if (sep) buf.push(",");
if (typeof item == 'function' || typeof item == 'undefined') {
buf.push("null");
} else {
nglr.toJsonArray(buf, item, pretty);
}
sep = true;
}
buf.push("]");
} else if (obj instanceof Date) {
buf.push(angular.String.quoteUnicode(angular.Date.toString(obj)));
} else {
buf.push("{");
if (pretty) buf.push(pretty);
var comma = false;
var childPretty = pretty ? pretty + " " : false;
var keys = [];
for(var k in obj) {
if (k.indexOf('$$') === 0)
continue;
keys.push(k);
}
keys.sort();
for ( var keyIndex = 0; keyIndex < keys.length; keyIndex++) {
var key = keys[keyIndex];
try {
var value = obj[key];
if (typeof value != 'function') {
if (comma) {
buf.push(",");
if (pretty) buf.push(pretty);
}
buf.push(angular.String.quote(key));
buf.push(":");
nglr.toJsonArray(buf, value, childPretty);
comma = true;
}
} catch (e) {
}
}
buf.push("}");
}
}
};

389
src/Loader.js Normal file
View file

@ -0,0 +1,389 @@
// Copyright (C) 2008,2009 BRAT Tech LLC
// IE compatibility
if (typeof document.getAttribute == 'undefined')
document.getAttribute = function() {
};
if (typeof Node == 'undefined') {
Node = {
ELEMENT_NODE : 1,
ATTRIBUTE_NODE : 2,
TEXT_NODE : 3,
CDATA_SECTION_NODE : 4,
ENTITY_REFERENCE_NODE : 5,
ENTITY_NODE : 6,
PROCESSING_INSTRUCTION_NODE : 7,
COMMENT_NODE : 8,
DOCUMENT_NODE : 9,
DOCUMENT_TYPE_NODE : 10,
DOCUMENT_FRAGMENT_NODE : 11,
NOTATION_NODE : 12
};
}
if (_.isUndefined(window.nglr)) nglr = {};
if (_.isUndefined(window.angular)) angular = {};
if (_.isUndefined(angular.validator)) angular.validator = {};
if (_.isUndefined(angular.filter)) angular.filter = {};
if (_.isUndefined(window.console))
window.console = {
log:function() {},
error:function() {}
};
if (_.isUndefined(nglr.alert)) {
nglr.alert = function(){console.log(arguments); window.alert.apply(window, arguments); };
}
nglr.consoleLog = function(level, objs) {
var log = document.createElement("div");
log.className = level;
var msg = "";
var sep = "";
for ( var i = 0; i < objs.length; i++) {
var obj = objs[i];
msg += sep + (typeof obj == 'string' ? obj : nglr.toJson(obj));
sep = " ";
}
log.appendChild(document.createTextNode(msg));
nglr.consoleNode.appendChild(log);
};
nglr.isNode = function(inp) {
return inp &&
inp.tagName &&
inp.nodeName &&
inp.ownerDocument &&
inp.removeAttribute;
};
nglr.isLeafNode = function(node) {
switch (node.nodeName) {
case "OPTION":
case "PRE":
case "TITLE":
return true;
default:
return false;
}
};
nglr.noop = function() {
};
nglr.setHtml = function(node, html) {
if (nglr.isLeafNode(node)) {
if (nglr.msie) {
node.innerText = html;
} else {
node.textContent = html;
}
} else {
node.innerHTML = html;
}
};
nglr.escapeHtml = function(html) {
if (!html || !html.replace)
return html;
return html.
replace(/&/g, '&amp;').
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
};
nglr.escapeAttr = function(html) {
if (!html || !html.replace)
return html;
return html.replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g,
'&quot;');
};
nglr.bind = function(_this, _function) {
if (!_this)
throw "Missing this";
if (!_.isFunction(_function))
throw "Missing function";
return function() {
return _function.apply(_this, arguments);
};
};
nglr.shiftBind = function(_this, _function) {
return function() {
var args = [ this ];
for ( var i = 0; i < arguments.length; i++) {
args.push(arguments[i]);
}
return _function.apply(_this, args);
};
};
nglr.outerHTML = function(node) {
var temp = document.createElement('div');
temp.appendChild(node);
var outerHTML = temp.innerHTML;
temp.removeChild(node);
return outerHTML;
};
nglr.trim = function(str) {
return str.replace(/^ */, '').replace(/ *$/, '');
};
nglr.toBoolean = function(value) {
var v = ("" + value).toLowerCase();
if (v == 'f' || v == '0' || v == 'false' || v == 'no')
value = false;
return !!value;
};
nglr.merge = function(src, dst) {
for ( var key in src) {
var value = dst[key];
var type = typeof value;
if (type == 'undefined') {
dst[key] = nglr.fromJson(nglr.toJson(src[key]));
} else if (type == 'object' && value.constructor != nglr.array &&
key.substring(0, 1) != "$") {
nglr.merge(src[key], value);
}
}
};
// ////////////////////////////
// Loader
// ////////////////////////////
nglr.Loader = function(document, head, config) {
this.document = jQuery(document);
this.head = jQuery(head);
this.config = config;
this.location = window.location;
};
nglr.Loader.prototype.load = function() {
this.configureLogging();
this.loadCss('/stylesheets/jquery-ui/smoothness/jquery-ui-1.7.1.css');
this.loadCss('/stylesheets/nglr.css');
console.log("Server: " + this.config.server);
jQuery.noConflict();
nglr.msie = jQuery.browser.msie;
this.configureJQueryPlugins();
this.computeConfiguration();
this.bindHtml();
};
nglr.Loader.prototype.configureJQueryPlugins = function() {
console.log('Loader.configureJQueryPlugins()');
jQuery.fn.removeNode = function() {
var node = this.get(0);
node.parentNode.removeChild(node);
};
jQuery.fn.scope = function() {
var element = this;
while (element && element.get(0)) {
var scope = element.data("scope");
if (scope)
return scope;
element = element.parent();
}
return null;
};
jQuery.fn.controller = function() {
return this.data('controller') || nglr.NullController.instance;
};
};
nglr.Loader.prototype.uid = function() {
return "" + new Date().getTime();
};
nglr.Loader.prototype.computeConfiguration = function() {
var config = this.config;
if (!config.database) {
var match = config.server.match(/https?:\/\/([\w]*)/)
config.database = match ? match[1] : "$MEMORY";
}
};
nglr.Loader.prototype.bindHtml = function() {
console.log('Loader.bindHtml()');
var watcher = new nglr.UrlWatcher(this.location);
var document = this.document;
var widgetFactory = new nglr.WidgetFactory(this.config.server, this.config.database);
var binder = new nglr.Binder(document[0], widgetFactory, watcher, this.config);
widgetFactory.onChangeListener = nglr.shiftBind(binder, binder.updateModel);
var controlBar = new nglr.ControlBar(document.find('body'), this.config.server);
var onUpdate = function(){binder.updateView();};
var server = this.config.database=="$MEMORY" ?
new nglr.FrameServer(this.window) :
new nglr.Server(this.config.server, jQuery.getScript);
server = new nglr.VisualServer(server, new nglr.Status(jQuery(document.body)), onUpdate);
var users = new nglr.Users(server, controlBar);
var databasePath = '/data/' + this.config.database;
var post = function(request, callback){
server.request("POST", databasePath, request, callback);
};
var datastore = new nglr.DataStore(post, users, binder.anchor);
binder.updateListeners.push(function(){datastore.flush();});
var scope = new nglr.Scope( {
$anchor : binder.anchor,
$binder : binder,
$config : this.config,
$console : window.console,
$datastore : datastore,
$save : function(callback) {
datastore.saveScope(scope.state, callback, binder.anchor);
},
$window : window,
$uid : this.uid,
$users : users
}, "ROOT");
jQuery.each(["get", "set", "eval", "addWatchListener", "updateView"],
function(i, method){
angular[method] = nglr.bind(scope, scope[method]);
});
document.data('scope', scope);
console.log('$binder.entity()');
binder.entity(scope);
console.log('$binder.compile()');
binder.compile();
console.log('ControlBar.bind()');
controlBar.bind();
console.log('$users.fetchCurrentUser()');
function fetchCurrentUser() {
users.fetchCurrentUser(function(u) {
if (!u && document.find("[ng-auth=eager]").length) {
users.login();
}
});
}
fetchCurrentUser();
console.log('PopUp.bind()');
new nglr.PopUp(document).bind();
console.log('$binder.parseAnchor()');
binder.parseAnchor();
console.log('$binder.executeInit()');
binder.executeInit();
console.log('$binder.updateView()');
binder.updateView();
watcher.listener = nglr.bind(binder, binder.onUrlChange, watcher);
watcher.onUpdate = function(){nglr.alert("update");};
watcher.watch();
document.find("body").show();
console.log('ready()');
};
nglr.Loader.prototype.visualPost = function(delegate) {
var status = new nglr.Status(jQuery(document.body));
return function(request, delegateCallback) {
status.beginRequest(request);
var callback = function() {
status.endRequest();
try {
delegateCallback.apply(this, arguments);
} catch (e) {
nglr.alert(nglr.toJson(e));
}
};
delegate(request, callback);
};
};
nglr.Loader.prototype.configureLogging = function() {
var url = window.location.href + '#';
url = url.split('#')[1];
var config = {
debug : null
};
var configs = url.split('&');
for ( var i = 0; i < configs.length; i++) {
var part = (configs[i] + '=').split('=');
config[part[0]] = part[1];
}
if (config.debug == 'console') {
nglr.consoleNode = document.createElement("div");
nglr.consoleNode.id = 'ng-console';
document.getElementsByTagName('body')[0].appendChild(nglr.consoleNode);
console.log = function() {
nglr.consoleLog('ng-console-info', arguments);
};
console.error = function() {
nglr.consoleLog('ng-console-error', arguments);
};
}
};
nglr.Loader.prototype.loadCss = function(css) {
var cssTag = document.createElement('link');
cssTag.rel = "stylesheet";
cssTag.type = "text/css";
if (!css.match(/^http:/))
css = this.config.server + css;
cssTag.href = css;
this.head[0].appendChild(cssTag);
};
nglr.UrlWatcher = function(location) {
this.location = location;
this.delay = 25;
this.setTimeout = function(fn, delay) {
window.setTimeout(fn, delay);
};
this.listener = function(url) {
return url;
};
this.expectedUrl = location.href;
};
nglr.UrlWatcher.prototype.watch = function() {
var self = this;
var pull = function() {
if (self.expectedUrl !== self.location.href) {
var notify = self.location.hash.match(/^#\$iframe_notify=(.*)$/);
if (notify) {
if (!self.expectedUrl.match(/#/)) {
self.expectedUrl += "#";
}
self.location.href = self.expectedUrl;
var id = '_iframe_notify_' + notify[1];
var notifyFn = nglr[id];
delete nglr[id];
try {
(notifyFn||nglr.noop)();
} catch (e) {
nglr.alert(e);
}
} else {
self.listener(self.location.href);
self.expectedUrl = self.location.href;
}
}
self.setTimeout(pull, self.delay);
};
pull();
};
nglr.UrlWatcher.prototype.setUrl = function(url) {
var existingURL = window.location.href;
if (!existingURL.match(/#/))
existingURL += '#';
if (existingURL != url)
window.location.href = url;
self.existingURL = url;
};
nglr.UrlWatcher.prototype.getUrl = function() {
return window.location.href;
};

65
src/Model.js Normal file
View file

@ -0,0 +1,65 @@
// Copyright (C) 2009 BRAT Tech LLC
// Single $ is special and does not get searched
// Double $$ is special an is client only (does not get sent to server)
nglr.Model = function(entity, initial) {
this.$$entity = entity;
this.$loadFrom(initial||{});
this.$entity = entity.title;
this.$migrate();
};
nglr.Model.copyDirectFields = function(src, dst) {
if (src === dst || !src || !dst) return;
var isDataField = function(src, dst, field) {
return (field.substring(0,2) !== '$$') &&
(typeof src[field] !== 'function') &&
(typeof dst[field] !== 'function');
};
for (var field in dst) {
if (isDataField(src, dst, field))
delete dst[field];
}
for (field in src) {
if (isDataField(src, dst, field))
dst[field] = src[field];
}
};
nglr.Model.prototype.$migrate = function() {
nglr.merge(this.$$entity.defaults, this);
return this;
};
nglr.Model.prototype.$merge = function(other) {
nglr.merge(other, this);
return this;
};
nglr.Model.prototype.$save = function(callback) {
this.$$entity.datastore.save(this, callback === true ? undefined : callback);
if (callback === true) this.$$entity.datastore.flush();
return this;
};
nglr.Model.prototype.$delete = function(callback) {
this.$$entity.datastore.remove(this, callback === true ? undefined : callback);
if (callback === true) this.$$entity.datastore.flush();
return this;
};
nglr.Model.prototype.$loadById = function(id, callback) {
this.$$entity.datastore.load(this, id, callback);
return this;
};
nglr.Model.prototype.$loadFrom = function(other) {
nglr.Model.copyDirectFields(other, this);
return this;
};
nglr.Model.prototype.$saveTo = function(other) {
nglr.Model.copyDirectFields(this, other);
return this;
};

741
src/Parser.js Normal file
View file

@ -0,0 +1,741 @@
nglr.Lexer = function(text, parsStrings){
this.text = text;
// UTC dates have 20 characters, we send them through parser
this.dateParseLength = parsStrings ? 20 : -1;
this.tokens = [];
this.index = 0;
};
nglr.Lexer.OPERATORS = {
'null':function(self){return null;},
'true':function(self){return true;},
'false':function(self){return false;},
'+':function(self, a,b){return (a||0)+(b||0);},
'-':function(self, a,b){return (a||0)-(b||0);},
'*':function(self, a,b){return a*b;},
'/':function(self, a,b){return a/b;},
'%':function(self, a,b){return a%b;},
'^':function(self, a,b){return a^b;},
'=':function(self, a,b){return self.scope.set(a, b);},
'==':function(self, a,b){return a==b;},
'!=':function(self, a,b){return a!=b;},
'<':function(self, a,b){return a<b;},
'>':function(self, a,b){return a>b;},
'<=':function(self, a,b){return a<=b;},
'>=':function(self, a,b){return a>=b;},
'&&':function(self, a,b){return a&&b;},
'||':function(self, a,b){return a||b;},
'&':function(self, a,b){return a&b;},
// '|':function(self, a,b){return a|b;},
'|':function(self, a,b){return b(self, a);},
'!':function(self, a){return !a;}
};
nglr.Lexer.prototype.peek = function() {
if (this.index + 1 < this.text.length) {
return this.text.charAt(this.index + 1);
} else {
return false;
}
};
nglr.Lexer.prototype.parse = function() {
var tokens = this.tokens;
var OPERATORS = nglr.Lexer.OPERATORS;
var canStartRegExp = true;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (ch == '"' || ch == "'") {
this.readString(ch);
canStartRegExp = true;
} else if (ch == '(' || ch == '[') {
tokens.push({index:this.index, text:ch});
this.index++;
} else if (ch == '{' ) {
var peekCh = this.peek();
if (peekCh == ':' || peekCh == '(') {
tokens.push({index:this.index, text:ch + peekCh});
this.index++;
} else {
tokens.push({index:this.index, text:ch});
}
this.index++;
canStartRegExp = true;
} else if (ch == ')' || ch == ']' || ch == '}' ) {
tokens.push({index:this.index, text:ch});
this.index++;
canStartRegExp = false;
} else if ( ch == ':' || ch == '.' || ch == ',' || ch == ';') {
tokens.push({index:this.index, text:ch});
this.index++;
canStartRegExp = true;
} else if ( canStartRegExp && ch == '/' ) {
this.readRegexp();
canStartRegExp = false;
} else if ( this.isNumber(ch) ) {
this.readNumber();
canStartRegExp = false;
} else if (this.isIdent(ch)) {
this.readIdent();
canStartRegExp = false;
} else if (this.isWhitespace(ch)) {
this.index++;
} else {
var ch2 = ch + this.peek();
var fn = OPERATORS[ch];
var fn2 = OPERATORS[ch2];
if (fn2) {
tokens.push({index:this.index, text:ch2, fn:fn2});
this.index += 2;
} else if (fn) {
tokens.push({index:this.index, text:ch, fn:fn});
this.index += 1;
} else {
throw "Lexer Error: Unexpected next character [" +
this.text.substring(this.index) +
"] in expression '" + this.text +
"' at column '" + (this.index+1) + "'.";
}
canStartRegExp = true;
}
}
return tokens;
};
nglr.Lexer.prototype.isNumber = function(ch) {
return '0' <= ch && ch <= '9';
};
nglr.Lexer.prototype.isWhitespace = function(ch) {
return ch == ' ' || ch == '\r' || ch == '\t' ||
ch == '\n' || ch == '\v';
};
nglr.Lexer.prototype.isIdent = function(ch) {
return 'a' <= ch && ch <= 'z' ||
'A' <= ch && ch <= 'Z' ||
'_' == ch || ch == '$';
};
nglr.Lexer.prototype.readNumber = function() {
var number = "";
var start = this.index;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (ch == '.' || this.isNumber(ch)) {
number += ch;
} else {
break;
}
this.index++;
}
number = 1 * number;
this.tokens.push({index:start, text:number,
fn:function(){return number;}});
};
nglr.Lexer.prototype.readIdent = function() {
var ident = "";
var start = this.index;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (ch == '.' || this.isIdent(ch) || this.isNumber(ch)) {
ident += ch;
} else {
break;
}
this.index++;
}
var fn = nglr.Lexer.OPERATORS[ident];
if (!fn) {
fn = function(self){
return self.scope.get(ident);
};
fn.isAssignable = ident;
}
this.tokens.push({index:start, text:ident, fn:fn});
};
nglr.Lexer.ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
nglr.Lexer.prototype.readString = function(quote) {
var start = this.index;
var dateParseLength = this.dateParseLength;
this.index++;
var string = "";
var escape = false;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (escape) {
if (ch == 'u') {
var hex = this.text.substring(this.index + 1, this.index + 5);
this.index += 4;
string += String.fromCharCode(parseInt(hex, 16));
} else {
var rep = nglr.Lexer.ESCAPE[ch];
if (rep) {
string += rep;
} else {
string += ch;
}
}
escape = false;
} else if (ch == '\\') {
escape = true;
} else if (ch == quote) {
this.index++;
this.tokens.push({index:start, text:string,
fn:function(){
return (string.length == dateParseLength) ?
angular.String.toDate(string) : string;
}});
return;
} else {
string += ch;
}
this.index++;
}
throw "Lexer Error: Unterminated quote [" +
this.text.substring(start) + "] starting at column '" +
(start+1) + "' in expression '" + this.text + "'.";
};
nglr.Lexer.prototype.readRegexp = function(quote) {
var start = this.index;
this.index++;
var regexp = "";
var escape = false;
while (this.index < this.text.length) {
var ch = this.text.charAt(this.index);
if (escape) {
regexp += ch;
escape = false;
} else if (ch === '\\') {
regexp += ch;
escape = true;
} else if (ch === '/') {
this.index++;
var flags = "";
if (this.isIdent(this.text.charAt(this.index))) {
this.readIdent();
flags = this.tokens.pop().text;
}
var compiledRegexp = new RegExp(regexp, flags);
this.tokens.push({index:start, text:regexp, flags:flags,
fn:function(){return compiledRegexp;}});
return;
} else {
regexp += ch;
}
this.index++;
}
throw "Lexer Error: Unterminated RegExp [" +
this.text.substring(start) + "] starting at column '" +
(start+1) + "' in expression '" + this.text + "'.";
};
nglr.Parser = function(text, parseStrings){
this.text = text;
this.tokens = new nglr.Lexer(text, parseStrings).parse();
this.index = 0;
};
nglr.Parser.ZERO = function(){
return 0;
};
nglr.Parser.prototype.error = function(msg, token) {
throw "Token '" + token.text +
"' is " + msg + " at column='" +
(token.index + 1) + "' of expression '" +
this.text + "' starting at '" + this.text.substring(token.index) + "'.";
};
nglr.Parser.prototype.peekToken = function() {
if (this.tokens.length === 0)
throw "Unexpected end of expression: " + this.text;
return this.tokens[0];
};
nglr.Parser.prototype.peek = function(e1, e2, e3, e4) {
var tokens = this.tokens;
if (tokens.length > 0) {
var token = tokens[0];
var t = token.text;
if (t==e1 || t==e2 || t==e3 || t==e4 ||
(!e1 && !e2 && !e3 && !e4)) {
return token;
}
}
return false;
};
nglr.Parser.prototype.expect = function(e1, e2, e3, e4){
var token = this.peek(e1, e2, e3, e4);
if (token) {
this.tokens.shift();
this.currentToken = token;
return token;
}
return false;
};
nglr.Parser.prototype.consume = function(e1){
if (!this.expect(e1)) {
var token = this.peek();
throw "Expecting '" + e1 + "' at column '" +
(token.index+1) + "' in '" +
this.text + "' got '" +
this.text.substring(token.index) + "'.";
}
};
nglr.Parser.prototype._unary = function(fn, parse) {
var right = parse.apply(this);
return function(self) {
return fn(self, right(self));
};
};
nglr.Parser.prototype._binary = function(left, fn, parse) {
var right = parse.apply(this);
return function(self) {
return fn(self, left(self), right(self));
};
};
nglr.Parser.prototype.hasTokens = function () {
return this.tokens.length > 0;
};
nglr.Parser.prototype.assertAllConsumed = function(){
if (this.tokens.length !== 0) {
throw "Did not understand '" + this.text.substring(this.tokens[0].index) +
"' while evaluating '" + this.text + "'.";
}
};
nglr.Parser.prototype.statements = function(){
var statements = [];
while(true) {
if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
statements.push(this.filterChain());
if (!this.expect(';')) {
return function (self){
var value;
for ( var i = 0; i < statements.length; i++) {
var statement = statements[i];
if (statement)
value = statement(self);
}
return value;
};
}
}
};
nglr.Parser.prototype.filterChain = function(){
var left = this.expression();
var token;
while(true) {
if ((token = this.expect('|'))) {
left = this._binary(left, token.fn, this.filter);
} else {
return left;
}
}
};
nglr.Parser.prototype.filter = function(){
return this._pipeFunction(angular.filter);
};
nglr.Parser.prototype.validator = function(){
return this._pipeFunction(angular.validator);
};
nglr.Parser.prototype._pipeFunction = function(fnScope){
var fn = this.functionIdent(fnScope);
var argsFn = [];
var token;
while(true) {
if ((token = this.expect(':'))) {
argsFn.push(this.expression());
} else {
var fnInvoke = function(self, input){
var args = [input];
for ( var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](self));
}
return fn.apply(self, args);
};
return function(){
return fnInvoke;
};
}
}
};
nglr.Parser.prototype.expression = function(){
return this.throwStmt();
};
nglr.Parser.prototype.throwStmt = function(){
if (this.expect('throw')) {
var throwExp = this.assignment();
return function (self) {
throw throwExp(self);
};
} else {
return this.assignment();
}
};
nglr.Parser.prototype.assignment = function(){
var left = this.logicalOR();
var token;
if (token = this.expect('=')) {
if (!left.isAssignable) {
throw "Left hand side '" +
this.text.substring(0, token.index) + "' of assignment '" +
this.text.substring(token.index) + "' is not assignable.";
}
var ident = function(){return left.isAssignable;};
return this._binary(ident, token.fn, this.logicalOR);
} else {
return left;
}
};
nglr.Parser.prototype.logicalOR = function(){
var left = this.logicalAND();
var token;
while(true) {
if ((token = this.expect('||'))) {
left = this._binary(left, token.fn, this.logicalAND);
} else {
return left;
}
}
};
nglr.Parser.prototype.logicalAND = function(){
var left = this.negated();
var token;
while(true) {
if ((token = this.expect('&&'))) {
left = this._binary(left, token.fn, this.negated);
} else {
return left;
}
}
};
nglr.Parser.prototype.negated = function(){
var token;
if (token = this.expect('!')) {
return this._unary(token.fn, this.equality);
} else {
return this.equality();
}
};
nglr.Parser.prototype.equality = function(){
var left = this.relational();
var token;
while(true) {
if ((token = this.expect('==','!='))) {
left = this._binary(left, token.fn, this.relational);
} else {
return left;
}
}
};
nglr.Parser.prototype.relational = function(){
var left = this.additive();
var token;
while(true) {
if ((token = this.expect('<', '>', '<=', '>='))) {
left = this._binary(left, token.fn, this.additive);
} else {
return left;
}
}
};
nglr.Parser.prototype.additive = function(){
var left = this.multiplicative();
var token;
while(token = this.expect('+','-')) {
left = this._binary(left, token.fn, this.multiplicative);
}
return left;
};
nglr.Parser.prototype.multiplicative = function(){
var left = this.unary();
var token;
while(token = this.expect('*','/','%')) {
left = this._binary(left, token.fn, this.unary);
}
return left;
};
nglr.Parser.prototype.unary = function(){
var token;
if (this.expect('+')) {
return this.primary();
} else if (token = this.expect('-')) {
return this._binary(nglr.Parser.ZERO, token.fn, this.multiplicative);
} else {
return this.primary();
}
};
nglr.Parser.prototype.functionIdent = function(fnScope) {
var token = this.expect();
var element = token.text.split('.');
var instance = fnScope;
var key;
for ( var i = 0; i < element.length; i++) {
key = element[i];
if (instance)
instance = instance[key];
}
if (typeof instance != 'function') {
throw "Function '" + token.text + "' at column '" +
(token.index+1) + "' in '" + this.text + "' is not defined.";
}
return instance;
};
nglr.Parser.prototype.primary = function() {
var primary;
if (this.expect('(')) {
var expression = this.filterChain();
this.consume(')');
primary = expression;
} else if (this.expect('[')) {
primary = this.arrayDeclaration();
} else if (this.expect('{')) {
primary = this.object();
} else if (this.expect('{:')) {
primary = this.closure(false);
} else if (this.expect('{(')) {
primary = this.closure(true);
} else {
var token = this.expect();
primary = token.fn;
if (!primary) {
this.error("not a primary expression", token);
}
}
var next;
while (next = this.expect('(', '[', '.')) {
if (next.text === '(') {
primary = this.functionCall(primary);
} else if (next.text === '[') {
primary = this.objectIndex(primary);
} else if (next.text === '.') {
primary = this.fieldAccess(primary);
} else {
throw "IMPOSSIBLE";
}
}
return primary;
};
nglr.Parser.prototype.closure = function(hasArgs) {
var args = [];
if (hasArgs) {
if (!this.expect(')')) {
args.push(this.expect().text);
while(this.expect(',')) {
args.push(this.expect().text);
}
this.consume(')');
}
this.consume(":");
}
var statements = this.statements();
this.consume("}");
return function(self){
return function($){
var scope = new nglr.Scope(self.scope.state);
scope.set('$', $);
for ( var i = 0; i < args.length; i++) {
scope.set(args[i], arguments[i]);
}
return statements({scope:scope});
};
};
};
nglr.Parser.prototype.fieldAccess = function(object) {
var field = this.expect().text;
var fn = function (self){
return nglr.Scope.getter(object(self), field);
};
fn.isAssignable = field;
return fn;
};
nglr.Parser.prototype.objectIndex = function(obj) {
var indexFn = this.expression();
this.consume(']');
if (this.expect('=')) {
var rhs = this.expression();
return function (self){
return obj(self)[indexFn(self)] = rhs(self);
};
} else {
return function (self){
var o = obj(self);
var i = indexFn(self);
return (o) ? o[i] : undefined;
};
}
};
nglr.Parser.prototype.functionCall = function(fn) {
var argsFn = [];
if (this.peekToken().text != ')') {
do {
argsFn.push(this.expression());
} while (this.expect(','));
}
this.consume(')');
return function (self){
var args = [];
for ( var i = 0; i < argsFn.length; i++) {
args.push(argsFn[i](self));
}
var fnPtr = fn(self);
if (typeof fnPtr === 'function') {
return fnPtr.apply(self, args);
} else {
throw "Expression '" + fn.isAssignable + "' is not a function.";
}
};
};
// This is used with json array declaration
nglr.Parser.prototype.arrayDeclaration = function () {
var elementFns = [];
if (this.peekToken().text != ']') {
do {
elementFns.push(this.expression());
} while (this.expect(','));
}
this.consume(']');
return function (self){
var array = [];
for ( var i = 0; i < elementFns.length; i++) {
array.push(elementFns[i](self));
}
return array;
};
};
nglr.Parser.prototype.object = function () {
var keyValues = [];
if (this.peekToken().text != '}') {
do {
var key = this.expect().text;
this.consume(":");
var value = this.expression();
keyValues.push({key:key, value:value});
} while (this.expect(','));
}
this.consume('}');
return function (self){
var object = {};
for ( var i = 0; i < keyValues.length; i++) {
var keyValue = keyValues[i];
var value = keyValue.value(self);
object[keyValue.key] = value;
}
return object;
};
};
nglr.Parser.prototype.entityDeclaration = function () {
var decl = [];
while(this.hasTokens()) {
decl.push(this.entityDecl());
if (!this.expect(';')) {
this.assertAllConsumed();
}
}
return function (self){
var code = "";
for ( var i = 0; i < decl.length; i++) {
code += decl[i](self);
}
return code;
};
};
nglr.Parser.prototype.entityDecl = function () {
var entity = this.expect().text;
var instance;
var defaults;
if (this.expect('=')) {
instance = entity;
entity = this.expect().text;
}
if (this.expect(':')) {
defaults = this.primary()(null);
}
return function(self) {
var datastore = self.scope.get('$datastore');
var Entity = datastore.entity(entity, defaults);
self.scope.set(entity, Entity);
if (instance) {
var document = Entity();
document.$$anchor = instance;
self.scope.set(instance, document);
return "$anchor." + instance + ":{" +
instance + "=" + entity + ".load($anchor." + instance + ");" +
instance + ".$$anchor=" + angular.String.quote(instance) + ";" +
"};";
} else {
return "";
}
};
};
nglr.Parser.prototype.watch = function () {
var decl = [];
while(this.hasTokens()) {
decl.push(this.watchDecl());
if (!this.expect(';')) {
this.assertAllConsumed();
}
}
this.assertAllConsumed();
return function (self){
for ( var i = 0; i < decl.length; i++) {
var d = decl[i](self);
self.addListener(d.name, d.fn);
}
};
};
nglr.Parser.prototype.watchDecl = function () {
var anchorName = this.expect().text;
this.consume(":");
var expression;
if (this.peekToken().text == '{') {
this.consume("{");
expression = this.statements();
this.consume("}");
} else {
expression = this.expression();
}
return function(self) {
return {name:anchorName, fn:expression};
};
};

198
src/Scope.js Normal file
View file

@ -0,0 +1,198 @@
// Copyright (C) 2009 BRAT Tech LLC
nglr.Scope = function(initialState, name) {
this.widgets = [];
this.watchListeners = {};
this.name = name;
initialState = initialState || {};
var State = function(){};
State.prototype = initialState;
this.state = new State();
this.state.$parent = initialState;
if (name == "ROOT") {
this.state.$root = this.state;
}
};
nglr.Scope.expressionCache = {};
nglr.Scope.prototype.updateView = function() {
var self = this;
this.fireWatchers();
_.each(this.widgets, function(widget){
self.evalWidget(widget, "", {}, function(){
this.updateView(self);
});
});
};
nglr.Scope.prototype.addWidget = function(controller) {
if (controller) this.widgets.push(controller);
};
nglr.Scope.prototype.isProperty = function(exp) {
for ( var i = 0; i < exp.length; i++) {
var ch = exp.charAt(i);
if (ch!='.' && !nglr.Lexer.prototype.isIdent(ch)) {
return false;
}
}
return true;
};
nglr.Scope.getter = function(instance, path) {
if (!path) return instance;
var element = path.split('.');
var key;
var lastInstance = instance;
var len = element.length;
for ( var i = 0; i < len; i++) {
key = element[i];
if (!key.match(/^[\$\w][\$\w\d]*$/))
throw "Expression '" + path + "' is not a valid expression for accesing variables.";
if (instance) {
lastInstance = instance;
instance = instance[key];
}
if (_.isUndefined(instance) && key.charAt(0) == '$') {
var type = angular.Global.typeOf(lastInstance);
type = angular[type.charAt(0).toUpperCase()+type.substring(1)];
var fn = type ? type[[key.substring(1)]] : undefined;
if (fn) {
instance = _.bind(fn, lastInstance, lastInstance);
return instance;
}
}
}
if (typeof instance === 'function' && !instance.$$factory) {
return nglr.bind(lastInstance, instance);
}
return instance;
};
nglr.Scope.prototype.get = function(path) {
return nglr.Scope.getter(this.state, path);
};
nglr.Scope.prototype.set = function(path, value) {
var element = path.split('.');
var instance = this.state;
for ( var i = 0; element.length > 1; i++) {
var key = element.shift();
var newInstance = instance[key];
if (!newInstance) {
newInstance = {};
instance[key] = newInstance;
}
instance = newInstance;
}
instance[element.shift()] = value;
return value;
};
nglr.Scope.prototype.setEval = function(expressionText, value) {
this.eval(expressionText + "=" + nglr.toJson(value));
};
nglr.Scope.prototype.eval = function(expressionText, context) {
var expression = nglr.Scope.expressionCache[expressionText];
if (!expression) {
var parser = new nglr.Parser(expressionText);
expression = parser.statements();
parser.assertAllConsumed();
nglr.Scope.expressionCache[expressionText] = expression;
}
context = context || {};
context.scope = this;
return expression(context);
};
//TODO: Refactor. This function needs to be an execution closure for widgets
// move to widgets
// remove expression, just have inner closure.
nglr.Scope.prototype.evalWidget = function(widget, expression, context, onSuccess, onFailure) {
try {
var value = this.eval(expression, context);
if (widget.hasError) {
widget.hasError = false;
jQuery(widget.view).
removeClass('ng-exception').
removeAttr('ng-error');
}
if (onSuccess) {
value = onSuccess.apply(widget, [value]);
}
return true;
} catch (e){
console.error('Eval Widget Error:', e);
var jsonError = nglr.toJson(e, true);
widget.hasError = true;
jQuery(widget.view).
addClass('ng-exception').
attr('ng-error', jsonError);
if (onFailure) {
onFailure.apply(widget, [e, jsonError]);
}
return false;
}
};
nglr.Scope.prototype.validate = function(expressionText, value) {
var expression = nglr.Scope.expressionCache[expressionText];
if (!expression) {
expression = new nglr.Parser(expressionText).validator();
nglr.Scope.expressionCache[expressionText] = expression;
}
var self = {scope:this};
return expression(self)(self, value);
};
nglr.Scope.prototype.entity = function(entityDeclaration) {
var expression = new nglr.Parser(entityDeclaration).entityDeclaration();
return expression({scope:this});
};
nglr.Scope.prototype.markInvalid = function(widget) {
this.state.$invalidWidgets.push(widget);
};
nglr.Scope.prototype.watch = function(declaration) {
var self = this;
new nglr.Parser(declaration).watch()({
scope:this,
addListener:function(watch, exp){
self.addWatchListener(watch, function(n,o){
try {
return exp({scope:self}, n, o);
} catch(e) {
nglr.alert(e);
}
});
}
});
};
nglr.Scope.prototype.addWatchListener = function(watchExpression, listener) {
var watcher = this.watchListeners[watchExpression];
if (!watcher) {
watcher = {listeners:[], expression:watchExpression};
this.watchListeners[watchExpression] = watcher;
}
watcher.listeners.push(listener);
};
nglr.Scope.prototype.fireWatchers = function() {
var self = this;
var fired = false;
jQuery.each(this.watchListeners, function(name, watcher) {
var value = self.eval(watcher.expression);
if (value !== watcher.lastValue) {
jQuery.each(watcher.listeners, function(i, listener){
listener(value, watcher.lastValue);
fired = true;
});
watcher.lastValue = value;
}
});
return fired;
};

69
src/Server.js Normal file
View file

@ -0,0 +1,69 @@
// Copyright (C) 2008,2009 BRAT Tech LLC
nglr.Server = function(url, getScript) {
this.url = url;
this.nextId = 0;
this.getScript = getScript;
this.uuid = "_" + ("" + Math.random()).substr(2) + "_";
this.maxSize = 1800;
};
nglr.Server.prototype.base64url = function(txt) {
return Base64.encode(txt);
};
nglr.Server.prototype.request = function(method, url, request, callback) {
var requestId = this.uuid + (this.nextId++);
nglr[requestId] = function(response) {
delete nglr[requestId];
callback(200, response);
};
var payload = {u:url, m:method, p:request};
payload = this.base64url(nglr.toJson(payload));
var totalPockets = Math.ceil(payload.length / this.maxSize);
var baseUrl = this.url + "/$/" + requestId + "/" + totalPockets + "/";
for ( var pocketNo = 0; pocketNo < totalPockets; pocketNo++) {
var pocket = payload.substr(pocketNo * this.maxSize, this.maxSize);
this.getScript(baseUrl + (pocketNo+1) + "?h=" + pocket, nglr.noop);
}
};
nglr.FrameServer = function(frame) {
this.frame = frame;
};
nglr.FrameServer.PREFIX = "$DATASET:";
nglr.FrameServer.prototype = {
read:function(){
this.data = nglr.fromJson(this.frame.name.substr(nglr.FrameServer.PREFIX.length));
},
write:function(){
this.frame.name = nglr.FrameServer.PREFIX + nglr.toJson(this.data);
},
request: function(method, url, request, callback) {
//alert(method + " " + url + " " + nglr.toJson(request) + " " + nglr.toJson(callback));
}
};
nglr.VisualServer = function(delegate, status, update) {
this.delegate = delegate;
this.update = update;
this.status = status;
};
nglr.VisualServer.prototype = {
request:function(method, url, request, callback) {
var self = this;
this.status.beginRequest(request);
this.delegate.request(method, url, request, function() {
self.status.endRequest();
try {
callback.apply(this, arguments);
} catch (e) {
nglr.alert(nglr.toJson(e));
}
self.update();
});
}
};

36
src/Users.js Normal file
View file

@ -0,0 +1,36 @@
// Copyright (C) 2008,2009 BRAT Tech LLC
nglr.Users = function(server, controlBar) {
this.server = server;
this.controlBar = controlBar;
};
nglr.Users.prototype = {
fetchCurrentUser:function(callback) {
var self = this;
this.server.request("GET", "/account.json", {}, function(code, response){
self.current = response.user;
callback(response.user);
});
},
logout: function(callback) {
var self = this;
this.controlBar.logout(function(){
delete self.current;
(callback||nglr.noop)();
});
},
login: function(callback) {
var self = this;
this.controlBar.login(function(){
self.fetchCurrentUser(function(){
(callback||nglr.noop)();
});
});
},
notAuthorized: function(){
this.controlBar.notAuthorized();
}
};

80
src/Validators.js Normal file
View file

@ -0,0 +1,80 @@
// Copyright (C) 2009 BRAT Tech LLC
angular.validator.regexp = function(value, regexp, msg) {
if (!value.match(regexp)) {
return msg ||
"Value does not match expected format " + regexp + ".";
} else {
return null;
}
};
angular.validator.number = function(value, min, max) {
var num = 1 * value;
if (num == value) {
if (typeof min != 'undefined' && num < min) {
return "Value can not be less than " + min + ".";
}
if (typeof min != 'undefined' && num > max) {
return "Value can not be greater than " + max + ".";
}
return null;
} else {
return "Value is not a number.";
}
};
angular.validator.integer = function(value, min, max) {
var number = angular.validator.number(value, min, max);
if (number === null && value != Math.round(value)) {
return "Value is not a whole number.";
}
return number;
};
angular.validator.date = function(value, min, max) {
if (value.match(/^\d\d?\/\d\d?\/\d\d\d\d$/)) {
return null;
}
return "Value is not a date. (Expecting format: 12/31/2009).";
};
angular.validator.ssn = function(value) {
if (value.match(/^\d\d\d-\d\d-\d\d\d\d$/)) {
return null;
}
return "SSN needs to be in 999-99-9999 format.";
};
angular.validator.email = function(value) {
if (value.match(/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/)) {
return null;
}
return "Email needs to be in username@host.com format.";
};
angular.validator.phone = function(value) {
if (value.match(/^1\(\d\d\d\)\d\d\d-\d\d\d\d$/)) {
return null;
}
if (value.match(/^\+\d{2,3} (\(\d{1,5}\))?[\d ]+\d$/)) {
return null;
}
return "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly.";
};
angular.validator.url = function(value) {
if (value.match(/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/)) {
return null;
}
return "URL needs to be in http://server[:port]/path format.";
};
angular.validator.json = function(value) {
try {
nglr.fromJson(value);
return null;
} catch (e) {
return e.toString();
}
};

774
src/Widgets.js Normal file
View file

@ -0,0 +1,774 @@
// Copyright (C) 2009 BRAT Tech LLC
nglr.WidgetFactory = function(serverUrl, database) {
this.nextUploadId = 0;
this.serverUrl = serverUrl;
this.database = database;
this.createSWF = swfobject.createSWF;
this.onChangeListener = function(){};
};
nglr.WidgetFactory.prototype.createController = function(input, scope) {
var controller;
var type = input.attr('type').toLowerCase();
var exp = input.attr('name');
if (exp) exp = exp.split(':').pop();
var event = "change";
var bubbleEvent = true;
if (type == 'button' || type == 'submit' || type == 'reset' || type == 'image') {
controller = new nglr.ButtonController(input[0], exp);
event = "click";
bubbleEvent = false;
} else if (type == 'text' || type == 'textarea' || type == 'hidden' || type == 'password') {
controller = new nglr.TextController(input[0], exp);
event = "keyup change";
} else if (type == 'checkbox') {
controller = new nglr.CheckboxController(input[0], exp);
event = "click";
} else if (type == 'radio') {
controller = new nglr.RadioController(input[0], exp);
event="click";
} else if (type == 'select-one') {
controller = new nglr.SelectController(input[0], exp);
} else if (type == 'select-multiple') {
controller = new nglr.MultiSelectController(input[0], exp);
} else if (type == 'file') {
controller = this.createFileController(input, exp);
} else {
throw 'Unknown type: ' + type;
}
input.data('controller', controller);
var binder = scope.get('$binder');
var action = function() {
if (controller.updateModel(scope)) {
var action = jQuery(controller.view).attr('ng-action') || "";
if (scope.evalWidget(controller, action)) {
binder.updateView(scope);
}
}
return bubbleEvent;
};
jQuery(controller.view, ":input").
bind(event, action);
return controller;
};
nglr.WidgetFactory.prototype.createFileController = function(fileInput) {
var uploadId = '__uploadWidget_' + (this.nextUploadId++);
var view = nglr.FileController.template(uploadId);
fileInput.after(view);
var att = {
data:this.serverUrl + "/admin/ServerAPI.swf",
width:"95", height:"20", align:"top",
wmode:"transparent"};
var par = {
flashvars:"uploadWidgetId=" + uploadId,
allowScriptAccess:"always"};
var swfNode = this.createSWF(att, par, uploadId);
fileInput.remove();
var cntl = new nglr.FileController(view, fileInput[0].name, swfNode, this.serverUrl + "/data/" + this.database);
jQuery(swfNode).data('controller', cntl);
return cntl;
};
nglr.WidgetFactory.prototype.createTextWidget = function(textInput) {
var controller = new nglr.TextController(textInput);
controller.onChange(this.onChangeListener);
return controller;
};
/////////////////////
// FileController
///////////////////////
nglr.FileController = function(view, scopeName, uploader, databaseUrl) {
this.view = view;
this.uploader = uploader;
this.scopeName = scopeName;
this.attachmentsPath = databaseUrl + '/_attachments';
this.value = null;
this.lastValue = undefined;
};
nglr.FileController.dispatchEvent = function(id, event, args) {
var object = document.getElementById(id);
var controller = jQuery(object).data("controller");
nglr.FileController.prototype['_on_' + event].apply(controller, args);
};
nglr.FileController.template = function(id) {
return jQuery('<span class="ng-upload-widget">' +
'<input type="checkbox" ng-non-bindable="true"/>' +
'<object id="' + id + '" />' +
'<a></a>' +
'<span/>' +
'</span>');
};
nglr.FileController.prototype._on_cancel = function() {
};
nglr.FileController.prototype._on_complete = function() {
};
nglr.FileController.prototype._on_httpStatus = function(status) {
nglr.alert("httpStatus:" + this.scopeName + " status:" + status);
};
nglr.FileController.prototype._on_ioError = function() {
nglr.alert("ioError:" + this.scopeName);
};
nglr.FileController.prototype._on_open = function() {
nglr.alert("open:" + this.scopeName);
};
nglr.FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) {
};
nglr.FileController.prototype._on_securityError = function() {
nglr.alert("securityError:" + this.scopeName);
};
nglr.FileController.prototype._on_uploadCompleteData = function(data) {
var value = nglr.fromJson(data);
value.url = this.attachmentsPath + '/' + value.id + '/' + value.text;
this.view.find("input").attr('checked', true);
var scope = this.view.scope();
this.value = value;
this.updateModel(scope);
this.value = null;
scope.get('$binder').updateView();
};
nglr.FileController.prototype._on_select = function(name, size, type) {
this.name = name;
this.view.find("a").text(name).attr('href', name);
this.view.find("span").text(angular.filter.bytes(size));
this.upload();
};
nglr.FileController.prototype.updateModel = function(scope) {
var isChecked = this.view.find("input").attr('checked');
var value = isChecked ? this.value : null;
if (this.lastValue === value) {
return false;
} else {
scope.set(this.scopeName, value);
return true;
}
};
nglr.FileController.prototype.updateView = function(scope) {
var modelValue = scope.get(this.scopeName);
if (modelValue && this.value !== modelValue) {
this.value = modelValue;
this.view.find("a").
attr("href", this.value.url).
text(this.value.text);
this.view.find("span").text(angular.filter.bytes(this.value.size));
}
this.view.find("input").attr('checked', !!modelValue);
};
nglr.FileController.prototype.upload = function() {
if (this.name) {
this.uploader.uploadFile(this.attachmentsPath);
}
};
///////////////////////
// NullController
///////////////////////
nglr.NullController = function(view) {this.view = view;};
nglr.NullController.prototype.updateModel = function() { return true; };
nglr.NullController.prototype.updateView = function() { };
nglr.NullController.instance = new nglr.NullController();
///////////////////////
// ButtonController
///////////////////////
nglr.ButtonController = function(view) {this.view = view;};
nglr.ButtonController.prototype.updateModel = function(scope) { return true; };
nglr.ButtonController.prototype.updateView = function(scope) {};
///////////////////////
// TextController
///////////////////////
nglr.TextController = function(view, exp) {
this.view = view;
this.exp = exp;
this.validator = view.getAttribute('ng-validate');
this.required = typeof view.attributes['ng-required'] != "undefined";
this.lastErrorText = null;
this.lastValue = undefined;
this.initialValue = view.value;
var widget = view.getAttribute('ng-widget');
if (widget === 'datepicker') {
jQuery(view).datepicker();
}
};
nglr.TextController.prototype.updateModel = function(scope) {
var value = this.view.value;
if (this.lastValue === value) {
return false;
} else {
scope.setEval(this.exp, value);
this.lastValue = value;
return true;
}
};
nglr.TextController.prototype.updateView = function(scope) {
var view = this.view;
var value = scope.get(this.exp);
if (typeof value === "undefined") {
value = this.initialValue;
scope.setEval(this.exp, value);
}
value = value ? value : '';
if (this.lastValue != value) {
view.value = value;
this.lastValue = value;
}
var isValidationError = false;
view.removeAttribute('ng-error');
if (this.required) {
isValidationError = !(value && value.length > 0);
}
var errorText = isValidationError ? "Required Value" : null;
if (!isValidationError && this.validator && value) {
errorText = scope.validate(this.validator, value);
isValidationError = !!errorText;
}
if (this.lastErrorText !== errorText) {
this.lastErrorText = isValidationError;
if (errorText !== null) {
view.setAttribute('ng-error', errorText);
scope.markInvalid(this);
}
jQuery(view).toggleClass('ng-validation-error', isValidationError);
}
};
///////////////////////
// CheckboxController
///////////////////////
nglr.CheckboxController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastValue = undefined;
this.initialValue = view.checked ? view.value : "";
};
nglr.CheckboxController.prototype.updateModel = function(scope) {
var input = this.view;
var value = input.checked ? input.value : '';
if (this.lastValue === value) {
return false;
} else {
scope.setEval(this.exp, value);
this.lastValue = value;
return true;
}
};
nglr.CheckboxController.prototype.updateView = function(scope) {
var input = this.view;
var value = scope.eval(this.exp);
if (typeof value === "undefined") {
value = this.initialValue;
scope.setEval(this.exp, value);
}
input.checked = input.value == (''+value);
};
///////////////////////
// SelectController
///////////////////////
nglr.SelectController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastValue = undefined;
this.initialValue = view.value;
};
nglr.SelectController.prototype.updateModel = function(scope) {
var input = this.view;
if (input.selectedIndex < 0) {
scope.setEval(this.exp, null);
} else {
var value = this.view.value;
if (this.lastValue === value) {
return false;
} else {
scope.setEval(this.exp, value);
this.lastValue = value;
return true;
}
}
};
nglr.SelectController.prototype.updateView = function(scope) {
var input = this.view;
var value = scope.get(this.exp);
if (typeof value === 'undefined') {
value = this.initialValue;
scope.setEval(this.exp, value);
}
if (value !== this.lastValue) {
input.value = value ? value : "";
this.lastValue = value;
}
};
///////////////////////
// MultiSelectController
///////////////////////
nglr.MultiSelectController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastValue = undefined;
this.initialValue = this.selected();
};
nglr.MultiSelectController.prototype.selected = function () {
var value = [];
var options = this.view.options;
for ( var i = 0; i < options.length; i++) {
var option = options[i];
if (option.selected) {
value.push(option.value);
}
}
return value;
};
nglr.MultiSelectController.prototype.updateModel = function(scope) {
var value = this.selected();
// TODO: This is wrong! no caching going on here as we are always comparing arrays
if (this.lastValue === value) {
return false;
} else {
scope.setEval(this.exp, value);
this.lastValue = value;
return true;
}
};
nglr.MultiSelectController.prototype.updateView = function(scope) {
var input = this.view;
var selected = scope.get(this.exp);
if (typeof selected === "undefined") {
selected = this.initialValue;
scope.setEval(this.exp, selected);
}
if (selected !== this.lastValue) {
var options = input.options;
for ( var i = 0; i < options.length; i++) {
var option = options[i];
option.selected = _.include(selected, option.value);
}
this.lastValue = selected;
}
};
///////////////////////
// RadioController
///////////////////////
nglr.RadioController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastChecked = undefined;
this.lastValue = undefined;
this.inputValue = view.value;
this.initialValue = view.checked ? view.value : null;
};
nglr.RadioController.prototype.updateModel = function(scope) {
var input = this.view;
if (this.lastChecked) {
return false;
} else {
input.checked = true;
this.lastValue = scope.setEval(this.exp, this.inputValue);
this.lastChecked = true;
return true;
}
};
nglr.RadioController.prototype.updateView = function(scope) {
var input = this.view;
var value = scope.get(this.exp);
if (this.initialValue && typeof value === "undefined") {
value = this.initialValue;
scope.setEval(this.exp, value);
}
if (this.lastValue != value) {
this.lastChecked = input.checked = this.inputValue == (''+value);
this.lastValue = value;
}
};
///////////////////////
//ElementController
///////////////////////
nglr.BindUpdater = function(view, exp) {
this.view = view;
this.exp = nglr.Binder.parseBindings(exp);
this.hasError = false;
this.scopeSelf = {element:view};
};
nglr.BindUpdater.toText = function(obj) {
var e = nglr.escapeHtml;
switch(typeof obj) {
case "string":
case "boolean":
case "number":
return e(obj);
case "function":
return nglr.BindUpdater.toText(obj());
case "object":
if (nglr.isNode(obj)) {
return nglr.outerHTML(obj);
} else if (obj instanceof angular.filter.Meta) {
switch(typeof obj.html) {
case "string":
case "number":
return obj.html;
case "function":
return obj.html();
case "object":
if (nglr.isNode(obj.html))
return nglr.outerHTML(obj.html);
default:
break;
}
switch(typeof obj.text) {
case "string":
case "number":
return e(obj.text);
case "function":
return e(obj.text());
default:
break;
}
}
if (obj === null)
return "";
return e(nglr.toJson(obj, true));
default:
return "";
}
};
nglr.BindUpdater.prototype.updateModel = function(scope) {};
nglr.BindUpdater.prototype.updateView = function(scope) {
var html = [];
var parts = this.exp;
var length = parts.length;
for(var i=0; i<length; i++) {
var part = parts[i];
var binding = nglr.Binder.binding(part);
if (binding) {
scope.evalWidget(this, binding, this.scopeSelf, function(value){
html.push(nglr.BindUpdater.toText(value));
}, function(e, text){
nglr.setHtml(this.view, text);
});
if (this.hasError) {
return;
}
} else {
html.push(nglr.escapeHtml(part));
}
}
nglr.setHtml(this.view, html.join(''));
};
nglr.BindAttrUpdater = function(view, attrs) {
this.view = view;
this.attrs = attrs;
};
nglr.BindAttrUpdater.prototype.updateModel = function(scope) {};
nglr.BindAttrUpdater.prototype.updateView = function(scope) {
var jNode = jQuery(this.view);
var attributeTemplates = this.attrs;
if (this.hasError) {
this.hasError = false;
jNode.
removeClass('ng-exception').
removeAttr('ng-error');
}
var isImage = jNode.is('img');
for (var attrName in attributeTemplates) {
var attributeTemplate = nglr.Binder.parseBindings(attributeTemplates[attrName]);
var attrValues = [];
for ( var i = 0; i < attributeTemplate.length; i++) {
var binding = nglr.Binder.binding(attributeTemplate[i]);
if (binding) {
try {
var value = scope.eval(binding, {element:jNode[0], attrName:attrName});
if (value && (value.constructor !== nglr.array || value.length !== 0))
attrValues.push(value);
} catch (e) {
this.hasError = true;
console.error('BindAttrUpdater', e);
var jsonError = nglr.toJson(e, true);
attrValues.push('[' + jsonError + ']');
jNode.
addClass('ng-exception').
attr('ng-error', jsonError);
}
} else {
attrValues.push(attributeTemplate[i]);
}
}
var attrValue = attrValues.length ? attrValues.join('') : null;
if(isImage && attrName == 'src' && !attrValue)
attrValue = scope.get('config.server') + '/images/blank.gif';
jNode.attr(attrName, attrValue);
}
};
nglr.EvalUpdater = function(view, exp) {
this.view = view;
this.exp = exp;
this.hasError = false;
};
nglr.EvalUpdater.prototype.updateModel = function(scope) {};
nglr.EvalUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp);
};
nglr.HideUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.HideUpdater.prototype.updateModel = function(scope) {};
nglr.HideUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(hideValue){
var view = jQuery(this.view);
if (nglr.toBoolean(hideValue)) {
view.hide();
} else {
view.show();
}
});
};
nglr.ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ShowUpdater.prototype.updateModel = function(scope) {};
nglr.ShowUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(hideValue){
var view = jQuery(this.view);
if (nglr.toBoolean(hideValue)) {
view.show();
} else {
view.hide();
}
});
};
nglr.ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ClassUpdater.prototype.updateModel = function(scope) {};
nglr.ClassUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(classValue){
if (classValue !== null && classValue !== undefined) {
this.view.className = classValue;
}
});
};
nglr.ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ClassEvenUpdater.prototype.updateModel = function(scope) {};
nglr.ClassEvenUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(classValue){
var index = scope.get('$index');
jQuery(this.view).toggleClass(classValue, index % 2 === 1);
});
};
nglr.ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ClassOddUpdater.prototype.updateModel = function(scope) {};
nglr.ClassOddUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(classValue){
var index = scope.get('$index');
jQuery(this.view).toggleClass(classValue, index % 2 === 0);
});
};
nglr.StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.StyleUpdater.prototype.updateModel = function(scope) {};
nglr.StyleUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(styleValue){
jQuery(this.view).attr('style', "").css(styleValue);
});
};
///////////////////////
// RepeaterUpdater
///////////////////////
nglr.RepeaterUpdater = function(view, repeaterExpression, template, prefix) {
this.view = view;
this.template = template;
this.prefix = prefix;
this.children = [];
var match = repeaterExpression.match(/^\s*(.+)\s+in\s+(.*)\s*$/);
if (! match) {
throw "Expected ng-repeat in form of 'item in collection' but got '" +
repeaterExpression + "'.";
}
var keyValue = match[1];
this.iteratorExp = match[2];
match = keyValue.match(/^([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\)$/);
if (!match) {
throw "'item' in 'item in collection' should be identifier or (key, value) but get '" +
keyValue + "'.";
}
this.valueExp = match[3] || match[1];
this.keyExp = match[2];
};
nglr.RepeaterUpdater.prototype.updateModel = function(scope) {};
nglr.RepeaterUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.iteratorExp, {}, function(iterator){
var self = this;
if (!iterator) {
iterator = [];
if (scope.isProperty(this.iteratorExp)) {
scope.set(this.iteratorExp, iterator);
}
}
var iteratorLength = iterator.length;
var childrenLength = this.children.length;
var cursor = this.view;
var time = 0;
var child = null;
var keyExp = this.keyExp;
var valueExp = this.valueExp;
var i = 0;
jQuery.each(iterator, function(key, value){
if (i < childrenLength) {
// reuse children
child = self.children[i];
child.scope.set(valueExp, value);
} else {
// grow children
var name = self.prefix +
valueExp + " in " + self.iteratorExp + "[" + i + "]";
var childScope = new nglr.Scope(scope.state, name);
childScope.set('$index', i);
if (keyExp)
childScope.set(keyExp, key);
childScope.set(valueExp, value);
child = { scope:childScope, element:self.template(childScope, self.prefix, i) };
cursor.after(child.element);
self.children.push(child);
}
cursor = child.element;
var s = new Date().getTime();
child.scope.updateView();
time += new Date().getTime() - s;
i++;
});
// shrink children
for ( var r = childrenLength; r > iteratorLength; --r) {
var unneeded = this.children.pop();
unneeded.element.removeNode();
}
// Special case for option in select
if (child && child.element[0].nodeName === "OPTION") {
var select = jQuery(child.element[0].parentNode);
var cntl = select.data('controller');
if (cntl) {
cntl.lastValue = undefined;
cntl.updateView(scope);
}
}
});
};
//////////////////////////////////
// PopUp
//////////////////////////////////
nglr.PopUp = function(doc) {
this.doc = doc;
};
nglr.PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup";
nglr.PopUp.prototype.bind = function () {
var self = this;
this.doc.find('.ng-validation-error,.ng-exception').
live("mouseover", nglr.PopUp.onOver);
};
nglr.PopUp.onOver = function(e) {
nglr.PopUp.onOut();
var jNode = jQuery(this);
jNode.bind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut);
var position = jNode.position();
var de = document.documentElement;
var w = self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
var hasArea = w - position.left;
var width = 300;
var title = jNode.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error...";
var msg = jNode.attr("ng-error");
var x;
var arrowPos = hasArea>(width+75) ? "left" : "right";
var tip = jQuery(
"<div id='ng-callout' style='width:"+width+"px'>" +
"<div class='ng-arrow-"+arrowPos+"'/>" +
"<div class='ng-title'>"+title+"</div>" +
"<div class='ng-content'>"+msg+"</div>" +
"</div>");
jQuery("body").append(tip);
if(arrowPos === 'left'){
x = position.left + this.offsetWidth + 11;
}else{
x = position.left - (width + 15);
tip.find('.ng-arrow-right').css({left:width+1});
}
tip.css({left: x+"px", top: (position.top - 3)+"px"});
return true;
};
nglr.PopUp.onOut = function() {
jQuery('#ng-callout').
unbind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut).
remove();
return true;
};
//////////////////////////////////
// Status
//////////////////////////////////
nglr.Status = function(body) {
this.loader = body.append(nglr.Status.DOM).find("#ng-loading");
this.requestCount = 0;
};
nglr.Status.DOM ='<div id="ng-spacer"></div><div id="ng-loading">loading....</div>';
nglr.Status.prototype.beginRequest = function () {
if (this.requestCount === 0) {
this.loader.show();
}
this.requestCount++;
};
nglr.Status.prototype.endRequest = function () {
this.requestCount--;
if (this.requestCount === 0) {
this.loader.hide("fold");
}
};

764
src/Widgets.js.orig Normal file
View file

@ -0,0 +1,764 @@
// Copyright (C) 2009 BRAT Tech LLC
nglr.WidgetFactory = function(serverUrl) {
this.nextUploadId = 0;
this.serverUrl = serverUrl;
this.createSWF = swfobject.createSWF;
this.onChangeListener = function(){};
};
nglr.WidgetFactory.prototype.createController = function(input, scope) {
var controller;
var type = input.attr('type').toLowerCase();
var exp = input.attr('name');
if (exp) exp = exp.split(':').pop();
var event = "change";
var bubbleEvent = true;
if (type == 'button' || type == 'submit' || type == 'reset') {
controller = new nglr.ButtonController(input[0], exp);
event = "click";
bubbleEvent = false;
} else if (type == 'text' || type == 'textarea') {
controller = new nglr.TextController(input[0], exp);
event = "keyup change";
} else if (type == 'checkbox') {
controller = new nglr.CheckboxController(input[0], exp);
event = "click";
} else if (type == 'radio') {
controller = new nglr.RadioController(input[0], exp);
event="click";
} else if (type == 'select-one') {
controller = new nglr.SelectController(input[0], exp);
} else if (type == 'select-multiple') {
controller = new nglr.MultiSelectController(input[0], exp);
} else if (type == 'file') {
controller = this.createFileController(input, exp);
} else {
throw 'Unknown type: ' + type;
}
input.data('controller', controller);
var binder = scope.get('$binder');
var action = function() {
if (controller.updateModel(scope)) {
var action = jQuery(controller.view).attr('ng-action') || "";
if (scope.evalWidget(controller, action)) {
binder.updateView(scope);
}
}
return bubbleEvent;
};
jQuery(controller.view, ":input").
bind(event, action);
return controller;
};
nglr.WidgetFactory.prototype.createFileController = function(fileInput) {
var uploadId = '__uploadWidget_' + (this.nextUploadId++);
var view = nglr.FileController.template(uploadId);
fileInput.after(view);
var att = {
data:this.serverUrl + "/admin/ServerAPI.swf",
width:"95", height:"20", align:"top",
wmode:"transparent"};
var par = {
flashvars:"uploadWidgetId=" + uploadId,
allowScriptAccess:"always"};
var swfNode = this.createSWF(att, par, uploadId);
fileInput.remove();
var cntl = new nglr.FileController(view, fileInput[0].name, swfNode, this.serverUrl);
jQuery(swfNode).data('controller', cntl);
return cntl;
};
nglr.WidgetFactory.prototype.createTextWidget = function(textInput) {
var controller = new nglr.TextController(textInput);
controller.onChange(this.onChangeListener);
return controller;
};
/////////////////////
// FileController
///////////////////////
nglr.FileController = function(view, scopeName, uploader, serverUrl) {
this.view = view;
this.uploader = uploader;
this.scopeName = scopeName;
this.uploadUrl = serverUrl + '/upload';
this.attachmentBase = serverUrl + '/attachments';
this.value = null;
this.lastValue = undefined;
};
nglr.FileController.dispatchEvent = function(id, event, args) {
var object = document.getElementById(id);
var controller = jQuery(object).data("controller");
nglr.FileController.prototype['_on_' + event].apply(controller, args);
};
nglr.FileController.template = function(id) {
return jQuery('<span class="ng-upload-widget">' +
'<input type="checkbox" ng-non-bindable="true"/>' +
'<object id="' + id + '" />' +
'<a></a>' +
'<span/>' +
'</span>');
};
nglr.FileController.prototype._on_cancel = function() {
};
nglr.FileController.prototype._on_complete = function() {
};
nglr.FileController.prototype._on_httpStatus = function(status) {
nglr.alert("httpStatus:" + this.scopeName + " status:" + status);
};
nglr.FileController.prototype._on_ioError = function() {
nglr.alert("ioError:" + this.scopeName);
};
nglr.FileController.prototype._on_open = function() {
nglr.alert("open:" + this.scopeName);
};
nglr.FileController.prototype._on_progress = function(bytesLoaded, bytesTotal) {
};
nglr.FileController.prototype._on_securityError = function() {
nglr.alert("securityError:" + this.scopeName);
};
nglr.FileController.prototype._on_uploadCompleteData = function(data) {
this.value = nglr.fromJson(data);
this.value.url = this.attachmentBase + '/' + this.value.id + '/' + this.value.text;
this.view.find("input").attr('checked', true);
var scope = this.view.scope();
this.updateModel(scope);
scope.get('$binder').updateView();
};
nglr.FileController.prototype._on_select = function(name, size, type) {
this.name = name;
this.view.find("a").text(name).attr('href', name);
this.view.find("span").text(filters.bytes(size));
this.upload();
};
nglr.FileController.prototype.updateModel = function(scope) {
var isChecked = this.view.find("input").attr('checked');
var value = isChecked ? this.value : null;
if (this.lastValue === value) {
return false;
} else {
scope.set(this.scopeName, value);
return true;
}
};
nglr.FileController.prototype.updateView = function(scope) {
var modelValue = scope.get(this.scopeName);
if (modelValue && this.value !== modelValue) {
this.value = modelValue;
this.view.find("a").
attr("href", this.value.url).
text(this.value.name);
this.view.find("span").text(filters.bytes(this.value.size));
}
this.view.find("input").attr('checked', !!modelValue);
};
nglr.FileController.prototype.upload = function() {
if (this.name) {
this.uploader.uploadFile(this.uploadUrl);
}
};
///////////////////////
// NullController
///////////////////////
nglr.NullController = function(view) {this.view = view;};
nglr.NullController.prototype.updateModel = function() { return true; };
nglr.NullController.prototype.updateView = function() { };
nglr.NullController.instance = new nglr.NullController();
///////////////////////
// ButtonController
///////////////////////
nglr.ButtonController = function(view) {this.view = view;};
nglr.ButtonController.prototype.updateModel = function(scope) { return true; };
nglr.ButtonController.prototype.updateView = function(scope) {};
///////////////////////
// TextController
///////////////////////
nglr.TextController = function(view, exp) {
this.view = view;
this.exp = exp;
this.validator = view.getAttribute('ng-validate');
this.required = typeof view.attributes['ng-required'] != "undefined";
this.lastErrorText = null;
this.lastValue = undefined;
this.initialValue = view.value;
var widget = view.getAttribute('ng-widget');
if (widget === 'datepicker') {
jQuery(view).datepicker();
}
};
nglr.TextController.prototype.updateModel = function(scope) {
var value = this.view.value;
if (this.lastValue === value) {
return false;
} else {
scope.set(this.exp, value);
this.lastValue = value;
return true;
}
};
nglr.TextController.prototype.updateView = function(scope) {
var view = this.view;
var value = scope.get(this.exp);
if (typeof value === "undefined") {
value = this.initialValue;
scope.set(this.exp, value);
}
value = value ? value : '';
if (this.lastValue != value) {
view.value = value;
this.lastValue = value;
}
var isValidationError = false;
view.removeAttribute('ng-error');
if (this.required) {
isValidationError = !(value && value.length > 0);
}
var errorText = isValidationError ? "Required Value" : null;
if (!isValidationError && this.validator && value) {
errorText = scope.validate(this.validator, value);
isValidationError = !!errorText;
}
if (this.lastErrorText !== errorText) {
this.lastErrorText = isValidationError;
if (errorText !== null) {
view.setAttribute('ng-error', errorText);
scope.markInvalid(this);
}
jQuery(view).toggleClass('ng-validation-error', isValidationError);
}
};
///////////////////////
// CheckboxController
///////////////////////
nglr.CheckboxController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastValue = undefined;
this.initialValue = view.checked ? view.value : "";
};
nglr.CheckboxController.prototype.updateModel = function(scope) {
var input = this.view;
var value = input.checked ? input.value : '';
if (this.lastValue === value) {
return false;
} else {
scope.setEval(this.exp, value);
this.lastValue = value;
return true;
}
};
nglr.CheckboxController.prototype.updateView = function(scope) {
var input = this.view;
var value = scope.eval(this.exp);
if (typeof value === "undefined") {
value = this.initialValue;
scope.setEval(this.exp, value);
}
input.checked = input.value == (''+value);
};
///////////////////////
// SelectController
///////////////////////
nglr.SelectController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastValue = undefined;
this.initialValue = view.value;
};
nglr.SelectController.prototype.updateModel = function(scope) {
var input = this.view;
if (input.selectedIndex < 0) {
scope.set(this.exp, null);
} else {
var value = this.view.value;
if (this.lastValue === value) {
return false;
} else {
scope.set(this.exp, value);
this.lastValue = value;
return true;
}
}
};
nglr.SelectController.prototype.updateView = function(scope) {
var input = this.view;
var value = scope.get(this.exp);
if (typeof value === 'undefined') {
value = this.initialValue;
scope.set(this.exp, value);
}
if (value !== this.lastValue) {
input.value = value ? value : "";
this.lastValue = value;
}
};
///////////////////////
// MultiSelectController
///////////////////////
nglr.MultiSelectController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastValue = undefined;
this.initialValue = this.selected();
};
nglr.MultiSelectController.prototype.selected = function () {
var value = [];
var options = this.view.options;
for ( var i = 0; i < options.length; i++) {
var option = options[i];
if (option.selected) {
value.push(option.value);
}
}
return value;
};
nglr.MultiSelectController.prototype.updateModel = function(scope) {
var value = this.selected();
// TODO: This is wrong! no caching going on here as we are always comparing arrays
if (this.lastValue === value) {
return false;
} else {
scope.set(this.exp, value);
this.lastValue = value;
return true;
}
};
nglr.MultiSelectController.prototype.updateView = function(scope) {
var input = this.view;
var selected = scope.get(this.exp);
if (typeof selected === "undefined") {
selected = this.initialValue;
scope.set(this.exp, selected);
}
if (selected !== this.lastValue) {
var options = input.options;
for ( var i = 0; i < options.length; i++) {
var option = options[i];
option.selected = selected.contains(option.value);
}
this.lastValue = selected;
}
};
///////////////////////
// RadioController
///////////////////////
nglr.RadioController = function(view, exp) {
this.view = view;
this.exp = exp;
this.lastChecked = undefined;
this.lastValue = undefined;
this.inputValue = view.value;
this.initialValue = view.checked ? view.value : null;
};
nglr.RadioController.prototype.updateModel = function(scope) {
var input = this.view;
if (this.lastChecked) {
return false;
} else {
input.checked = true;
this.lastValue = scope.set(this.exp, this.inputValue);
this.lastChecked = true;
return true;
}
};
nglr.RadioController.prototype.updateView = function(scope) {
var input = this.view;
var value = scope.get(this.exp);
if (this.initialValue && typeof value === "undefined") {
value = this.initialValue;
scope.set(this.exp, value);
}
if (this.lastValue != value) {
this.lastChecked = input.checked = this.inputValue == (''+value);
this.lastValue = value;
}
};
///////////////////////
//ElementController
///////////////////////
nglr.BindUpdater = function(view, exp) {
this.view = view;
this.exp = exp.parseBindings();
this.hasError = false;
this.scopeSelf = {element:view};
};
nglr.BindUpdater.toText = function(obj) {
var e = nglr.escapeHtml;
switch(typeof obj) {
case "string":
case "boolean":
case "number":
return e(obj);
case "function":
return nglr.BindUpdater.toText(obj());
case "object":
if (nglr.isNode(obj)) {
return nglr.outerHTML(obj);
} else if (obj && obj.TAG === filters.Meta.TAG) {
switch(typeof obj.html) {
case "string":
case "number":
return obj.html;
case "function":
return obj.html();
default:
break;
}
switch(typeof obj.text) {
case "string":
case "number":
return e(obj.text);
case "function":
return e(obj.text());
default:
break;
}
}
if (obj === null)
return "";
return e(nglr.toJson(obj, true));
default:
return "";
}
};
nglr.BindUpdater.prototype.updateModel = function(scope) {};
nglr.BindUpdater.prototype.updateView = function(scope) {
var html = [];
var parts = this.exp;
var length = parts.length;
for(var i=0; i<length; i++) {
var part = parts[i];
var binding = part.binding();
if (binding) {
scope.evalWidget(this, binding, this.scopeSelf, function(value){
html.push(nglr.BindUpdater.toText(value));
}, function(e, text){
nglr.setHtml(this.view, text);
});
if (this.hasError) {
return;
}
} else {
html.push(nglr.escapeHtml(part));
}
}
nglr.setHtml(this.view, html.join(''));
};
nglr.BindAttrUpdater = function(view, attrs) {
this.view = view;
this.attrs = attrs;
};
nglr.BindAttrUpdater.prototype.updateModel = function(scope) {};
nglr.BindAttrUpdater.prototype.updateView = function(scope) {
var jNode = jQuery(this.view);
var attributeTemplates = this.attrs;
if (this.hasError) {
this.hasError = false;
jNode.
removeClass('ng-exception').
removeAttr('ng-error');
}
var isImage = jNode.is('img');
for (var attrName in attributeTemplates) {
var attributeTemplate = attributeTemplates[attrName].parseBindings();
var attrValues = [];
for ( var i = 0; i < attributeTemplate.length; i++) {
var binding = attributeTemplate[i].binding();
if (binding) {
try {
var value = scope.eval(binding, {element:jNode[0], attrName:attrName});
if (value && (value.constructor !== nglr.array || value.length !== 0))
attrValues.push(value);
} catch (e) {
this.hasError = true;
console.error('BindAttrUpdater', e);
var jsonError = nglr.toJson(e, true);
attrValues.push('[' + jsonError + ']');
jNode.
addClass('ng-exception').
attr('ng-error', jsonError);
}
} else {
attrValues.push(attributeTemplate[i]);
}
}
var attrValue = attrValues.length ? attrValues.join('') : null;
if(isImage && attrName == 'src' && !attrValue)
attrValue = scope.get('config.server') + '/images/blank.gif';
jNode.attr(attrName, attrValue);
}
};
nglr.EvalUpdater = function(view, exp) {
this.view = view;
this.exp = exp;
this.hasError = false;
};
nglr.EvalUpdater.prototype.updateModel = function(scope) {};
nglr.EvalUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp);
};
nglr.HideUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.HideUpdater.prototype.updateModel = function(scope) {};
nglr.HideUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(hideValue){
var view = jQuery(this.view);
if (nglr.toBoolean(hideValue)) {
view.hide();
} else {
view.show();
}
});
};
nglr.ShowUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ShowUpdater.prototype.updateModel = function(scope) {};
nglr.ShowUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(hideValue){
var view = jQuery(this.view);
if (nglr.toBoolean(hideValue)) {
view.show();
} else {
view.hide();
}
});
};
nglr.ClassUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ClassUpdater.prototype.updateModel = function(scope) {};
nglr.ClassUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(classValue){
if (classValue !== null && classValue !== undefined) {
this.view.className = classValue;
}
});
};
nglr.ClassEvenUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ClassEvenUpdater.prototype.updateModel = function(scope) {};
nglr.ClassEvenUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(classValue){
var index = scope.get('$index');
jQuery(this.view).toggleClass(classValue, index % 2 === 1);
});
};
nglr.ClassOddUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.ClassOddUpdater.prototype.updateModel = function(scope) {};
nglr.ClassOddUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(classValue){
var index = scope.get('$index');
jQuery(this.view).toggleClass(classValue, index % 2 === 0);
});
};
nglr.StyleUpdater = function(view, exp) { this.view = view; this.exp = exp; };
nglr.StyleUpdater.prototype.updateModel = function(scope) {};
nglr.StyleUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.exp, {}, function(styleValue){
jQuery(this.view).attr('style', "").css(styleValue);
});
};
///////////////////////
// RepeaterUpdater
///////////////////////
nglr.RepeaterUpdater = function(view, repeaterExpression, template, prefix) {
this.view = view;
this.template = template;
this.prefix = prefix;
this.children = [];
var match = repeaterExpression.match(/^\s*(.+)\s+in\s+(.*)\s*$/);
if (! match) {
throw "Expected ng-repeat in form of 'item in collection' but got '" + repeaterExpression + "'.";
}
this.itemExp = match[1];
this.iteratorExp = match[2];
};
nglr.RepeaterUpdater.prototype.updateModel = function(scope) {};
nglr.RepeaterUpdater.prototype.updateView = function(scope) {
scope.evalWidget(this, this.iteratorExp, {}, function(iterator){
if (!iterator) {
iterator = [];
if (scope.isProperty(this.iteratorExp)) {
scope.set(this.iteratorExp, iterator);
}
}
var iteratorLength = iterator.length;
var childrenLength = this.children.length;
var cursor = this.view;
var time = 0;
var child = null;
var itemExp = this.itemExp;
for ( var i = 0; i < iteratorLength; i++) {
if (i < iteratorLength) {
if (i < childrenLength) { // reuse children
child = this.children[i];
child.scope.set(itemExp, iterator[i]);
} else { // grow children
var name = this.prefix +
itemExp + " in " + this.iteratorExp + "[" + i + "]";
var childScope = new nglr.Scope(scope.state, name);
childScope.set('$index', i);
childScope.set(itemExp, iterator[i]);
child = { scope:childScope, element:this.template(childScope, this.prefix, i) };
cursor.after(child.element);
this.children.push(child);
}
cursor = child.element;
var s = new Date().getTime();
child.scope.updateView();
time += new Date().getTime() - s;
}
}
// shrink children
for ( var r = childrenLength; r > iteratorLength; --r) {
var unneeded = this.children.pop();
unneeded.element.removeNode();
}
// Special case for option in select
if (child && child.element[0].nodeName === "OPTION") {
var select = jQuery(child.element[0].parentNode);
var cntl = select.data('controller');
if (cntl) {
cntl.lastValue = undefined;
cntl.updateView(scope);
}
}
});
};
//////////////////////////////////
// PopUp
//////////////////////////////////
nglr.PopUp = function(doc) {
this.doc = doc;
};
nglr.PopUp.OUT_EVENT = "mouseleave mouseout click dblclick keypress keyup";
nglr.PopUp.prototype.bind = function () {
var self = this;
this.doc.find('.ng-validation-error,.ng-exception').
live("mouseover", nglr.PopUp.onOver);
};
nglr.PopUp.onOver = function(e) {
nglr.PopUp.onOut();
var jNode = jQuery(this);
jNode.bind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut);
var position = jNode.position();
var de = document.documentElement;
var w = self.innerWidth || (de&&de.clientWidth) || document.body.clientWidth;
var hasArea = w - position.left;
var width = 300;
var title = jNode.hasClass("ng-exception") ? "EXCEPTION:" : "Validation error...";
var msg = jNode.attr("ng-error");
var x;
var arrowPos = hasArea>(width+75) ? "left" : "right";
var tip = jQuery(
"<div id='ng-callout' style='width:"+width+"px'>" +
"<div class='ng-arrow-"+arrowPos+"'/>" +
"<div class='ng-title'>"+title+"</div>" +
"<div class='ng-content'>"+msg+"</div>" +
"</div>");
jQuery("body").append(tip);
if(arrowPos === 'left'){
x = position.left + this.offsetWidth + 11;
}else{
x = position.left - (width + 15);
tip.find('.ng-arrow-right').css({left:width+1});
}
tip.css({left: x+"px", top: (position.top - 3)+"px"});
return true;
};
nglr.PopUp.onOut = function() {
jQuery('#ng-callout').
unbind(nglr.PopUp.OUT_EVENT, nglr.PopUp.onOut).
remove();
return true;
};
//////////////////////////////////
// Status
//////////////////////////////////
nglr.Status = function (body) {
this.body = body;
this.requestCount = 0;
};
nglr.Status.ANGULAR = "&lt;a class='ng-angular-logo' href='http://www.getangular.com'&gt;&amp;lt;angular/&amp;gt;&lt;/a&gt;&trade;";
nglr.Status.prototype.beginRequest = function () {
if (this.requestCount === 0) {
<<<<<<< HEAD:public/javascripts/nglr/Widgets.js
this.dialogView = jQuery('<div class="ng-dialog" title="'+nglr.ControlBar.ANGULAR+' Server Communication:">Please Wait...<div/><div class="loader"></div></div>');
=======
this.dialogView = jQuery('<div title="'+nglr.Status.ANGULAR+' Server Communication:">Please Wait...<div/></div>');
this.progressWidget = this.dialogView.find("div");
this.progressWidget.progressbar({value:0});
>>>>>>> master:public/javascripts/nglr/Widgets.js
this.dialogView.dialog({bgiframe:true, minHeight:50, modal:true});
this.maxRequestCount = 0;
}
this.requestCount++;
this.maxRequestCount++;
};
nglr.Status.prototype.endRequest = function () {
this.requestCount--;
if (this.requestCount === 0) {
this.dialogView.dialog("destroy");
this.dialogView.remove();
this.dialogView = null;
}
};

100
src/XSitePost.js Normal file
View file

@ -0,0 +1,100 @@
// Copyright (C) 2008,2009 BRAT Tech LLC
if (typeof nglr == 'undefined') nglr = {};
if (typeof console == 'undefined') console = {};
if (typeof console.log == 'undefined')
console.log = function() {};
if (typeof console.error == 'undefined')
console.error = function() {};
nglr.XSitePost = function(baseUrl, window, prefix) {
this.baseUrl = baseUrl;
this.post = jQuery.post;
this.window = window;
this.inQueue = {};
this.outQueue = [];
this.maxMsgSize = 100000;
this.delay = 20;
this.prefix = prefix;
this.setTimeout=function(fn, delay){window.setTimeout(fn, delay);};
};
nglr.XSitePost.prototype.init = function() {
this.window.name = '';
this.response('ready', 'null');
};
nglr.XSitePost.prototype.incomingFragment = function(fragment) {
var parts = fragment.split(";");
this.incomingMsg(parts.shift(), 1*parts.shift(), 1*parts.shift(), parts.shift());
};
nglr.XSitePost.prototype.incomingMsg = function(id, partNo, totalParts, msgPart) {
var msg = this.inQueue[id];
if (!msg) {
msg = {id:id, parts:[], count:0};
this.inQueue[id] = msg;
}
msg.parts[partNo] = msgPart;
msg.count++;
if (totalParts === msg.count) {
delete this.inQueue[id];
var request = this.decodePost(msg.parts.join(''));
var self = this;
this.post(this.baseUrl + request.url, request.params, function(response, status){
self.response(id, response, status);
});
}
};
nglr.XSitePost.prototype.response = function(id, response, status) {
var start = 0;
var end;
var msg = Base64.encode(response);
var msgLen = msg.length;
var total = Math.ceil(msgLen / this.maxMsgSize);
var part = 0;
while (start < msgLen) {
end = Math.min(msgLen, start + this.maxMsgSize);
this.outQueue.push(id + ':'+part+':'+total+':' + msg.substring(start, end));
start = end;
part++;
}
};
nglr.XSitePost.prototype.decodePost = function(post) {
var parts = post.split(':');
var url = Base64.decode(parts.shift());
var params = {};
while(parts.length !== 0) {
var key = parts.shift();
var value = Base64.decode(parts.shift());
params[key] = value;
}
return {url:url, params:params};
};
nglr.XSitePost.prototype.listen = function() {
console.log("listen()");
var self = this;
var window = this.window;
var outQueue = this.outQueue;
var setTimeout = this.setTimeout;
var prefix = this.prefix;
var prefixLen = prefix.length;
var prefixRec = prefix + '>';
var prefixRecLen = prefixRec.length;
window.name = prefix;
var pull = function(){
var value = window.name;
if (value == prefix && outQueue.length > 0) {
window.name = prefix + '<' + outQueue.shift();
} else if (value.substr(0, prefixRecLen) == prefixRec) {
self.incomingFragment(value.substr(prefixRecLen));
window.name = prefix;
}
setTimeout(pull, self.delay);
};
pull();
};

100
src/angular-bootstrap.js vendored Normal file
View file

@ -0,0 +1,100 @@
// Copyright (C) 2008,2009 BRAT Tech LLC
(function(previousOnLoad){
var filename = /(.*)\/angular-(.*).js(#(.*))?/;
var scripts = document.getElementsByTagName("script");
var scriptConfig = {
autoSubmit:true,
autoBind:true,
autoLoadDependencies:false
};
for(var j = 0; j < scripts.length; j++) {
var src = scripts[j].src;
if (src && src.match(filename)) {
var parts = src.match(filename);
if (parts[2] == 'bootstrap') {
scriptConfig.autoLoadDependencies = true;
}
scriptConfig.server = parts[1] || '';
if (!scriptConfig.server) {
scriptConfig.server = window.location.toString().split(window.location.pathname)[0];
}
if (parts[4]) {
var directive = parts[4].split('&');
for ( var i = 0; i < directive.length; i++) {
var keyValue = directive[i].split('=');
var key = keyValue[0];
var value = keyValue.length == 1 ? true : keyValue[1];
if (value == 'false') value = false;
if (value == 'true') value = true;
scriptConfig[key] = value;
}
}
}
}
var addScript = function(path, server){
server = server || scriptConfig.server;
document.write('<script type="text/javascript" src="' + server + path +'"></script>');
};
if (scriptConfig.autoLoadDependencies) {
addScript("/javascripts/webtoolkit.base64.js");
addScript("/javascripts/swfobject.js");
addScript("/javascripts/jQuery/jquery-1.3.2.js");
addScript("/javascripts/jQuery/jquery-ui-1.7.1.custom.min.js");
addScript("/javascripts/underscore/underscore.js");
addScript("/javascripts/nglr/Loader.js");
addScript("/javascripts/nglr/API.js");
addScript("/javascripts/nglr/Binder.js");
addScript("/javascripts/nglr/ControlBar.js");
addScript("/javascripts/nglr/DataStore.js");
addScript("/javascripts/nglr/Filters.js");
addScript("/javascripts/nglr/JSON.js");
addScript("/javascripts/nglr/Model.js");
addScript("/javascripts/nglr/Parser.js");
addScript("/javascripts/nglr/Scope.js");
addScript("/javascripts/nglr/Server.js");
addScript("/javascripts/nglr/Users.js");
addScript("/javascripts/nglr/Validators.js");
addScript("/javascripts/nglr/Widgets.js");
} else {
addScript("/ajax/libs/swfobject/2.2/swfobject.js", "http://ajax.googleapis.com");
addScript("/ajax/libs/jquery/1.3.2/jquery.min.js", "http://ajax.googleapis.com");
addScript("/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js", "http://ajax.googleapis.com");
}
window.onload = function() {
window.angular.init = function(root, config){
var cnfgMerged = _.clone(scriptConfig||{});
_.extend(cnfgMerged, config);
new nglr.Loader(root, jQuery("head"), cnfgMerged).load();
};
var doc = window.document;
if (scriptConfig.bindRootId) {
doc = null;
var ids = scriptConfig.bindRootId.split('|');
for ( var i = 0; i < ids.length && !doc; i++) {
var idCond = ids[i].split('?');
var id = idCond[0];
if (idCond.length > 1) {
if (!window.document.getElementById(idCond[1])) {
continue;
}
}
doc = window.document.getElementById(id);
}
}
if (scriptConfig.autoBind && doc) {
window.angular.init(doc);
}
if (typeof previousOnLoad === 'function') {
try {
previousOnLoad.apply(this, arguments);
} catch (e) {}
}
};
})(window.onload);

160
src/test/Runner.js Normal file
View file

@ -0,0 +1,160 @@
nglr.test.ScenarioRunner = function(scenarios, body) {
this.scenarios = scenarios;
this.body = body;
};
nglr.test.ScenarioRunner.prototype = {
run:function(){
this.setUpUI();
this.runScenarios();
},
setUpUI:function(){
this.body.html(
'<div id="runner">' +
'<div class="console"></div>' +
'</div>' +
'<div id="testView">' +
'<iframe></iframe>' +
'</div>');
this.console = this.body.find(".console");
this.testFrame = this.body.find("iframe");
this.console.find(".run").live("click", function(){
jQuery(this).parent().find('.log').toggle();
});
},
runScenarios:function(){
var runner = new nglr.test.Runner(this.console, this.testFrame);
_.stepper(this.scenarios, function(next, scenario, name){
new nglr.test.Scenario(name, scenario).run(runner, next);
}, function(){
}
);
}
};
nglr.test.Runner = function(console, frame){
this.console = console;
this.current = null;
this.tests = [];
this.frame = frame;
};
nglr.test.Runner.prototype = {
start:function(name){
var current = this.current = {
name:name,
start:new Date().getTime(),
scenario:jQuery('<div class="scenario"></div>')
};
current.run = current.scenario.append(
'<div class="run">' +
'<span class="name">.</span>' +
'<span class="time">.</span>' +
'<span class="state">.</span>' +
'</run>').find(".run");
current.log = current.scenario.append('<div class="log"></div>').find(".log");
current.run.find(".name").text(name);
this.tests.push(current);
this.console.append(current.scenario);
},
end:function(name){
var current = this.current;
var run = current.run;
this.current = null;
current.end = new Date().getTime();
current.time = current.end - current.start;
run.find(".time").text(current.time);
run.find(".state").text(current.error ? "FAIL" : "PASS");
run.addClass(current.error ? "fail" : "pass");
if (current.error)
run.find(".run").append('<span div="error"></span>').text(current.error);
current.scenario.find(".log").hide();
},
log:function(level) {
var buf = [];
for ( var i = 1; i < arguments.length; i++) {
var arg = arguments[i];
buf.push(typeof arg == "string" ?arg:nglr.toJson(arg));
}
var log = jQuery('<div class="' + level + '"></div>');
log.text(buf.join(" "));
this.current.log.append(log);
this.console.scrollTop(this.console[0].scrollHeight);
if (level == "error")
this.current.error = buf.join(" ");
}
};
nglr.test.Scenario = function(name, scenario){
this.name = name;
this.scenario = scenario;
};
nglr.test.Scenario.prototype = {
run:function(runner, callback) {
var self = this;
_.stepper(this.scenario, function(next, steps, name){
if (name.charAt(0) == '$') {
next();
} else {
runner.start(self.name + "::" + name);
var allSteps = (self.scenario.$before||[]).concat(steps);
_.stepper(allSteps, function(next, step){
self.executeStep(runner, step, next);
}, function(){
runner.end();
next();
});
}
}, callback);
},
verb:function(step){
var fn = null;
if (!step) fn = function (){ throw "Step is null!"; }
else if (step.Given) fn = angular.test.GIVEN[step.Given];
else if (step.When) fn = angular.test.WHEN[step.When];
else if (step.Then) fn = angular.test.THEN[step.Then];
return fn || function (){
throw "ERROR: Need Given/When/Then got: " + nglr.toJson(step);
};
},
context: function(runner) {
var frame = runner.frame;
var window = frame[0].contentWindow;
var document;
if (window.jQuery)
document = window.jQuery(window.document);
var context = {
frame:frame,
window:window,
log:_.bind(runner.log, runner, "info"),
document:document,
assert:function(element, path){
if (element.size() != 1) {
throw "Expected to find '1' found '"+
element.size()+"' for '"+path+"'.";
}
return element;
},
element:function(path){
var exp = path.replace("{{","[ng-bind=").replace("}}", "]");
var element = document.find(exp);
return context.assert(element, path);
}
};
return context;
},
executeStep:function(runner, step, callback) {
if (!step) {
callback();
return;
}
runner.log("info", nglr.toJson(step));
var fn = this.verb(step);
var context = this.context(runner);
_.extend(context, step);
try {
(fn.call(context)||function(c){c();})(callback);
} catch (e) {
runner.log("error", "ERROR: " + nglr.toJson(e));
}
}
};

57
src/test/Steps.js Normal file
View file

@ -0,0 +1,57 @@
angular.test.GIVEN = {
browser:function(){
var self = this;
if (jQuery.browser.safari && this.frame.attr('src') == this.at) {
this.window.location.reload();
} else {
this.frame.attr('src', this.at);
}
return function(done){
self.frame.load(function(){
self.frame.unbind();
done();
});
};
},
dataset:function(){
this.frame.name="$DATASET:" + nglr.toJson({dataset:this.dataset});
}
};
angular.test.WHEN = {
enter:function(){
var element = this.element(this.at);
element.attr('value', this.text);
element.change();
},
click:function(){
var element = this.element(this.at);
var input = element[0];
// emulate the browser behavior which causes it
// to be overridden at the end.
var checked = input.checked = !input.checked;
element.click();
input.checked = checked;
},
select:function(){
var element = this.element(this.at);
var path = "option[value=" + this.option + "]";
var option = this.assert(element.find(path));
option[0].selected = !option[0].selected;
element.change();
}
};
angular.test.THEN = {
text:function(){
var element = this.element(this.at);
if (typeof this.should_be != undefined ) {
var should_be = this.should_be;
if (_.isArray(this.should_be))
should_be = JSON.stringify(should_be);
if (element.text() != should_be)
throw "Expected " + should_be +
" but was " + element.text() + ".";
}
},
drainRequestQueue:function(){
}
};

5
src/test/_namespace.js Normal file
View file

@ -0,0 +1,5 @@
if (!angular) angular = {};
if (!angular.test) angular.test = {};
if (!angular.test.GIVEN) angular.test.GIVEN = {};
if (!angular.test.WHEN) angular.test.WHEN = {};
if (!angular.test.THEN) angular.test.THEN = {};

252
test/ApiTest.js Normal file
View file

@ -0,0 +1,252 @@
ApiTest = TestCase("ApiTest");
ApiTest.prototype.testItShouldReturnTypeOf = function (){
assertEquals("undefined", angular.Object.typeOf(undefined));
assertEquals("null", angular.Object.typeOf(null));
assertEquals("object", angular.Collection.typeOf({}));
assertEquals("array", angular.Array.typeOf([]));
assertEquals("string", angular.Object.typeOf(""));
assertEquals("date", angular.Object.typeOf(new Date()));
assertEquals("element", angular.Object.typeOf(document.body));
assertEquals("function", angular.Object.typeOf(function(){}));
};
ApiTest.prototype.testItShouldReturnSize = function(){
assertEquals(0, angular.Collection.size({}));
assertEquals(1, angular.Collection.size({a:"b"}));
assertEquals(0, angular.Object.size({}));
assertEquals(1, angular.Array.size([0]));
};
ApiTest.prototype.testIncludeIf = function() {
var array = [];
var obj = {};
angular.Array.includeIf(array, obj, true);
angular.Array.includeIf(array, obj, true);
assertTrue(_.include(array, obj));
assertEquals(1, array.length);
angular.Array.includeIf(array, obj, false);
assertFalse(_.include(array, obj));
assertEquals(0, array.length);
angular.Array.includeIf(array, obj, 'x');
assertTrue(_.include(array, obj));
assertEquals(1, array.length);
angular.Array.includeIf(array, obj, '');
assertFalse(_.include(array, obj));
assertEquals(0, array.length);
};
ApiTest.prototype.testSum = function(){
assertEquals(3, angular.Array.sum([{a:"1"}, {a:"2"}], 'a'));
};
ApiTest.prototype.testSumContainingNaN = function(){
assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], 'a'));
assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], function($){return $.a;}));
};
ApiTest.prototype.testInclude = function(){
assertTrue(angular.Array.include(['a'], 'a'));
assertTrue(angular.Array.include(['a', 'b'], 'a'));
assertTrue(!angular.Array.include(['c'], 'a'));
assertTrue(!angular.Array.include(['c', 'b'], 'a'));
};
ApiTest.prototype.testIndex = function(){
assertEquals(angular.Array.indexOf(['a'], 'a'), 0);
assertEquals(angular.Array.indexOf(['a', 'b'], 'a'), 0);
assertEquals(angular.Array.indexOf(['b', 'a'], 'a'), 1);
assertEquals(angular.Array.indexOf(['b', 'b'],'x'), -1);
};
ApiTest.prototype.testRemove = function(){
var items = ['a', 'b', 'c'];
assertEquals(angular.Array.remove(items, 'q'), 'q');
assertEquals(items.length, 3);
assertEquals(angular.Array.remove(items, 'b'), 'b');
assertEquals(items.length, 2);
assertEquals(angular.Array.remove(items, 'a'), 'a');
assertEquals(items.length, 1);
assertEquals(angular.Array.remove(items, 'c'), 'c');
assertEquals(items.length, 0);
assertEquals(angular.Array.remove(items, 'q'), 'q');
assertEquals(items.length, 0);
};
ApiTest.prototype.testFindById = function() {
var items = [{$id:1}, {$id:2}, {$id:3}];
assertNull(angular.Array.findById(items, 0));
assertEquals(items[0], angular.Array.findById(items, 1));
assertEquals(items[1], angular.Array.findById(items, 2));
assertEquals(items[2], angular.Array.findById(items, 3));
};
ApiTest.prototype.testFilter = function() {
var items = ["MIsKO", {name:"shyam"}, ["adam"], 1234];
assertEquals(4, angular.Array.filter(items, "").length);
assertEquals(4, angular.Array.filter(items, undefined).length);
assertEquals(1, angular.Array.filter(items, 'iSk').length);
assertEquals("MIsKO", angular.Array.filter(items, 'isk')[0]);
assertEquals(1, angular.Array.filter(items, 'yam').length);
assertEquals(items[1], angular.Array.filter(items, 'yam')[0]);
assertEquals(1, angular.Array.filter(items, 'da').length);
assertEquals(items[2], angular.Array.filter(items, 'da')[0]);
assertEquals(1, angular.Array.filter(items, '34').length);
assertEquals(1234, angular.Array.filter(items, '34')[0]);
assertEquals(0, angular.Array.filter(items, "I don't exist").length);
};
ApiTest.prototype.testShouldNotFilterOnSystemData = function() {
assertEquals("", "".charAt(0)); // assumption
var items = [{$name:"misko"}];
assertEquals(0, angular.Array.filter(items, "misko").length);
};
ApiTest.prototype.testFilterOnSpecificProperty = function() {
var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}];
assertEquals(2, angular.Array.filter(items, {}).length);
assertEquals(2, angular.Array.filter(items, {name:'a'}).length);
assertEquals(1, angular.Array.filter(items, {name:'b'}).length);
assertEquals("abc", angular.Array.filter(items, {name:'b'})[0].name);
};
ApiTest.prototype.testFilterOnFunction = function() {
var items = [{name:"a"}, {name:"abc", done:true}];
assertEquals(1, angular.Array.filter(items, function(i){return i.done;}).length);
};
ApiTest.prototype.testFilterIsAndFunction = function() {
var items = [{first:"misko", last:"hevery"},
{first:"adam", last:"abrons"}];
assertEquals(2, angular.Array.filter(items, {first:'', last:''}).length);
assertEquals(1, angular.Array.filter(items, {first:'', last:'hevery'}).length);
assertEquals(0, angular.Array.filter(items, {first:'adam', last:'hevery'}).length);
assertEquals(1, angular.Array.filter(items, {first:'misko', last:'hevery'}).length);
assertEquals(items[0], angular.Array.filter(items, {first:'misko', last:'hevery'})[0]);
};
ApiTest.prototype.testFilterNot = function() {
var items = ["misko", "adam"];
assertEquals(1, angular.Array.filter(items, '!isk').length);
assertEquals(items[1], angular.Array.filter(items, '!isk')[0]);
};
ApiTest.prototype.testAdd = function() {
var add = angular.Array.add;
assertJsonEquals([{}, "a"], add(add([]),"a"));
};
ApiTest.prototype.testCount = function() {
var array = [{name:'a'},{name:'b'},{name:''}];
var obj = {};
assertEquals(3, angular.Array.count(array));
assertEquals(2, angular.Array.count(array, 'name'));
assertEquals(1, angular.Array.count(array, 'name=="a"'));
};
ApiTest.prototype.testFind = function() {
var array = [{name:'a'},{name:'b'},{name:''}];
var obj = {};
assertEquals(undefined, angular.Array.find(array, 'false'));
assertEquals('default', angular.Array.find(array, 'false', 'default'));
assertEquals('a', angular.Array.find(array, 'name == "a"').name);
assertEquals('', angular.Array.find(array, 'name == ""').name);
};
ApiTest.prototype.testItShouldSortArray = function() {
assertEquals([2,15], angular.Array.orderBy([15,2]));
assertEquals(["a","B", "c"], angular.Array.orderBy(["c","B", "a"]));
assertEquals([15,"2"], angular.Array.orderBy([15,"2"]));
assertEquals(["15","2"], angular.Array.orderBy(["15","2"]));
assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a'));
assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a', "F"));
};
ApiTest.prototype.testItShouldSortArrayInReverse = function() {
assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', true));
assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "T"));
assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "reverse"));
};
ApiTest.prototype.testItShouldSortArrayByPredicate = function() {
assertJsonEquals([{a:2, b:1},{a:15, b:1}],
angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['a', 'b']));
assertJsonEquals([{a:2, b:1},{a:15, b:1}],
angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['b', 'a']));
assertJsonEquals([{a:15, b:1},{a:2, b:1}],
angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['+b', '-a']));
};
ApiTest.prototype.testQuoteString = function(){
assertEquals(angular.String.quote('a'), '"a"');
assertEquals(angular.String.quote('\\'), '"\\\\"');
assertEquals(angular.String.quote("'a'"), '"\'a\'"');
assertEquals(angular.String.quote('"a"'), '"\\"a\\""');
assertEquals(angular.String.quote('\n\f\r\t'), '"\\n\\f\\r\\t"');
};
ApiTest.prototype.testQuoteStringBug = function(){
assertEquals(angular.String.quote('"7\\\\\\\"7"', "7\\\"7"));
};
ApiTest.prototype.testQuoteUnicode = function(){
assertEquals('"abc\\u00a0def"', angular.String.quoteUnicode('abc\u00A0def'));
};
ApiTest.prototype.testMerge = function() {
var array = [{name:"misko"}];
angular.Array.merge(array, 0, {name:"", email:"email1"});
angular.Array.merge(array, 1, {name:"adam", email:"email2"});
assertJsonEquals([{"email":"email1","name":"misko"},{"email":"email2","name":"adam"}], array);
};
ApiTest.prototype.testOrderByToggle = function() {
var orderByToggle = angular.Array.orderByToggle;
var predicate = [];
assertEquals(['+a'], orderByToggle(predicate, 'a'));
assertEquals(['-a'], orderByToggle(predicate, 'a'));
assertEquals(['-a', '-b'], orderByToggle(['-b', 'a'], 'a'));
};
ApiTest.prototype.testOrderByToggle = function() {
var orderByDirection = angular.Array.orderByDirection;
assertEquals("", orderByDirection(['+a','b'], 'x'));
assertEquals("", orderByDirection(['+a','b'], 'b'));
assertEquals('ng-ascend', orderByDirection(['a','b'], 'a'));
assertEquals('ng-ascend', orderByDirection(['+a','b'], 'a'));
assertEquals('ng-descend', orderByDirection(['-a','b'], 'a'));
assertEquals('up', orderByDirection(['+a','b'], 'a', 'up', 'down'));
assertEquals('down', orderByDirection(['-a','b'], 'a', 'up', 'down'));
};
ApiTest.prototype.testDateToUTC = function(){
var date = new Date("Sep 10 2003 13:02:03 GMT");
assertEquals("date", angular.Object.typeOf(date));
assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date));
};
ApiTest.prototype.testStringFromUTC = function(){
var date = angular.String.toDate("2003-09-10T13:02:03Z");
assertEquals("date", angular.Object.typeOf(date));
assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date));
assertEquals("str", angular.String.toDate("str"));
};

5
test/Base64Test.js Normal file
View file

@ -0,0 +1,5 @@
Base64Test = TestCase('Base64Test');
Base64Test.prototype.testEncodeDecode = function(){
assertEquals(Base64.decode(Base64.encode('hello')), 'hello');
};

1001
test/BinderTest.js Normal file

File diff suppressed because it is too large Load diff

13
test/ConsoleTest.js Normal file
View file

@ -0,0 +1,13 @@
ConsoleTest = TestCase('ConsoleTest');
ConsoleTest.prototype.testConsoleWrite = function(){
var consoleNode = $("<div></div>")[0];
nglr.consoleNode = consoleNode;
nglr.consoleLog("error", ["Hello", "world"]);
assertEquals($(consoleNode)[0].nodeName, 'DIV');
assertEquals($(consoleNode).text(), 'Hello world');
assertEquals($('div', consoleNode)[0].className, 'error');
nglr.consoleLog("error",["Bye"]);
assertEquals($(consoleNode).text(), 'Hello worldBye');
nglr.consoleNode = null;
};

2
test/ControlBarTest.js Normal file
View file

@ -0,0 +1,2 @@
ControlBarTest = TestCase("ControlBarTest");

617
test/DataStoreTest.js Normal file
View file

@ -0,0 +1,617 @@
DataStoreTest = TestCase('DataStoreTest');
DataStoreTest.prototype.testSavePostsToServer = function(){
expectAsserts(10);
var response;
var post = function(data, callback){
var method = data[0][0];
var posted = data[0][2];
assertEquals("POST", method);
assertEquals("abc", posted.$entity);
assertEquals("123", posted.$id);
assertEquals("1", posted.$version);
assertFalse('function' == typeof posted.save);
response = nglr.fromJson(nglr.toJson(posted));
response.$entity = "abc";
response.$id = "123";
response.$version = "2";
callback(200, [response]);
};
var model;
var datastore = new nglr.DataStore(post);
model = datastore.entity('abc', {name: "value"})();
model.$id = "123";
model.$version = "1";
datastore.save(model, function(obj){
assertTrue(obj === model);
assertEquals(obj.$entity, "abc");
assertEquals(obj.$id, "123");
assertEquals(obj.$version, "2");
assertEquals(obj.name, "value");
obj.after = true;
});
datastore.flush();
};
DataStoreTest.prototype.testLoadGetsFromServer = function(){
expectAsserts(12);
var post = function(data, callback){
var method = data[0][0];
var path = data[0][1];
assertEquals("GET", method);
assertEquals("abc/1", path);
response = [{$entity:'abc', $id:'1', $version:'2', key:"value"}];
callback(200, response);
};
var datastore = new nglr.DataStore(post);
var model = datastore.entity("abc", {merge:true})();
assertEquals(datastore.load(model, '1', function(obj){
assertEquals(obj.$entity, "abc");
assertEquals(obj.$id, "1");
assertEquals(obj.$version, "2");
assertEquals(obj.key, "value");
}), model);
datastore.flush();
assertEquals(model.$entity, "abc");
assertEquals(model.$id, "1");
assertEquals(model.$version, "2");
assertEquals(model.key, "value");
assertEquals(model.merge, true);
};
DataStoreTest.prototype.testRemove = function(){
expectAsserts(8);
var response;
var post = function(data, callback){
var method = data[0][0];
var posted = data[0][2];
assertEquals("DELETE", method);
assertEquals("abc", posted.$entity);
assertEquals("123", posted.$id);
assertEquals("1", posted.$version);
assertFalse('function' == typeof posted.save);
response = nglr.fromJson(nglr.toJson(posted));
response.$entity = "abc";
response.$id = "123";
response.$version = "2";
callback(200, [response]);
};
var model;
var datastore = new nglr.DataStore(post);
model = datastore.entity('abc', {name: "value"})();
model.$id = "123";
model.$version = "1";
datastore.remove(model, function(obj){
assertEquals(obj.$id, "123");
assertEquals(obj.$version, "2");
assertEquals(obj.name, "value");
obj.after = true;
});
datastore.flush();
};
DataStoreTest.prototype.test401ResponseDoesNotCallCallback = function(){
expectAsserts(1);
var post = function(data, callback) {
callback(200, {$status_code: 401});
};
var datastore = new nglr.DataStore(post, {login:function(){
assertTrue(true);
}});
var onLoadAll = function(){
assertTrue(false, "onLoadAll should not be called when response is status 401");
};
datastore.bulkRequest.push({});
datastore.flush();
datastore.loadAll({type: "A"}, onLoadAll);
};
DataStoreTest.prototype.test403ResponseDoesNotCallCallback = function(){
expectAsserts(1);
var post = function(data, callback) {
callback(200, [{$status_code: 403}]);
};
var datastore = new nglr.DataStore(post, {notAuthorized:function(){
assertTrue(true);
}});
var onLoadAll = function(){
assertTrue(false, "onLoadAll should not be called when response is status 403");
};
datastore.bulkRequest.push({});
datastore.flush();
datastore.loadAll({type: "A"}, onLoadAll);
};
DataStoreTest.prototype.testLoadCalledWithoutIdShouldBeNoop = function(){
expectAsserts(2);
var post = function(url, callback){
assertTrue(false);
};
var datastore = new nglr.DataStore(post);
var model = datastore.entity("abc")();
assertEquals(datastore.load(model, undefined), model);
assertEquals(model.$entity, "abc");
};
DataStoreTest.prototype.testEntityFactory = function(){
var ds = new nglr.DataStore();
var Recipe = ds.entity("Recipe", {a:1, b:2});
assertEquals(Recipe.title, "Recipe");
assertEquals(Recipe.defaults.a, 1);
assertEquals(Recipe.defaults.b, 2);
var recipe = Recipe();
assertEquals(recipe.$entity, "Recipe");
assertEquals(recipe.a, 1);
assertEquals(recipe.b, 2);
recipe = new Recipe();
assertEquals(recipe.$entity, "Recipe");
assertEquals(recipe.a, 1);
assertEquals(recipe.b, 2);
};
DataStoreTest.prototype.testEntityFactoryNoDefaults = function(){
var ds = new nglr.DataStore();
var Recipe = ds.entity("Recipe");
assertEquals(Recipe.title, "Recipe");
recipe = new Recipe();
assertEquals(recipe.$entity, "Recipe");
};
DataStoreTest.prototype.testEntityFactoryWithInitialValues = function(){
var ds = new nglr.DataStore();
var Recipe = ds.entity("Recipe");
var recipe = Recipe({name: "name"});
assertEquals("name", recipe.name);
};
DataStoreTest.prototype.testEntityLoad = function(){
var ds = new nglr.DataStore();
var Recipe = ds.entity("Recipe", {a:1, b:2});
ds.load = function(instance, id, callback){
callback.apply(instance);
return instance;
};
var instance = null;
var recipe2 = Recipe.load("ID", function(){
instance = this;
});
assertTrue(recipe2 === instance);
};
DataStoreTest.prototype.testSaveScope = function(){
var ds = new nglr.DataStore();
var log = "";
var Person = ds.entity("Person");
var person1 = Person({name:"A", $entity:"Person", $id:"1", $version:"1"}, ds);
person1.$$anchor = "A";
var person2 = Person({name:"B", $entity:"Person", $id:"2", $version:"2"}, ds);
person2.$$anchor = "B";
var anchor = {};
ds.anchor = anchor;
ds._jsonRequest = function(request, callback){
log += "save(" + request[2].$id + ");";
callback({$id:request[2].$id});
};
ds.saveScope({person1:person1, person2:person2,
ignoreMe:{name: "ignore", save:function(callback){callback();}}}, function(){
log += "done();";
});
assertEquals("save(1);save(2);done();", log);
assertEquals(1, anchor.A);
assertEquals(2, anchor.B);
};
DataStoreTest.prototype.testEntityLoadAllRows = function(){
var ds = new nglr.DataStore();
var Recipe = ds.entity("Recipe");
var list = [];
ds.loadAll = function(entity, callback){
assertTrue(Recipe === entity);
callback.apply(list);
return list;
};
var items = Recipe.all(function(){
assertTrue(list === this);
});
assertTrue(items === list);
};
DataStoreTest.prototype.testLoadAll = function(){
expectAsserts(8);
var post = function(data, callback){
assertEquals("GET", data[0][0]);
assertEquals("A", data[0][1]);
callback(200, [[{$entity:'A', $id:'1'},{$entity:'A', $id:'2'}]]);
};
var datastore = new nglr.DataStore(post);
var list = datastore.entity("A").all(function(){
assertTrue(true);
});
datastore.flush();
assertEquals(list.length, 2);
assertEquals(list[0].$entity, "A");
assertEquals(list[0].$id, "1");
assertEquals(list[1].$entity, "A");
assertEquals(list[1].$id, "2");
};
DataStoreTest.prototype.testQuery = function(){
expectAsserts(5);
var post = function(data, callback) {
assertEquals("GET", data[0][0]);
assertEquals("Employee/managerId=123abc", data[0][1]);
callback(200, [[{$entity:"Employee", $id: "456", managerId: "123ABC"}]]);
};
var datastore = new nglr.DataStore(post);
var Employee = datastore.entity("Employee");
var list = Employee.query('managerId', "123abc", function(){
assertTrue(true);
});
datastore.flush();
assertJsonEquals([[{$entity:"Employee", $id: "456", managerId: "123ABC"}]], datastore._cache.$collections);
assertEquals(list[0].$id, "456");
};
DataStoreTest.prototype.testLoadingDocumentRefreshesExistingArrays = function() {
expectAsserts(12);
var post;
var datastore = new nglr.DataStore(function(r, c){post(r,c);});
var Book = datastore.entity('Book');
post = function(req, callback) {
callback(200, [[{$id:1, $entity:"Book", name:"Moby"},
{$id:2, $entity:"Book", name:"Dick"}]]);
};
var allBooks = Book.all();
datastore.flush();
var queryBooks = Book.query("a", "b");
datastore.flush();
assertEquals("Moby", allBooks[0].name);
assertEquals("Dick", allBooks[1].name);
assertEquals("Moby", queryBooks[0].name);
assertEquals("Dick", queryBooks[1].name);
post = function(req, callback) {
assertEquals('[["GET","Book/1"]]', nglr.toJson(req));
callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]);
};
var book = Book.load(1);
datastore.flush();
assertEquals("Moby Dick", book.name);
assertEquals("Moby Dick", allBooks[0].name);
assertEquals("Moby Dick", queryBooks[0].name);
post = function(req, callback) {
assertEquals('POST', req[0][0]);
callback(200, [{$id:1, $entity:"Book", name:"The Big Fish"}]);
};
book.$save();
datastore.flush();
assertEquals("The Big Fish", book.name);
assertEquals("The Big Fish", allBooks[0].name);
assertEquals("The Big Fish", queryBooks[0].name);
};
DataStoreTest.prototype.testEntityProperties = function() {
expectAsserts(2);
var datastore = new nglr.DataStore();
var callback = {};
datastore._jsonRequest = function(request, callbackFn) {
assertJsonEquals(["GET", "Cheese/$properties"], request);
assertEquals(callback, callbackFn);
};
var Cheese = datastore.entity("Cheese");
Cheese.properties(callback);
};
DataStoreTest.prototype.testLoadInstanceIsNotFromCache = function() {
var post;
var datastore = new nglr.DataStore(function(r, c){post(r,c);});
var Book = datastore.entity('Book');
post = function(req, callback) {
assertEquals('[["GET","Book/1"]]', nglr.toJson(req));
callback(200, [{$id:1, $entity:"Book", name:"Moby Dick"}]);
};
var book = Book.load(1);
datastore.flush();
assertEquals("Moby Dick", book.name);
assertFalse(book === datastore._cache['Book/1']);
};
DataStoreTest.prototype.testLoadStarsIsNewDocument = function() {
var datastore = new nglr.DataStore();
var Book = datastore.entity('Book');
var book = Book.load('*');
assertEquals('Book', book.$entity);
};
DataStoreTest.prototype.testUndefinedEntityReturnsNullValueObject = function() {
var datastore = new nglr.DataStore();
var Entity = datastore.entity(undefined);
var all = Entity.all();
assertEquals(0, all.length);
};
DataStoreTest.prototype.testFetchEntities = function(){
expectAsserts(6);
var post = function(data, callback){
assertJsonEquals(["GET", "$entities"], data[0]);
callback(200, [{A:0, B:0}]);
};
var datastore = new nglr.DataStore(post);
var entities = datastore.entities(function(){
assertTrue(true);
});
datastore.flush();
assertJsonEquals([], datastore.bulkRequest);
assertEquals(2, entities.length);
assertEquals("A", entities[0].title);
assertEquals("B", entities[1].title);
};
DataStoreTest.prototype.testItShouldMigrateSchema = function() {
var datastore = new nglr.DataStore();
var Entity = datastore.entity("Entity", {a:[], user:{name:"Misko", email:""}});
var doc = Entity().$loadFrom({b:'abc', user:{email:"misko@hevery.com"}});
assertFalse(
nglr.toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}) ==
nglr.toJson(doc));
doc.$migrate();
assertEquals(
nglr.toJson({a:[], b:'abc', user:{name:"Misko", email:"misko@hevery.com"}}),
nglr.toJson(doc));
};
DataStoreTest.prototype.testItShouldCollectRequestsForBulk = function() {
var ds = new nglr.DataStore();
var Book = ds.entity("Book");
var Library = ds.entity("Library");
Book.all();
Library.load("123");
assertEquals(2, ds.bulkRequest.length);
assertJsonEquals(["GET", "Book"], ds.bulkRequest[0]);
assertJsonEquals(["GET", "Library/123"], ds.bulkRequest[1]);
};
DataStoreTest.prototype.testEmptyFlushShouldDoNothing = function () {
var ds = new nglr.DataStore(function(){
fail("expecting noop");
});
ds.flush();
};
DataStoreTest.prototype.testFlushShouldCallAllCallbacks = function() {
var log = "";
function post(request, callback){
log += 'BulkRequest:' + nglr.toJson(request) + ';';
callback(200, [[{$id:'ABC'}], {$id:'XYZ'}]);
}
var ds = new nglr.DataStore(post);
var Book = ds.entity("Book");
var Library = ds.entity("Library");
Book.all(function(instance){
log += nglr.toJson(instance) + ';';
});
Library.load("123", function(instance){
log += nglr.toJson(instance) + ';';
});
assertEquals("", log);
ds.flush();
assertJsonEquals([], ds.bulkRequest);
assertEquals('BulkRequest:[["GET","Book"],["GET","Library/123"]];[{"$id":"ABC"}];{"$id":"XYZ"};', log);
};
DataStoreTest.prototype.testSaveOnNotLoggedInRetriesAfterLoggin = function(){
var log = "";
var book;
var ds = new nglr.DataStore(null, {login:function(c){c();}});
ds.post = function (request, callback){
assertJsonEquals([["POST", "", book]], request);
ds.post = function(request, callback){
assertJsonEquals([["POST", "", book]], request);
ds.post = function(){fail("too much recursion");};
callback(200, [{saved:"ok"}]);
};
callback(200, {$status_code:401});
};
book = ds.entity("Book")({name:"misko"});
book.$save();
ds.flush();
assertJsonEquals({saved:"ok"}, book);
};
DataStoreTest.prototype.testItShouldRemoveItemFromCollectionWhenDeleted = function() {
expectAsserts(6);
var ds = new nglr.DataStore();
ds.post = function(request, callback){
assertJsonEquals([["GET", "Book"]], request);
callback(200, [[{name:"Moby Dick", $id:123, $entity:'Book'}]]);
};
var Book = ds.entity("Book");
var books = Book.all();
ds.flush();
assertJsonEquals([[{name:"Moby Dick", $id:123, $entity:'Book'}]], ds._cache.$collections);
assertDefined(ds._cache['Book/123']);
var book = Book({$id:123});
ds.post = function(request, callback){
assertJsonEquals([["DELETE", "", book]], request);
callback(200, [book]);
};
ds.remove(book);
ds.flush();
assertUndefined(ds._cache['Book/123']);
assertJsonEquals([[]],ds._cache.$collections);
};
DataStoreTest.prototype.testItShouldAddToAll = function() {
expectAsserts(8);
var ds = new nglr.DataStore();
ds.post = function(request, callback){
assertJsonEquals([["GET", "Book"]], request);
callback(200, [[]]);
};
var Book = ds.entity("Book");
var books = Book.all();
assertEquals(0, books.length);
ds.flush();
var moby = Book({name:'moby'});
moby.$save();
ds.post = function(request, callback){
assertJsonEquals([["POST", "", moby]], request);
moby.$id = '123';
callback(200, [moby]);
};
ds.flush();
assertEquals(1, books.length);
assertEquals(moby, books[0]);
moby.$save();
ds.flush();
assertEquals(1, books.length);
assertEquals(moby, books[0]);
};
DataStoreTest.prototype.testItShouldReturnCreatedDocumentCountByUser = function(){
expectAsserts(2);
var datastore = new nglr.DataStore(
function(request, callback){
assertJsonEquals([["GET", "$users"]], request);
callback(200, [{misko:1, adam:1}]);
});
var users = datastore.documentCountsByUser();
assertJsonEquals({misko:1, adam:1}, users);
};
DataStoreTest.prototype.testItShouldReturnDocumentIdsForUeserByEntity = function(){
expectAsserts(2);
var datastore = new nglr.DataStore(
function(request, callback){
assertJsonEquals([["GET", "$users/misko@hevery.com"]], request);
callback(200, [{Book:["1"], Library:["2"]}]);
});
var users = datastore.userDocumentIdsByEntity("misko@hevery.com");
assertJsonEquals({Book:["1"], Library:["2"]}, users);
};
DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){
expectAsserts(7);
var log = "";
var datastore = new nglr.DataStore(
function(request, callback){
assertJsonEquals([["GET", "User/misko"]], request);
callback(200, [{$status_code:404}]);
});
var User = datastore.entity("User", {admin:false});
var user = User.loadOrCreate('misko', function(i){log+="cb "+i.$id+";";});
datastore.flush();
assertEquals("misko", user.$id);
assertEquals("User", user.$entity);
assertEquals(false, user.admin);
assertEquals("undefined", typeof user.$secret);
assertEquals("undefined", typeof user.$version);
assertEquals("cb misko;", log);
};
DataStoreTest.prototype.testItShouldReturnNewInstanceOn404 = function(){
var log = "";
var datastore = new nglr.DataStore(
function(request, callback){
assertJsonEquals([["GET", "User/misko"],["GET", "User/adam"]], request);
callback(200, [{$id:'misko'},{$id:'adam'}]);
});
var User = datastore.entity("User");
var users = User.loadMany(['misko', 'adam'], function(i){log+="cb "+nglr.toJson(i)+";";});
datastore.flush();
assertEquals("misko", users[0].$id);
assertEquals("adam", users[1].$id);
assertEquals('cb [{"$id":"misko"},{"$id":"adam"}];', log);
};
DataStoreTest.prototype.testItShouldCreateJoinAndQuery = function() {
var datastore = new nglr.DataStore();
var Invoice = datastore.entity("Invoice");
var Customer = datastore.entity("Customer");
var InvoiceWithCustomer = datastore.join({
invoice:{join:Invoice},
customer:{join:Customer, on:"invoice.customer"}
});
var invoiceWithCustomer = InvoiceWithCustomer.query("invoice.month", 1);
assertEquals([], invoiceWithCustomer);
assertJsonEquals([["GET", "Invoice/month=1"]], datastore.bulkRequest);
var request = datastore.bulkRequest.shift();
request.$$callback([{$id:1, customer:1},{$id:2, customer:1},{$id:3, customer:3}]);
assertJsonEquals([["GET","Customer/1"],["GET","Customer/3"]], datastore.bulkRequest);
datastore.bulkRequest.shift().$$callback({$id:1});
datastore.bulkRequest.shift().$$callback({$id:3});
assertJsonEquals([
{invoice:{$id:1,customer:1},customer:{$id:1}},
{invoice:{$id:2,customer:1},customer:{$id:1}},
{invoice:{$id:3,customer:3},customer:{$id:3}}], invoiceWithCustomer);
};
DataStoreTest.prototype.testItShouldThrowIfMoreThanOneEntityIsPrimary = function() {
var datastore = new nglr.DataStore();
var Invoice = datastore.entity("Invoice");
var Customer = datastore.entity("Customer");
assertThrows("Exactly one entity needs to be primary.", function(){
datastore.join({
invoice:{join:Invoice},
customer:{join:Customer}
});
});
};
DataStoreTest.prototype.testItShouldThrowIfLoopInReferences = function() {
var datastore = new nglr.DataStore();
var Invoice = datastore.entity("Invoice");
var Customer = datastore.entity("Customer");
assertThrows("Infinite loop in join: invoice -> customer", function(){
datastore.join({
invoice:{join:Invoice, on:"customer.invoice"},
customer:{join:Customer, on:"invoice.customer"}
});
});
};
DataStoreTest.prototype.testItShouldThrowIfReferenceToNonExistantJoin = function() {
var datastore = new nglr.DataStore();
var Invoice = datastore.entity("Invoice");
var Customer = datastore.entity("Customer");
assertThrows("Named entity 'x' is undefined.", function(){
datastore.join({
invoice:{join:Invoice, on:"x.invoice"},
customer:{join:Customer, on:"invoice.customer"}
});
});
};
DataStoreTest.prototype.testItShouldThrowIfQueryOnNonPrimary = function() {
var datastore = new nglr.DataStore();
var Invoice = datastore.entity("Invoice");
var Customer = datastore.entity("Customer");
var InvoiceWithCustomer = datastore.join({
invoice:{join:Invoice},
customer:{join:Customer, on:"invoice.customer"}
});
assertThrows("Named entity 'customer' is not a primary entity.", function(){
InvoiceWithCustomer.query("customer.month", 1);
});
};

View file

@ -0,0 +1,46 @@
EntityDeclarationTest = TestCase('EntityDeclarationTest');
EntityDeclarationTest.prototype.testEntityTypeOnly = function(){
expectAsserts(2);
var scope = new nglr.Scope({$datastore:{entity:function(name){
assertEquals("Person", name);
}}});
var init = scope.entity("Person");
assertEquals("", init);
};
EntityDeclarationTest.prototype.testWithDefaults = function(){
expectAsserts(4);
var scope = new nglr.Scope({$datastore:{entity:function(name, init){
assertEquals("Person", name);
assertEquals("=a:", init.a);
assertEquals(0, init.b.length);
}}});
var init = scope.entity('Person:{a:"=a:", b:[]}');
assertEquals("", init);
};
EntityDeclarationTest.prototype.testWithName = function(){
expectAsserts(2);
var scope = new nglr.Scope({$datastore:{entity:function(name, init){
assertEquals("Person", name);
return function (){ return {}; };
}}});
var init = scope.entity('friend=Person');
assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};", init);
};
EntityDeclarationTest.prototype.testMultipleEntities = function(){
expectAsserts(3);
var expect = ['Person', 'Book'];
var i=0;
var scope = new nglr.Scope({$datastore:{entity:function(name, init){
assertEquals(expect[i], name);
i++;
return function (){ return {}; };
}}});
var init = scope.entity('friend=Person;book=Book;');
assertEquals("$anchor.friend:{friend=Person.load($anchor.friend);friend.$$anchor=\"friend\";};" +
"$anchor.book:{book=Book.load($anchor.book);book.$$anchor=\"book\";};",
init);
};

View file

@ -0,0 +1,98 @@
FileControllerTest = TestCase('FileControllerTest');
FileControllerTest.prototype.testOnSelectUpdateView = function(){
var view = jQuery('<span><a/><span/></span>');
var swf = {};
var controller = new nglr.FileController(view, null, swf);
swf.uploadFile = function(path){};
controller._on_select('A', 9, '9 bytes');
assertEquals(view.find('a').text(), "A");
assertEquals(view.find('span').text(), "9 bytes");
};
FileControllerTest.prototype.testUpdateModelView = function(){
var view = nglr.FileController.template('');
var input = $('<input name="value.input">');
var controller;
var scope = new nglr.Scope({value:{}, $binder:{updateView:function(){
controller.updateView(scope);
}}});
view.data('scope', scope);
controller = new nglr.FileController(view, 'value.input', null, "http://server_base");
var value = '{"text":"A", "size":123, "id":"890"}';
controller._on_uploadCompleteData(value);
controller.updateView(scope);
assertEquals(scope.get('value.input.text'), 'A');
assertEquals(scope.get('value.input.size'), 123);
assertEquals(scope.get('value.input.id'), '890');
assertEquals(scope.get('value.input.url'), 'http://server_base/_attachments/890/A');
assertEquals(view.find('a').text(), "A");
assertEquals(view.find('a').attr('href'), "http://server_base/_attachments/890/A");
assertEquals(view.find('span').text(), "123 bytes");
};
FileControllerTest.prototype.testFileUpload = function(){
expectAsserts(1);
var swf = {};
var controller = new nglr.FileController(null, null, swf, "http://server_base");
swf.uploadFile = function(path){
assertEquals("http://server_base/_attachments", path);
};
controller.name = "Name";
controller.upload();
};
FileControllerTest.prototype.testFileUploadNoFileIsNoop = function(){
expectAsserts(0);
var swf = {uploadFile:function(path){
fail();
}};
var controller = new nglr.FileController(null, swf);
controller.upload("basePath", null);
};
FileControllerTest.prototype.testRemoveAttachment = function(){
var doc = nglr.FileController.template();
var input = $('<input name="file">');
var scope = new nglr.Scope();
input.data('scope', scope);
var controller = new nglr.FileController(doc, 'file', null, null);
controller.updateView(scope);
assertEquals(false, doc.find('input').attr('checked'));
scope.set('file', {url:'url', size:123});
controller.updateView(scope);
assertEquals(true, doc.find('input').attr('checked'));
doc.find('input').attr('checked', false);
controller.updateModel(scope);
assertNull(scope.get('file'));
doc.find('input').attr('checked', true);
controller.updateModel(scope);
assertEquals('url', scope.get('file.url'));
assertEquals(123, scope.get('file.size'));
};
FileControllerTest.prototype.testShouldEmptyOutOnUndefined = function () {
var view = nglr.FileController.template('hello');
var controller = new nglr.FileController(view, 'abc', null, null);
var scope = new nglr.Scope();
scope.set('abc', {text: 'myname', url: 'myurl', size: 1234});
controller.updateView(scope);
assertEquals("myurl", view.find('a').attr('href'));
assertEquals("myname", view.find('a').text());
assertEquals(true, view.find('input').is(':checked'));
assertEquals("1.2 KB", view.find('span').text());
scope.set('abc', undefined);
controller.updateView(scope);
assertEquals("myurl", view.find('a').attr('href'));
assertEquals("myname", view.find('a').text());
assertEquals(false, view.find('input').is(':checked'));
assertEquals("1.2 KB", view.find('span').text());
};

153
test/FiltersTest.js Normal file
View file

@ -0,0 +1,153 @@
FiltersTest = TestCase('FiltersTest');
FiltersTest.prototype.testCurrency = function(){
var html = $('<span/>');
var context = {element:html[0]};
var currency = nglr.bind(context, angular.filter.currency);
assertEquals(currency(0), '$0.00');
assertEquals(html.hasClass('ng-format-negative'), false);
assertEquals(currency(-999), '$-999.00');
assertEquals(html.hasClass('ng-format-negative'), true);
assertEquals(currency(1234.5678), '$1,234.57');
assertEquals(html.hasClass('ng-format-negative'), false);
};
FiltersTest.prototype.testFilterThisIsContext = function(){
expectAsserts(2);
var scope = new nglr.Scope();
nglr.Scope.expressionCache = {};
var context = {element:123};
angular.filter.testFn = function () {
assertEquals('Context not equal', this, context);
assertEquals('scope not equal', this.scope, scope);
};
scope.eval("0|testFn", context);
delete angular.filter['testFn'];
};
FiltersTest.prototype.testNumberFormat = function(){
var context = {jqElement:$('<span/>')};
var number = nglr.bind(context, angular.filter.number);
assertEquals('0', number(0, 0));
assertEquals('0.00', number(0));
assertEquals('-999.00', number(-999));
assertEquals('1,234.57', number(1234.5678));
assertEquals('', number(Number.NaN));
assertEquals('1,234.57', number("1234.5678"));
assertEquals("", number(1/0));
};
FiltersTest.prototype.testJson = function () {
assertEquals(nglr.toJson({a:"b"}, true), angular.filter.json({a:"b"}));
};
FiltersTest.prototype.testPackageTracking = function () {
var assert = function(title, trackingNo) {
var val = angular.filter.trackPackage(trackingNo, title);
assertNotNull("Did Not Match: " + trackingNo, val);
assertEquals(angular.filter.Meta.TAG, val.TAG);
assertEquals(title + ": " + nglr.trim(trackingNo), val.text);
assertNotNull(val.url);
assertEquals(nglr.trim(trackingNo), val.trackingNo);
assertEquals('<a href="' + val.url + '">' + val.text + '</a>', val.html);
};
assert('UPS', ' 1Z 999 999 99 9999 999 9 ');
assert('UPS', '1ZW5w5220379084747');
assert('FedEx', '418822131061812');
assert('FedEx', '9612019 5935 3267 2473 738');
assert('FedEx', '9612019593532672473738');
assert('FedEx', '235354667129449');
assert('FedEx', '915368880571');
assert('FedEx', '901712142390');
assert('FedEx', '297391510063413');
assert('USPS', '9101 8052 1390 7402 4335 49');
assert('USPS', '9101010521297963339560');
assert('USPS', '9102901001301038667029');
assert('USPS', '910 27974 4490 3000 8916 56');
assert('USPS', '9102801438635051633253');
};
FiltersTest.prototype.testLink = function() {
var assert = function(text, url, obj){
var val = angular.filter.link(obj);
assertEquals(angular.filter.Meta.TAG, val.TAG);
assertEquals('<a href="' + url + '">' + text + '</a>', val.html);
};
assert("url", "url", "url");
assert("hello", "url", {text:"hello", url:"url"});
assert("a@b.com", "mailto:a@b.com", "a@b.com");
};
FiltersTest.prototype.testBytes = function(){
var controller = new nglr.FileController();
assertEquals(angular.filter.bytes(123), '123 bytes');
assertEquals(angular.filter.bytes(1234), '1.2 KB');
assertEquals(angular.filter.bytes(1234567), '1.1 MB');
};
FiltersTest.prototype.testImage = function(){
assertEquals(null, angular.filter.image());
assertEquals(null, angular.filter.image({}));
assertEquals(null, angular.filter.image(""));
assertEquals('<img src="abc"/>', angular.filter.image({url:"abc"}).html);
assertEquals(
'<img src="abc" style="max-width: 10px; max-height: 10px;"/>',
angular.filter.image({url:"abc"}, 10).html);
assertEquals(
'<img src="abc" style="max-width: 10px; max-height: 20px;"/>',
angular.filter.image({url:"abc"}, 10, 20).html);
};
FiltersTest.prototype.testQRcode = function() {
assertEquals(
'<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello%20world&chs=200x200&cht=qr"/>',
angular.filter.qrcode('Hello world').html);
assertEquals(
'<img width="100" height="100" src="http://chart.apis.google.com/chart?chl=http%3A%2F%2Fserver%3Fa%26b%3Dc&chs=100x100&cht=qr"/>',
angular.filter.qrcode('http://server?a&b=c', 100).html);
};
FiltersTest.prototype.testLowercase = function() {
assertEquals('abc', angular.filter.lowercase('AbC'));
assertEquals(null, angular.filter.lowercase(null));
};
FiltersTest.prototype.testUppercase = function() {
assertEquals('ABC', angular.filter.uppercase('AbC'));
assertEquals(null, angular.filter.uppercase(null));
};
FiltersTest.prototype.testLineCount = function() {
assertEquals(1, angular.filter.linecount(null));
assertEquals(1, angular.filter.linecount(''));
assertEquals(1, angular.filter.linecount('a'));
assertEquals(2, angular.filter.linecount('a\nb'));
assertEquals(3, angular.filter.linecount('a\nb\nc'));
};
FiltersTest.prototype.testIf = function() {
assertEquals('A', angular.filter['if']('A', true));
assertEquals(undefined, angular.filter['if']('A', false));
};
FiltersTest.prototype.testUnless = function() {
assertEquals('A', angular.filter.unless('A', false));
assertEquals(undefined, angular.filter.unless('A', true));
};
FiltersTest.prototype.testGoogleChartApiEncode = function() {
assertEquals(
'<img width="200" height="200" src="http://chart.apis.google.com/chart?chl=Hello world&chs=200x200&cht=qr"/>',
angular.filter.googleChartApi.encode({cht:"qr", chl:"Hello world"}).html);
};
FiltersTest.prototype.testHtml = function() {
assertEquals(
"a<b>c</b>d",
angular.filter.html("a<b>c</b>d").html);
assertTrue(angular.filter.html("a<b>c</b>d") instanceof angular.filter.Meta);
};

69
test/JsonTest.js Normal file
View file

@ -0,0 +1,69 @@
JsonTest = TestCase("JsonTest");
JsonTest.prototype.testPrimitives = function () {
assertEquals("null", nglr.toJson(0/0));
assertEquals("null", nglr.toJson(null));
assertEquals("true", nglr.toJson(true));
assertEquals("false", nglr.toJson(false));
assertEquals("123.45", nglr.toJson(123.45));
assertEquals('"abc"', nglr.toJson("abc"));
assertEquals('"a \\t \\n \\r b \\\\"', nglr.toJson("a \t \n \r b \\"));
};
JsonTest.prototype.testEscaping = function () {
assertEquals("\"7\\\\\\\"7\"", nglr.toJson("7\\\"7"));
};
JsonTest.prototype.testObjects = function () {
assertEquals('{"a":1,"b":2}', nglr.toJson({a:1,b:2}));
assertEquals('{"a":{"b":2}}', nglr.toJson({a:{b:2}}));
assertEquals('{"a":{"b":{"c":0}}}', nglr.toJson({a:{b:{c:0}}}));
assertEquals('{"a":{"b":null}}', nglr.toJson({a:{b:0/0}}));
};
JsonTest.prototype.testObjectPretty = function () {
assertEquals('{\n "a":1,\n "b":2}', nglr.toJson({a:1,b:2}, true));
assertEquals('{\n "a":{\n "b":2}}', nglr.toJson({a:{b:2}}, true));
};
JsonTest.prototype.testArray = function () {
assertEquals('[]', nglr.toJson([]));
assertEquals('[1,"b"]', nglr.toJson([1,"b"]));
};
JsonTest.prototype.testIgnoreFunctions = function () {
assertEquals('[null,1]', nglr.toJson([function(){},1]));
assertEquals('{}', nglr.toJson({a:function(){}}));
};
JsonTest.prototype.testParseNull = function () {
assertNull(nglr.fromJson("null"));
};
JsonTest.prototype.testParseBoolean = function () {
assertTrue(nglr.fromJson("true"));
assertFalse(nglr.fromJson("false"));
};
JsonTest.prototype.test$$isIgnored = function () {
assertEquals("{}", nglr.toJson({$$:0}));
};
JsonTest.prototype.testArrayWithEmptyItems = function () {
var a = [];
a[1] = "X";
assertEquals('[null,"X"]', nglr.toJson(a));
};
JsonTest.prototype.testItShouldEscapeUnicode = function () {
assertEquals(1, "\u00a0".length);
assertEquals(8, nglr.toJson("\u00a0").length);
assertEquals(1, nglr.fromJson(nglr.toJson("\u00a0")).length);
};
JsonTest.prototype.testItShouldUTCDates = function() {
var date = angular.String.toDate("2009-10-09T01:02:03Z");
assertEquals('"2009-10-09T01:02:03Z"', nglr.toJson(date));
assertEquals(date.getTime(),
nglr.fromJson('"2009-10-09T01:02:03Z"').getTime());
};

70
test/LoaderTest.js Normal file
View file

@ -0,0 +1,70 @@
LoaderTest = TestCase('LoaderTest');
LoaderTest.prototype.testLoadCss = function(){
if ($.browser.safari) return;
var head = jQuery('<head/>')[0];
var loader = new nglr.Loader(document, head, {});
var log = '';
loader.config.server = 'http://';
loader.loadCss('x');
assertEquals($(head).find('link').attr('href'), 'http://x');
};
LoaderTest.prototype.testDefaultDatabasePathFromSubdomain = function() {
var loader = new nglr.Loader(null, null, {server:"http://account.getangular.com", database:"database"});
loader.computeConfiguration();
assertEquals("database", loader.config.database);
loader = new nglr.Loader(null, null, {server:"http://account.getangular.com"});
loader.computeConfiguration();
assertEquals("account", loader.config.database);
loader = new nglr.Loader(null, null, {server:"https://account.getangular.com"});
loader.computeConfiguration();
assertEquals("account", loader.config.database);
};
UrlWatcherTest = TestCase('UrlWatcherTest');
UrlWatcherTest.prototype.testUrlWatcher = function () {
expectAsserts(2);
var location = {href:"http://server", hash:""};
var watcher = new nglr.UrlWatcher(location);
watcher.delay = 1;
watcher.listener = function(url){
assertEquals('http://getangular.test', url);
};
watcher.setTimeout = function(fn, delay){
assertEquals(1, delay);
location.href = "http://getangular.test";
watcher.setTimeout = function(fn, delay) {
};
fn();
};
watcher.watch();
};
UrlWatcherTest.prototype.testItShouldFireOnUpdateEventWhenSpecialURLSet = function(){
expectAsserts(2);
var location = {href:"http://server", hash:"#$iframe_notify=1234"};
var watcher = new nglr.UrlWatcher(location);
nglr._iframe_notify_1234 = function () {
assertEquals("undefined", typeof nglr._iframe_notify_1234);
assertEquals("http://server2#", location.href);
};
watcher.delay = 1;
watcher.expectedUrl = "http://server2";
watcher.setTimeout = function(fn, delay){
watcher.setTimeout = function(fn, delay) {};
fn();
};
watcher.watch();
};
FunctionTest = TestCase("FunctionTest");
FunctionTest.prototype.testEscapeHtml = function () {
assertEquals("&lt;div&gt;&amp;amp;&lt;/div&gt;", nglr.escapeHtml('<div>&amp;</div>'));
};

84
test/ModelTest.js Normal file
View file

@ -0,0 +1,84 @@
ModelTest = TestCase('ModelTest');
ModelTest.prototype.testLoadSaveOperations = function(){
var m1 = new nglr.DataStore().entity('A')();
m1.a = 1;
var m2 = {b:1};
m1.$loadFrom(m2);
assertTrue(!m1.a);
assertEquals(m1.b, 1);
};
ModelTest.prototype.testLoadfromDoesNotClobberFunctions = function(){
var m1 = new nglr.DataStore().entity('A')();
m1.id = function(){return 'OK';};
m1.$loadFrom({id:null});
assertEquals(m1.id(), 'OK');
m1.b = 'OK';
m1.$loadFrom({b:function(){}});
assertEquals(m1.b, 'OK');
};
ModelTest.prototype.testDataStoreDoesNotGetClobbered = function(){
var ds = new nglr.DataStore();
var m = ds.entity('A')();
assertTrue(m.$$entity.datastore === ds);
m.$loadFrom({});
assertTrue(m.$$entity.datastore === ds);
};
ModelTest.prototype.testManagedModelDelegatesMethodsToDataStore = function(){
expectAsserts(7);
var datastore = new nglr.DataStore();
var model = datastore.entity("A", {a:1})();
var fn = {};
datastore.save = function(instance, callback) {
assertTrue(model === instance);
assertTrue(callback === fn);
};
datastore.remove = function(instance, callback) {
assertTrue(model === instance);
assertTrue(callback === fn);
};
datastore.load = function(instance, id, callback) {
assertTrue(model === instance);
assertTrue(id === "123");
assertTrue(callback === fn);
};
model.$save(fn);
model.$delete(fn);
model.$loadById("123", fn);
};
ModelTest.prototype.testManagedModelCanBeForcedToFlush = function(){
expectAsserts(6);
var datastore = new nglr.DataStore();
var model = datastore.entity("A", {a:1})();
datastore.save = function(instance, callback) {
assertTrue(model === instance);
assertTrue(callback === undefined);
};
datastore.remove = function(instance, callback) {
assertTrue(model === instance);
assertTrue(callback === undefined);
};
datastore.flush = function(){
assertTrue(true);
};
model.$save(true);
model.$delete(true);
};
ModelTest.prototype.testItShouldMakeDeepCopyOfInitialValues = function (){
var initial = {a:[]};
var entity = new nglr.DataStore().entity("A", initial);
var model = entity();
model.a.push(1);
assertEquals(0, entity().a.length);
};

462
test/ParserTest.js Normal file
View file

@ -0,0 +1,462 @@
LexerTest = TestCase('LexerTest');
LexerTest.prototype.testTokenizeAString = function(){
var lexer = new nglr.Lexer("a.bc[22]+1.3|f:'a\\\'c':\"d\\\"e\"");
var tokens = lexer.parse();
var i = 0;
assertEquals(tokens[i].index, 0);
assertEquals(tokens[i].text, 'a.bc');
i++;
assertEquals(tokens[i].index, 4);
assertEquals(tokens[i].text, '[');
i++;
assertEquals(tokens[i].index, 5);
assertEquals(tokens[i].text, 22);
i++;
assertEquals(tokens[i].index, 7);
assertEquals(tokens[i].text, ']');
i++;
assertEquals(tokens[i].index, 8);
assertEquals(tokens[i].text, '+');
i++;
assertEquals(tokens[i].index, 9);
assertEquals(tokens[i].text, 1.3);
i++;
assertEquals(tokens[i].index, 12);
assertEquals(tokens[i].text, '|');
i++;
assertEquals(tokens[i].index, 13);
assertEquals(tokens[i].text, 'f');
i++;
assertEquals(tokens[i].index, 14);
assertEquals(tokens[i].text, ':');
i++;
assertEquals(tokens[i].index, 15);
assertEquals(tokens[i].text, "a'c");
i++;
assertEquals(tokens[i].index, 21);
assertEquals(tokens[i].text, ':');
i++;
assertEquals(tokens[i].index, 22);
assertEquals(tokens[i].text, 'd"e');
};
LexerTest.prototype.testTokenizeRegExp = function(){
var lexer = new nglr.Lexer("/r 1/");
var tokens = lexer.parse();
var i = 0;
assertEquals(tokens[i].index, 0);
assertEquals(tokens[i].text, 'r 1');
assertEquals("r 1".match(tokens[i].fn())[0], 'r 1');
};
LexerTest.prototype.testQuotedString = function(){
var str = "['\\'', \"\\\"\"]";
var lexer = new nglr.Lexer(str);
var tokens = lexer.parse();
assertEquals(1, tokens[1].index);
assertEquals("'", tokens[1].text);
assertEquals(7, tokens[3].index);
assertEquals('"', tokens[3].text);
};
LexerTest.prototype.testQuotedStringEscape = function(){
var str = '"\\"\\n\\f\\r\\t\\v\\u00A0"';
var lexer = new nglr.Lexer(str);
var tokens = lexer.parse();
assertEquals('"\n\f\r\t\v\u00A0', tokens[0].text);
};
LexerTest.prototype.testTokenizeUnicode = function(){
var lexer = new nglr.Lexer('"\\u00A0"');
var tokens = lexer.parse();
assertEquals(1, tokens.length);
assertEquals('\u00a0', tokens[0].text);
};
LexerTest.prototype.testTokenizeRegExpWithOptions = function(){
var lexer = new nglr.Lexer("/r/g");
var tokens = lexer.parse();
var i = 0;
assertEquals(tokens[i].index, 0);
assertEquals(tokens[i].text, 'r');
assertEquals(tokens[i].flags, 'g');
assertEquals("rr".match(tokens[i].fn()).length, 2);
};
LexerTest.prototype.testTokenizeRegExpWithEscape = function(){
var lexer = new nglr.Lexer("/\\/\\d/");
var tokens = lexer.parse();
var i = 0;
assertEquals(tokens[i].index, 0);
assertEquals(tokens[i].text, '\\/\\d');
assertEquals("/1".match(tokens[i].fn())[0], '/1');
};
LexerTest.prototype.testIgnoreWhitespace = function(){
var lexer = new nglr.Lexer("a \t \n \r b");
var tokens = lexer.parse();
assertEquals(tokens[0].text, 'a');
assertEquals(tokens[1].text, 'b');
};
LexerTest.prototype.testRelation = function(){
var lexer = new nglr.Lexer("! == != < > <= >=");
var tokens = lexer.parse();
assertEquals(tokens[0].text, '!');
assertEquals(tokens[1].text, '==');
assertEquals(tokens[2].text, '!=');
assertEquals(tokens[3].text, '<');
assertEquals(tokens[4].text, '>');
assertEquals(tokens[5].text, '<=');
assertEquals(tokens[6].text, '>=');
};
LexerTest.prototype.testStatements = function(){
var lexer = new nglr.Lexer("a;b;");
var tokens = lexer.parse();
assertEquals(tokens[0].text, 'a');
assertEquals(tokens[1].text, ';');
assertEquals(tokens[2].text, 'b');
assertEquals(tokens[3].text, ';');
};
ParserTest = TestCase('ParserTest');
ParserTest.prototype.testExpressions = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("-1"), -1);
assertEquals(scope.eval("1 + 2.5"), 3.5);
assertEquals(scope.eval("1 + -2.5"), -1.5);
assertEquals(scope.eval("1+2*3/4"), 1+2*3/4);
assertEquals(scope.eval("0--1+1.5"), 0- -1 + 1.5);
assertEquals(scope.eval("-0--1++2*-3/-4"), -0- -1+ +2*-3/-4);
assertEquals(scope.eval("1/2*3"), 1/2*3);
};
ParserTest.prototype.testComparison = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("false"), false);
assertEquals(scope.eval("!true"), false);
assertEquals(scope.eval("1==1"), true);
assertEquals(scope.eval("1!=2"), true);
assertEquals(scope.eval("1<2"), true);
assertEquals(scope.eval("1<=1"), true);
assertEquals(scope.eval("1>2"), 1>2);
assertEquals(scope.eval("2>=1"), 2>=1);
};
ParserTest.prototype.testLogical = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("0&&2"), 0&&2);
assertEquals(scope.eval("0||2"), 0||2);
assertEquals(scope.eval("0||1&&2"), 0||1&&2);
};
ParserTest.prototype.testString = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("'a' + 'b c'"), "ab c");
};
ParserTest.prototype.testFilters = function(){
angular.filter.substring = function(input, start, end) {
return input.substring(start, end);
};
angular.filter.upper = {_case:function(input) {
return input.toUpperCase();
}};
var scope = new nglr.Scope();
try {
scope.eval("1|nonExistant");
fail();
} catch (e) {
assertEquals(e, "Function 'nonExistant' at column '3' in '1|nonExistant' is not defined.");
}
scope.set('offset', 3);
assertEquals(scope.eval("'abcd'|upper._case"), "ABCD");
assertEquals(scope.eval("'abcd'|substring:1:offset"), "bc");
assertEquals(scope.eval("'abcd'|substring:1:3|upper._case"), "BC");
};
ParserTest.prototype.testScopeAccess = function(){
var scope = new nglr.Scope();
scope.set('a', 123);
scope.set('b.c', 456);
assertEquals(scope.eval("a", scope), 123);
assertEquals(scope.eval("b.c", scope), 456);
assertEquals(scope.eval("x.y.z", scope), undefined);
};
ParserTest.prototype.testGrouping = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("(1+2)*3"), (1+2)*3);
};
ParserTest.prototype.testAssignments = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("a=12"), 12);
assertEquals(scope.get("a"), 12);
scope = new nglr.Scope();
assertEquals(scope.eval("x.y.z=123;"), 123);
assertEquals(scope.get("x.y.z"), 123);
assertEquals(234, scope.eval("a=123; b=234"));
assertEquals(123, scope.get("a"));
assertEquals(234, scope.get("b"));
};
ParserTest.prototype.testFunctionCallsNoArgs = function(){
var scope = new nglr.Scope();
scope.set('const', function(a,b){return 123;});
assertEquals(scope.eval("const()"), 123);
};
ParserTest.prototype.testFunctionCalls = function(){
var scope = new nglr.Scope();
scope.set('add', function(a,b){
return a+b;
});
assertEquals(3, scope.eval("add(1,2)"));
};
ParserTest.prototype.testCalculationBug = function(){
var scope = new nglr.Scope();
scope.set('taxRate', 8);
scope.set('subTotal', 100);
assertEquals(scope.eval("taxRate / 100 * subTotal"), 8);
assertEquals(scope.eval("subTotal * taxRate / 100"), 8);
};
ParserTest.prototype.testArray = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("[]").length, 0);
assertEquals(scope.eval("[1, 2]").length, 2);
assertEquals(scope.eval("[1, 2]")[0], 1);
assertEquals(scope.eval("[1, 2]")[1], 2);
};
ParserTest.prototype.testArrayAccess = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("[1][0]"), 1);
assertEquals(scope.eval("[[1]][0][0]"), 1);
assertEquals(scope.eval("[].length"), 0);
assertEquals(scope.eval("[1, 2].length"), 2);
};
ParserTest.prototype.testObject = function(){
var scope = new nglr.Scope();
assertEquals(nglr.toJson(scope.eval("{}")), "{}");
assertEquals(nglr.toJson(scope.eval("{a:'b'}")), '{"a":"b"}');
assertEquals(nglr.toJson(scope.eval("{'a':'b'}")), '{"a":"b"}');
assertEquals(nglr.toJson(scope.eval("{\"a\":'b'}")), '{"a":"b"}');
};
ParserTest.prototype.testObjectAccess = function(){
var scope = new nglr.Scope();
assertEquals("WC", scope.eval("{false:'WC', true:'CC'}[false]"));
};
ParserTest.prototype.testJSON = function(){
var scope = new nglr.Scope();
assertEquals(nglr.toJson(scope.eval("[{}]")), "[{}]");
assertEquals(nglr.toJson(scope.eval("[{a:[]}, {b:1}]")), '[{"a":[]},{"b":1}]');
};
ParserTest.prototype.testMultippleStatements = function(){
var scope = new nglr.Scope();
assertEquals(scope.eval("a=1;b=3;a+b"), 4);
assertEquals(scope.eval(";;1;;"), 1);
};
ParserTest.prototype.testParseThrow = function(){
expectAsserts(1);
var scope = new nglr.Scope();
scope.set('e', 'abc');
try {
scope.eval("throw e");
} catch(e) {
assertEquals(e, 'abc');
}
};
ParserTest.prototype.testMethodsGetDispatchedWithCorrectThis = function(){
var scope = new nglr.Scope();
var C = function (){
this.a=123;
};
C.prototype.getA = function(){
return this.a;
};
scope.set("obj", new C());
assertEquals(123, scope.eval("obj.getA()"));
};
ParserTest.prototype.testMethodsArgumentsGetCorrectThis = function(){
var scope = new nglr.Scope();
var C = function (){
this.a=123;
};
C.prototype.sum = function(value){
return this.a + value;
};
C.prototype.getA = function(){
return this.a;
};
scope.set("obj", new C());
assertEquals(246, scope.eval("obj.sum(obj.getA())"));
};
ParserTest.prototype.testObjectPointsToScopeValue = function(){
var scope = new nglr.Scope();
scope.set('a', "abc");
assertEquals("abc", scope.eval("{a:a}").a);
};
ParserTest.prototype.testFieldAccess = function(){
var scope = new nglr.Scope();
var fn = function(){
return {name:'misko'};
};
scope.set('a', fn);
assertEquals("misko", scope.eval("a().name"));
};
ParserTest.prototype.testArrayIndexBug = function () {
var scope = new nglr.Scope();
scope.set('items', [{}, {name:'misko'}]);
assertEquals("misko", scope.eval('items[1].name'));
};
ParserTest.prototype.testArrayAssignment = function () {
var scope = new nglr.Scope();
scope.set('items', []);
assertEquals("abc", scope.eval('items[1] = "abc"'));
assertEquals("abc", scope.eval('items[1]'));
// Dont know how to make this work....
// assertEquals("moby", scope.eval('books[1] = "moby"'));
// assertEquals("moby", scope.eval('books[1]'));
};
ParserTest.prototype.testFiltersCanBeGrouped = function () {
var scope = new nglr.Scope({name:'MISKO'});
assertEquals('misko', scope.eval('n = (name|lowercase)'));
assertEquals('misko', scope.eval('n'));
};
ParserTest.prototype.testFiltersCanBeGrouped = function () {
var scope = new nglr.Scope({name:'MISKO'});
assertEquals('misko', scope.eval('n = (name|lowercase)'));
assertEquals('misko', scope.eval('n'));
};
ParserTest.prototype.testRemainder = function () {
var scope = new nglr.Scope();
assertEquals(1, scope.eval('1%2'));
};
ParserTest.prototype.testSumOfUndefinedIsNotUndefined = function () {
var scope = new nglr.Scope();
assertEquals(1, scope.eval('1+undefined'));
assertEquals(1, scope.eval('undefined+1'));
};
ParserTest.prototype.testMissingThrowsError = function() {
var scope = new nglr.Scope();
try {
scope.eval('[].count(');
fail();
} catch (e) {
assertEquals('Unexpected end of expression: [].count(', e);
}
};
ParserTest.prototype.testItShouldParseOnChangeIntoHashSet = function () {
var scope = new nglr.Scope({count:0});
scope.watch("$anchor.a:count=count+1;$anchor.a:count=count+20;b:count=count+300");
scope.watchListeners["$anchor.a"].listeners[0]();
assertEquals(1, scope.get("count"));
scope.watchListeners["$anchor.a"].listeners[1]();
assertEquals(21, scope.get("count"));
scope.watchListeners["b"].listeners[0]({scope:scope});
assertEquals(321, scope.get("count"));
};
ParserTest.prototype.testItShouldParseOnChangeBlockIntoHashSet = function () {
var scope = new nglr.Scope({count:0});
var listeners = {a:[], b:[]};
scope.watch("a:{count=count+1;count=count+20;};b:count=count+300",
function(n, fn){listeners[n].push(fn);});
assertEquals(1, scope.watchListeners.a.listeners.length);
assertEquals(1, scope.watchListeners.b.listeners.length);
scope.watchListeners["a"].listeners[0]();
assertEquals(21, scope.get("count"));
scope.watchListeners["b"].listeners[0]();
assertEquals(321, scope.get("count"));
};
ParserTest.prototype.testItShouldParseEmptyOnChangeAsNoop = function () {
var scope = new nglr.Scope();
scope.watch("", function(){fail();});
};
ParserTest.prototype.testItShouldCreateClosureFunctionWithNoArguments = function () {
var scope = new nglr.Scope();
var fn = scope.eval("{:value}");
scope.set("value", 1);
assertEquals(1, fn());
scope.set("value", 2);
assertEquals(2, fn());
fn = scope.eval("{():value}");
assertEquals(2, fn());
};
ParserTest.prototype.testItShouldCreateClosureFunctionWithArguments = function () {
var scope = new nglr.Scope();
var fn = scope.eval("{(a):value+a}");
scope.set("value", 1);
assertEquals(11, fn(10));
scope.set("value", 2);
assertEquals(12, fn(10));
fn = scope.eval("{(a,b):value+a+b}");
assertEquals(112, fn(10, 100));
};
ParserTest.prototype.testItShouldHaveDefaultArugument = function(){
var scope = new nglr.Scope();
var fn = scope.eval("{:$*2}");
assertEquals(4, fn(2));
};
ParserTest.prototype.testReturnFunctionsAreNotBound = function(){
var scope = new nglr.Scope();
scope.set("$datastore", new nglr.DataStore());
scope.entity("Group");
var Group = scope.get("Group");
assertEquals("eval Group", "function", typeof scope.eval("Group"));
assertEquals("direct Group", "function", typeof Group);
assertEquals("eval Group.all", "function", typeof scope.eval("Group.query"));
assertEquals("direct Group.all", "function", typeof Group.query);
};

144
test/ScopeTest.js Normal file
View file

@ -0,0 +1,144 @@
ScopeTest = TestCase('ScopeTest');
ScopeTest.prototype.testGetScopeRetrieval = function(){
var scope = {};
var form = jQuery("<a><b><c></c></b></a>");
form.data('scope', scope);
var c = form.find('c');
assertTrue(scope === c.scope());
};
ScopeTest.prototype.testGetScopeRetrievalIntermediateNode = function(){
var scope = {};
var form = jQuery("<a><b><c></c></b></a>");
form.find("b").data('scope', scope);
var b = form.find('b');
assertTrue(scope === b.scope());
};
ScopeTest.prototype.testNoScopeDoesNotCauseInfiniteRecursion = function(){
var form = jQuery("<a><b><c></c></b></a>");
var c = form.find('c');
assertTrue(!c.scope());
};
ScopeTest.prototype.testScopeEval = function(){
var scope = new nglr.Scope({b:345});
assertEquals(scope.eval('b = 123'), 123);
assertEquals(scope.get('b'), 123);
};
ScopeTest.prototype.testScopeFromPrototype = function(){
var scope = new nglr.Scope({b:123});
scope.eval('a = b');
scope.eval('b = 456');
assertEquals(scope.get('a'), 123);
assertEquals(scope.get('b'), 456);
};
ScopeTest.prototype.testSetScopeGet = function(){
var scope = new nglr.Scope();
scope.set('a', 987);
assertEquals(scope.get('a'), 987);
assertEquals(scope.eval('a'), 987);
};
ScopeTest.prototype.testGetChain = function(){
var scope = new nglr.Scope({a:{b:987}});
assertEquals(scope.get('a.b'), 987);
assertEquals(scope.eval('a.b'), 987);
};
ScopeTest.prototype.testGetUndefinedChain = function(){
var scope = new nglr.Scope();
assertEquals(typeof scope.get('a.b'), 'undefined');
};
ScopeTest.prototype.testSetChain = function(){
var scope = new nglr.Scope({a:{}});
scope.set('a.b', 987);
assertEquals(scope.get('a.b'), 987);
assertEquals(scope.eval('a.b'), 987);
};
ScopeTest.prototype.testSetGetOnChain = function(){
var scope = new nglr.Scope();
scope.set('a.b', 987);
assertEquals(scope.get('a.b'), 987);
assertEquals(scope.eval('a.b'), 987);
};
ScopeTest.prototype.testGlobalFunctionAccess =function(){
window['scopeAddTest'] = function (a, b) {return a+b;};
var scope = new nglr.Scope({window:window});
assertEquals(scope.eval('window.scopeAddTest(1,2)'), 3);
scope.set('add', function (a, b) {return a+b;});
assertEquals(scope.eval('add(1,2)'), 3);
scope.set('math.add', function (a, b) {return a+b;});
assertEquals(scope.eval('math.add(1,2)'), 3);
};
ScopeTest.prototype.testValidationEval = function(){
expectAsserts(4);
var scope = new nglr.Scope();
angular.validator.testValidator = function(value, expect){
assertEquals(scope, this.scope);
return value == expect ? null : "Error text";
};
assertEquals("Error text", scope.validate("testValidator:'abc'", 'x'));
assertEquals(null, scope.validate("testValidator:'abc'", 'abc'));
delete angular.validator['testValidator'];
};
ScopeTest.prototype.testCallingNonExistantMethodShouldProduceFriendlyException = function() {
expectAsserts(1);
var scope = new nglr.Scope({obj:{}});
try {
scope.eval("obj.iDontExist()");
fail();
} catch (e) {
assertEquals("Expression 'obj.iDontExist' is not a function.", e);
}
};
ScopeTest.prototype.testAccessingWithInvalidPathShouldThrowError = function() {
var scope = new nglr.Scope();
try {
scope.get('a.{{b}}');
fail();
} catch (e) {
assertEquals("Expression 'a.{{b}}' is not a valid expression for accesing variables.", e);
}
};
ScopeTest.prototype.testItShouldHave$parent = function() {
var parent = new nglr.Scope({}, "ROOT");
var child = new nglr.Scope(parent.state);
assertSame("parent", child.state.$parent, parent.state);
assertSame("root", child.state.$root, parent.state);
};
ScopeTest.prototype.testItShouldHave$root = function() {
var scope = new nglr.Scope({}, "ROOT");
assertSame(scope.state.$root, scope.state);
};
ScopeTest.prototype.testItShouldBuildPathOnUndefined = function(){
var scope = new nglr.Scope({}, "ROOT");
scope.setEval("a.$b.c", 1);
assertJsonEquals({$b:{c:1}}, scope.get("a"));
};
ScopeTest.prototype.testItShouldMapUnderscoreFunctions = function(){
var scope = new nglr.Scope({}, "ROOT");
scope.set("a", [1,2,3]);
assertEquals('function', typeof scope.get("a.$size"));
scope.eval("a.$includeIf(4,true)");
assertEquals(4, scope.get("a.$size")());
assertEquals(4, scope.eval("a.$size()"));
assertEquals('undefined', typeof scope.get("a.dontExist"));
};

42
test/ServerTest.js Normal file
View file

@ -0,0 +1,42 @@
ServerTest = TestCase("ServerTest");
ServerTest.prototype.testBreakLargeRequestIntoPackets = function() {
var log = "";
var server = new nglr.Server("http://server", function(url){
log += "|" + url;
});
server.maxSize = 30;
server.uuid = "uuid";
server.request("POST", "/data/database", {}, function(code, r){
assertEquals(200, code);
assertEquals("response", r);
});
nglr.uuid0("response");
assertEquals(
"|http://server/$/uuid0/2/1?h=eyJtIjoiUE9TVCIsInAiOnt9LCJ1Ij" +
"|http://server/$/uuid0/2/2?h=oiL2RhdGEvZGF0YWJhc2UifQ==",
log);
};
ServerTest.prototype.testItShouldEncodeUsingUrlRules = function() {
var server = new nglr.Server("http://server");
assertEquals("fn5-fn5-", server.base64url("~~~~~~"));
assertEquals("fn5_fn5_", server.base64url("~~\u007f~~\u007f"));
};
FrameServerTest = TestCase("FrameServerTest");
FrameServerTest.prototype = {
testRead:function(){
var window = {name:'$DATASET:"MyData"'};
var server = new nglr.FrameServer(window);
server.read();
assertEquals("MyData", server.data);
},
testWrite:function(){
var window = {};
var server = new nglr.FrameServer(window);
server.data = "TestData"
server.write();
assertEquals('$DATASET:"TestData"', window.name);
}
};

26
test/UsersTest.js Normal file
View file

@ -0,0 +1,26 @@
// Copyright (C) 2008,2009 BRAT Tech LLC
UsersTest = TestCase("UsersTest");
UsersTest.prototype = {
setUp:function(){},
tearDown:function(){},
testItShouldFetchCurrentUser:function(){
expectAsserts(5);
var user;
var users = new nglr.Users({request:function(method, url, request, callback){
assertEquals("GET", method);
assertEquals("/account.json", url);
assertEquals("{}", nglr.toJson(request));
callback(200, {$status_code:200, user:{name:'misko'}});
}});
users.fetchCurrentUser(function(u){
user = u;
assertEquals("misko", u.name);
assertEquals("misko", users.current.name);
});
}
};

65
test/ValidatorsTest.js Normal file
View file

@ -0,0 +1,65 @@
ValidatorTest = TestCase('ValidatorTest');
ValidatorTest.prototype.testRegexp = function() {
assertEquals(angular.validator.regexp("abc", /x/, "E1"), "E1");
assertEquals(angular.validator.regexp("abc", '/x/'),
"Value does not match expected format /x/.");
assertEquals(angular.validator.regexp("ab", '^ab$'), null);
assertEquals(angular.validator.regexp("ab", '^axb$', "E3"), "E3");
};
ValidatorTest.prototype.testNumber = function() {
assertEquals(angular.validator.number("ab"), "Value is not a number.");
assertEquals(angular.validator.number("-0.1",0), "Value can not be less than 0.");
assertEquals(angular.validator.number("10.1",0,10), "Value can not be greater than 10.");
assertEquals(angular.validator.number("1.2"), null);
assertEquals(angular.validator.number(" 1 ", 1, 1), null);
};
ValidatorTest.prototype.testInteger = function() {
assertEquals(angular.validator.integer("ab"), "Value is not a number.");
assertEquals(angular.validator.integer("1.1"), "Value is not a whole number.");
assertEquals(angular.validator.integer("-1",0), "Value can not be less than 0.");
assertEquals(angular.validator.integer("11",0,10), "Value can not be greater than 10.");
assertEquals(angular.validator.integer("1"), null);
assertEquals(angular.validator.integer(" 1 ", 1, 1), null);
};
ValidatorTest.prototype.testDate = function() {
var error = "Value is not a date. (Expecting format: 12/31/2009).";
assertEquals(angular.validator.date("ab"), error);
assertEquals(angular.validator.date("12/31/2009"), null);
};
ValidatorTest.prototype.testPhone = function() {
var error = "Phone number needs to be in 1(987)654-3210 format in North America or +999 (123) 45678 906 internationaly.";
assertEquals(angular.validator.phone("ab"), error);
assertEquals(null, angular.validator.phone("1(408)757-3023"));
assertEquals(null, angular.validator.phone("+421 (0905) 933 297"));
assertEquals(null, angular.validator.phone("+421 0905 933 297"));
};
ValidatorTest.prototype.testSSN = function() {
var error = "SSN needs to be in 999-99-9999 format.";
assertEquals(angular.validator.ssn("ab"), error);
assertEquals(angular.validator.ssn("123-45-6789"), null);
};
ValidatorTest.prototype.testURL = function() {
var error = "URL needs to be in http://server[:port]/path format.";
assertEquals(angular.validator.url("ab"), error);
assertEquals(angular.validator.url("http://server:123/path"), null);
};
ValidatorTest.prototype.testEmail = function() {
var error = "Email needs to be in username@host.com format.";
assertEquals(error, angular.validator.email("ab"));
assertEquals(null, angular.validator.email("misko@hevery.com"));
};
ValidatorTest.prototype.testJson = function() {
assertNotNull(angular.validator.json("'"));
assertNotNull(angular.validator.json("''X"));
assertNull(angular.validator.json("{}"));
};

269
test/WidgetsTest.js Normal file
View file

@ -0,0 +1,269 @@
WidgetTest = TestCase('WidgetTest');
WidgetTest.prototype.testRequired = function () {
var view = $('<input name="a" ng-required>');
var scope = new nglr.Scope({$invalidWidgets:[]});
var cntl = new nglr.TextController(view[0], 'a');
cntl.updateView(scope);
assertTrue(view.hasClass('ng-validation-error'));
assertEquals("Required Value", view.attr('ng-error'));
scope.set('a', 'A');
cntl.updateView(scope);
assertFalse(view.hasClass('ng-validation-error'));
assertEquals("undefined", typeof view.attr('ng-error'));
};
WidgetTest.prototype.testValidator = function () {
var view = $('<input name="a" ng-validate="testValidator:\'ABC\'">');
var scope = new nglr.Scope({$invalidWidgets:[]});
var cntl = new nglr.TextController(view[0], 'a');
angular.validator.testValidator = function(value, expect){
return value == expect ? null : "Error text";
};
scope.set('a', '');
cntl.updateView(scope);
assertEquals(view.hasClass('ng-validation-error'), false);
assertEquals(null, view.attr('ng-error'));
scope.set('a', 'X');
cntl.updateView(scope);
assertEquals(view.hasClass('ng-validation-error'), true);
assertEquals(view.attr('ng-error'), "Error text");
assertEquals("Error text", view.attr('ng-error'));
scope.set('a', 'ABC');
cntl.updateView(scope);
assertEquals(view.hasClass('ng-validation-error'), false);
assertEquals(view.attr('ng-error'), null);
assertEquals(null, view.attr('ng-error'));
delete angular.validator['testValidator'];
};
WidgetTest.prototype.testRequiredValidator = function () {
var view = $('<input name="a" ng-required ng-validate="testValidator:\'ABC\'">');
var scope = new nglr.Scope({$invalidWidgets:[]});
var cntl = new nglr.TextController(view[0], 'a');
angular.validator.testValidator = function(value, expect){
return value == expect ? null : "Error text";
};
scope.set('a', '');
cntl.updateView(scope);
assertEquals(view.hasClass('ng-validation-error'), true);
assertEquals("Required Value", view.attr('ng-error'));
scope.set('a', 'X');
cntl.updateView(scope);
assertEquals(view.hasClass('ng-validation-error'), true);
assertEquals("Error text", view.attr('ng-error'));
scope.set('a', 'ABC');
cntl.updateView(scope);
assertEquals(view.hasClass('ng-validation-error'), false);
assertEquals(null, view.attr('ng-error'));
delete angular.validator['testValidator'];
};
TextController = TestCase("TextController");
TextController.prototype.testDatePicker = function() {
var input = $('<input type="text" ng-widget="datepicker">');
input.data('scope', new nglr.Scope());
var body = $(document.body);
body.append(input);
var binder = new nglr.Binder(input[0], new nglr.WidgetFactory());
assertTrue('before', input.data('datepicker') === undefined);
binder.compile();
assertTrue('after', input.data('datepicker') !== null);
assertTrue(body.html(), input.hasClass('hasDatepicker'));
};
RepeaterUpdater = TestCase("RepeaterUpdater");
RepeaterUpdater.prototype.testRemoveThenAdd = function() {
var view = $("<div><span/></div>");
var template = function () {
return $("<li/>");
};
var repeater = new nglr.RepeaterUpdater(view.find("span"), "a in b", template, "");
var scope = new nglr.Scope();
scope.set('b', [1,2]);
repeater.updateView(scope);
scope.set('b', []);
repeater.updateView(scope);
scope.set('b', [1]);
repeater.updateView(scope);
assertEquals(1, view.find("li").size());
};
RepeaterUpdater.prototype.testShouldBindWidgetOnRepeaterClone = function(){
//fail();
};
RepeaterUpdater.prototype.testShouldThrowInformativeSyntaxError= function(){
expectAsserts(1);
try {
var repeater = new nglr.RepeaterUpdater(null, "a=b");
} catch (e) {
assertEquals("Expected ng-repeat in form of 'item in collection' but got 'a=b'.", e);
}
};
SelectControllerTest = TestCase("SelectControllerTest");
SelectControllerTest.prototype.testShouldUpdateModelNullOnNothingSelected = function(){
var scope = new nglr.Scope();
var view = {selectedIndex:-1, options:[]};
var cntl = new nglr.SelectController(view, 'abc');
cntl.updateModel(scope);
assertNull(scope.get('abc'));
};
SelectControllerTest.prototype.testShouldUpdateModelWhenNothingSelected = function(){
var scope = new nglr.Scope();
var view = {value:'123'};
var cntl = new nglr.SelectController(view, 'abc');
cntl.updateView(scope);
assertEquals("123", scope.get('abc'));
};
BindUpdaterTest = TestCase("BindUpdaterTest");
BindUpdaterTest.prototype.testShouldDisplayNothingForUndefined = function () {
var view = $('<span />');
var controller = new nglr.BindUpdater(view[0], "{{a}}");
var scope = new nglr.Scope();
scope.set('a', undefined);
controller.updateView(scope);
assertEquals("", view.text());
scope.set('a', null);
controller.updateView(scope);
assertEquals("", view.text());
};
BindUpdaterTest.prototype.testShouldDisplayJsonForNonStrings = function () {
var view = $('<span />');
var controller = new nglr.BindUpdater(view[0], "{{obj}}");
controller.updateView(new nglr.Scope({obj:[]}));
assertEquals("[]", view.text());
controller.updateView(new nglr.Scope({obj:{text:'abc'}}));
assertEquals('abc', nglr.fromJson(view.text()).text);
};
BindUpdaterTest.prototype.testShouldInsertHtmlNode = function () {
var view = $('<span />');
var controller = new nglr.BindUpdater(view[0], "<fake>&{{obj}}</fake>");
var scope = new nglr.Scope();
scope.set("obj", $('<div>myDiv</div>')[0]);
controller.updateView(scope);
assertEquals("<fake>&myDiv</fake>", view.text());
};
BindUpdaterTest.prototype.testShouldDisplayTextMethod = function () {
var view = $('<div />');
var controller = new nglr.BindUpdater(view[0], "{{obj}}");
var scope = new nglr.Scope();
scope.set("obj", new angular.filter.Meta({text:function(){return "abc";}}));
controller.updateView(scope);
assertEquals("abc", view.text());
scope.set("obj", new angular.filter.Meta({text:"123"}));
controller.updateView(scope);
assertEquals("123", view.text());
scope.set("obj", {text:"123"});
controller.updateView(scope);
assertEquals("123", nglr.fromJson(view.text()).text);
};
BindUpdaterTest.prototype.testShouldDisplayHtmlMethod = function () {
var view = $('<div />');
var controller = new nglr.BindUpdater(view[0], "{{obj}}");
var scope = new nglr.Scope();
scope.set("obj", new angular.filter.Meta({html:function(){return "a<div>b</div>c";}}));
controller.updateView(scope);
assertEquals("abc", view.text());
scope.set("obj", new angular.filter.Meta({html:"1<div>2</div>3"}));
controller.updateView(scope);
assertEquals("123", view.text());
scope.set("obj", {html:"123"});
controller.updateView(scope);
assertEquals("123", nglr.fromJson(view.text()).html);
};
BindUpdaterTest.prototype.testUdateBoolean = function() {
var view = $('<div />');
var controller = new nglr.BindUpdater(view[0], "{{true}}, {{false}}");
controller.updateView(new nglr.Scope());
assertEquals('true, false', view.text());
};
BindAttrUpdaterTest = TestCase("BindAttrUpdaterTest");
BindAttrUpdaterTest.prototype.testShouldLoadBlankImageWhenBindingIsUndefined = function () {
var view = $('<img />');
var controller = new nglr.BindAttrUpdater(view[0], {src: '{{imageUrl}}'});
var scope = new nglr.Scope();
scope.set('imageUrl', undefined);
scope.set('config.server', 'http://server');
controller.updateView(scope);
assertEquals("http://server/images/blank.gif", view.attr('src'));
};
RepeaterUpdaterTest = TestCase("RepeaterUpdaterTest");
RepeaterUpdaterTest.prototype.testShouldNotDieWhenRepeatExpressionIsNull = function() {
var rep = new nglr.RepeaterUpdater(null, "$item in items", null, null);
var scope = new nglr.Scope();
scope.set('items', undefined);
rep.updateView(scope);
};
RepeaterUpdaterTest.prototype.testShouldIterateOverKeys = function() {
var rep = new nglr.RepeaterUpdater(null, "($k,_v) in items", null, null);
assertEquals("items", rep.iteratorExp);
assertEquals("_v", rep.valueExp);
assertEquals("$k", rep.keyExp);
};
EvalUpdaterTest = TestCase("EvalUpdaterTest");
EvalUpdaterTest.prototype.testEvalThrowsException = function(){
var view = $('<div/>');
var eval = new nglr.EvalUpdater(view[0], 'undefined()');
eval.updateView(new nglr.Scope());
assertTrue(!!view.attr('ng-error'));
assertTrue(view.hasClass('ng-exception'));
eval.exp = "1";
eval.updateView(new nglr.Scope());
assertFalse(!!view.attr('ng-error'));
assertFalse(view.hasClass('ng-exception'));
};
RadioControllerTest = TestCase("RadioController");
RadioControllerTest.prototype.testItShouldTreatTrueStringAsBoolean = function () {
var view = $('<input type="radio" name="select" value="true"/>');
var radio = new nglr.RadioController(view[0], 'select');
var scope = new nglr.Scope({select:true});
radio.updateView(scope);
assertTrue(view[0].checked);
};

47
test/XSitePostTest.js Normal file
View file

@ -0,0 +1,47 @@
XSitePost = TestCase("XSitePost");
var e = function(text){ return Base64.encode(text); };
XSitePost.prototype.testMessageReceived = function () {
expectAsserts(4);
var xPost = new nglr.XSitePost();
xPost.baseUrl = "http://getangular.test";
xPost.post = function(url, request, callback){
assertEquals('http://getangular.test/url', url);
assertEquals('abc', request.a);
assertEquals('xyz', request.x);
};
xPost.incomingFragment('#id;0;1;'+e('/url')+':a:'+e('abc')+':x:'+e('xyz'));
assertEquals('{}', nglr.toJson(xPost.inQueue));
};
XSitePost.prototype.testMessageReceivedInParts = function () {
expectAsserts(5);
var xPost = new nglr.XSitePost();
xPost.baseUrl = "http://getangular.test";
xPost.post = function(url, request, callback){
assertEquals('http://getangular.test/url', url);
assertEquals('abc', request.a);
assertEquals('xyz', request.x);
};
xPost.incomingFragment('#id;1;2;:x:'+e('xyz'));
assertNotSame('{}', nglr.toJson(xPost.inQueue));
xPost.incomingFragment('#id;0;2;'+e('/url')+':a:'+e('abc'));
assertEquals('{}', nglr.toJson(xPost.inQueue));
};
XSitePost.prototype.testPostResponsIsEnqueued = function () {
var xPost = new nglr.XSitePost();
xPost.maxMsgSize = 11;
xPost.response("id", "response", "status");
assertEquals('["id:0:2:cmVzcG9uc2U","id:1:2:="]',
nglr.toJson(xPost.outQueue));
};
XSitePost.prototype.testPush = function () {
var window = {};
var xPost = new nglr.XSitePost(window);
xPost.response("id", "response", "status");
assertEquals('id:0:1:cmVzcG9uc2U=', xPost.outQueue[0]);
};

22
test/formsTest.js Normal file
View file

@ -0,0 +1,22 @@
nglrTest = TestCase('nglrTest');
nglrTest.prototype.testShiftBind = function(){
expectAsserts(3);
nglr.shiftBind('this', function(target, arg) {
assertEquals(this, 'this');
assertEquals(target, 'target');
assertEquals(arg, 'arg');
}).apply('target', ['arg']);
};
nglrTest.prototype.testBind = function(){
expectAsserts(2);
nglr.bind('this', function(arg) {
assertEquals(this, 'this');
assertEquals(arg, 'arg');
}).apply('XXX', ['arg']);
};

7
test/test/StepsTest.js Normal file
View file

@ -0,0 +1,7 @@
StepsTest = TestCase("StepsTest");
StepsTest.prototype.testGivenDataset=function(){
var self = {frame:{}, dataset:[]};
angular.test.GIVEN.dataset.call(self);
assertEquals('$DATASET:{"dataset":[]}', self.frame.name);
};

129
test/testabilityPatch.js Normal file
View file

@ -0,0 +1,129 @@
TestCase = function(name) { return jstestdriver.testCaseManager.TestCase(name); };
HIDDEN = jQuery.browser.msie ?
'' :
jQuery.browser.safari ?
' style="display: none; "' :
' style="display: none;"';
nglr.msie = jQuery.browser.msie;
nglr.alert = function(msg) {jstestdriver.console.log("ALERT: " + msg);};
function noop(){}
jstd = jstestdriver;
function html(content) {
return jQuery("<div></div>").html(content);
}
function report(reportTest){
$("#tests").children().each(function(i){
var success = this.className == "pass";
var strong = this.firstChild;
var msg = strong.firstChild.nodeValue;
var parts = msg.split(" module: ");
var module = parts[0];
var name = parts[1].replace(/ *$/, "");
reportTest(success, module, name, this.nodeValue);
});
}
MockUrlWatcher = function() {
this.url = "http://server";
};
MockUrlWatcher.prototype.getUrl = function(){
return this.url;
};
MockUrlWatcher.prototype.setUrl = function(url){
this.url = url;
};
jQuery.fn.sortedHtml = function() {
var html = "";
var toString = function(index, node) {
node = node || this;
if (node.nodeName == "#text") {
html += nglr.escapeHtml(node.nodeValue);
} else {
html += '<' + node.nodeName.toLowerCase();
var attributes = node.attributes || [];
var attrs = [];
for(var i=0; i<attributes.length; i++) {
var attr = attributes[i];
if(attr.name.match(/^ng-/) ||
attr.value &&
attr.value !='null' &&
attr.value !='auto' &&
attr.value !='false' &&
attr.value !='inherit' &&
attr.value !='0' &&
attr.name !='loop' &&
attr.name !='maxLength' &&
attr.name !='size' &&
attr.name !='start' &&
attr.name !='tabIndex' &&
attr.name.substr(0, 6) != 'jQuery') {
// in IE we need to check for all of these.
attrs.push(' ' + attr.name + '="' + attr.value + '"');
}
}
attrs.sort();
html += attrs.join('');
html += '>';
var children = node.childNodes;
for(var j=0; j<children.length; j++) {
toString(j, children[j]);
}
html += '</' + node.nodeName.toLowerCase() + '>';
}
};
this.children().each(toString);
return html;
};
function encode64(obj){
return Base64.encode(nglr.toJson(obj));
}
function decode64(base64){
return nglr.fromJson(Base64.decode(base64));
}
nglr.Loader.prototype.configureJQueryPlugins();
function assertHidden(node) {
var display = node.css('display');
assertEquals("Node should be hidden but vas visible: " + node.sortedHtml(), 'none', display);
}
function assertVisible(node) {
var display = node.css('display');
if (display == 'block') display = "";
assertEquals("Node should be visible but vas hidden: " + node.sortedHtml(), '', display);
}
function assertJsonEquals(expected, actual) {
assertEquals(nglr.toJson(expected), nglr.toJson(actual));
}
function assertUndefined(value) {
assertEquals('undefined', typeof value);
}
function assertDefined(value) {
assertTrue(nglr.toJson(value), !!value);
}
function assertThrows(error, fn){
var exception = null;
try {
fn();
} catch(e) {
exception = e;
}
if (!exception) {
fail("Expecting exception, none thrown");
}
assertEquals(error, exception);
}