From c9c176a53b1632ca2b1c6ed27382ab72ac21d45d Mon Sep 17 00:00:00 2001 From: Adam Abrons Date: Tue, 5 Jan 2010 16:36:58 -0800 Subject: angular.js --- LICENSE | 2 + README.md | 3 + Rakefile | 74 + angular.js | 128 + jsTestDriver.conf | 16 + lib/compiler-closure/COPYING | 202 ++ lib/compiler-closure/README | 193 ++ lib/compiler-closure/compiler.jar | Bin 0 -> 4237729 bytes lib/jquery/jquery-1.3.2.js | 4376 ++++++++++++++++++++++++++++++ lib/jquery/jquery-ui-1.7.1.custom.min.js | 77 + lib/jsl/jsl | Bin 0 -> 519636 bytes lib/jsl/jsl.default.conf | 128 + lib/jstestdriver/JsTestDriver.jar | Bin 0 -> 3065205 bytes lib/shrinksafe/js.jar | Bin 0 -> 756883 bytes lib/shrinksafe/shrinksafe.jar | Bin 0 -> 17685 bytes lib/swfobject/swfobject.js | 4 + lib/underscore/underscore.js | 609 +++++ lib/webtoolkit/webtoolkit.base64.js | 142 + src/API.js | 318 +++ src/Binder.js | 341 +++ src/ControlBar.js | 71 + src/DataStore.js | 332 +++ src/Filters.js | 290 ++ src/JSON.js | 92 + src/Loader.js | 389 +++ src/Model.js | 65 + src/Parser.js | 741 +++++ src/Scope.js | 198 ++ src/Server.js | 69 + src/Users.js | 36 + src/Validators.js | 80 + src/Widgets.js | 774 ++++++ src/Widgets.js.orig | 764 ++++++ src/XSitePost.js | 100 + src/angular-bootstrap.js | 100 + src/test/Runner.js | 160 ++ src/test/Steps.js | 57 + src/test/_namespace.js | 5 + test/ApiTest.js | 252 ++ test/Base64Test.js | 5 + test/BinderTest.js | 1001 +++++++ test/ConsoleTest.js | 13 + test/ControlBarTest.js | 2 + test/DataStoreTest.js | 617 +++++ test/EntityDeclarationTest.js | 46 + test/FileControllerTest.js | 98 + test/FiltersTest.js | 153 ++ test/JsonTest.js | 69 + test/LoaderTest.js | 70 + test/ModelTest.js | 84 + test/ParserTest.js | 462 ++++ test/ScopeTest.js | 144 + test/ServerTest.js | 42 + test/UsersTest.js | 26 + test/ValidatorsTest.js | 65 + test/WidgetsTest.js | 269 ++ test/XSitePostTest.js | 47 + test/formsTest.js | 22 + test/test/StepsTest.js | 7 + test/testabilityPatch.js | 129 + 60 files changed, 14559 insertions(+) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 Rakefile create mode 100644 angular.js create mode 100644 jsTestDriver.conf create mode 100644 lib/compiler-closure/COPYING create mode 100644 lib/compiler-closure/README create mode 100644 lib/compiler-closure/compiler.jar create mode 100644 lib/jquery/jquery-1.3.2.js create mode 100644 lib/jquery/jquery-ui-1.7.1.custom.min.js create mode 100755 lib/jsl/jsl create mode 100755 lib/jsl/jsl.default.conf create mode 100644 lib/jstestdriver/JsTestDriver.jar create mode 100644 lib/shrinksafe/js.jar create mode 100644 lib/shrinksafe/shrinksafe.jar create mode 100644 lib/swfobject/swfobject.js create mode 100644 lib/underscore/underscore.js create mode 100644 lib/webtoolkit/webtoolkit.base64.js create mode 100644 src/API.js create mode 100644 src/Binder.js create mode 100644 src/ControlBar.js create mode 100644 src/DataStore.js create mode 100644 src/Filters.js create mode 100644 src/JSON.js create mode 100644 src/Loader.js create mode 100644 src/Model.js create mode 100644 src/Parser.js create mode 100644 src/Scope.js create mode 100644 src/Server.js create mode 100644 src/Users.js create mode 100644 src/Validators.js create mode 100644 src/Widgets.js create mode 100644 src/Widgets.js.orig create mode 100644 src/XSitePost.js create mode 100644 src/angular-bootstrap.js create mode 100644 src/test/Runner.js create mode 100644 src/test/Steps.js create mode 100644 src/test/_namespace.js create mode 100644 test/ApiTest.js create mode 100644 test/Base64Test.js create mode 100644 test/BinderTest.js create mode 100644 test/ConsoleTest.js create mode 100644 test/ControlBarTest.js create mode 100644 test/DataStoreTest.js create mode 100644 test/EntityDeclarationTest.js create mode 100644 test/FileControllerTest.js create mode 100644 test/FiltersTest.js create mode 100644 test/JsonTest.js create mode 100644 test/LoaderTest.js create mode 100644 test/ModelTest.js create mode 100644 test/ParserTest.js create mode 100644 test/ScopeTest.js create mode 100644 test/ServerTest.js create mode 100644 test/UsersTest.js create mode 100644 test/ValidatorsTest.js create mode 100644 test/WidgetsTest.js create mode 100644 test/XSitePostTest.js create mode 100644 test/formsTest.js create mode 100644 test/test/StepsTest.js create mode 100644 test/testabilityPatch.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5be7e4b9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +TODO +apache or MIT probably diff --git a/README.md b/README.md new file mode 100644 index 00000000..41fa9038 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +Angular +====== +TODO diff --git a/Rakefile b/Rakefile new file mode 100644 index 00000000..9fb7f173 --- /dev/null +++ b/Rakefile @@ -0,0 +1,74 @@ +include FileUtils + +desc 'Compile JavaScript' +task :compile do + compiled = %x(java -jar lib/shrinksafe/shrinksafe.jar \ + lib/webtoolkit/webtoolkit.base64.js \ + lib/underscore/underscore.js \ + src/Loader.js \ + src/API.js \ + src/Binder.js \ + src/ControlBar.js \ + src/DataStore.js \ + src/Filters.js \ + src/JSON.js \ + src/Model.js \ + src/Parser.js \ + src/Scope.js \ + src/Server.js \ + src/Users.js \ + src/Validators.js \ + src/Widgets.js \ + src/angular-bootstrap.js \ + ) + f = File.new("angular.js", 'w') + f.write(compiled) + f.close +end + +desc 'Compile JavaScript with Google Closure Compiler' +task :compileclosure do + %x(java -jar lib/compiler-closure/compiler.jar \ + --compilation_level ADVANCED_OPTIMIZATIONS \ + --js lib/webtoolkit/webtoolkit.base64.js \ + --js lib/underscore/underscore.js \ + --js src/Loader.js \ + --js src/API.js \ + --js src/Binder.js \ + --js src/ControlBar.js \ + --js src/DataStore.js \ + --js src/Filters.js \ + --js src/JSON.js \ + --js src/Model.js \ + --js src/Parser.js \ + --js src/Scope.js \ + --js src/Server.js \ + --js src/Users.js \ + --js src/Validators.js \ + --js src/Widgets.js \ + --js src/angular-bootstrap.js \ + --js_output_file angular.js) +end + +namespace :server do + desc 'Run JsTestDriver Server' + task :start do + sh %x(java -jar lib/jstestdriver/JsTestDriver.jar --browser open --port 9876) + end + + desc "Run JavaScript tests against the server" + task :test do + sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all) + end +end + +desc "Run JavaScript tests" +task :test do + sh %(java -jar lib/jstestdriver/JsTestDriver.jar --tests all --browser open --port 9876) +end + +desc 'Lint' +task :lint do + out = %x(lib/jsl/jsl -conf lib/jsl/jsl.default.conf) + print out +end diff --git a/angular.js b/angular.js new file mode 100644 index 00000000..0c19175d --- /dev/null +++ b/angular.js @@ -0,0 +1,128 @@ +function r(){return function(a){return a}}function v(){return function(){}}function x(a){return function(b){this[a]=b}}function z(a){return function(){return a}}var A; +function aa(a){var b="",c,e,d,f,i,j,n=0;a=a;a=a.replace(/\r\n/g,"\n");e="";for(d=0;d127&&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>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=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;sp?1:0}),"value")};d.yf=function(g,h,k){k=k||d.ka;for(var l=0,p=g.length;l>1;k(g[s])=0})})};d.Pf=function(){for(var g=d.D(arguments),h=d.max(d.vb(g,"length")),k=new Array(h),l=0;l0?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)[^\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/g,">")};nglr.hd=function(a){if(!a||!a.replace)return a;return a.replace(//g,">").replace(/\"/g,""")}; +nglr.u=function(a,b){if(!a)throw"Missing this";if(!_.P(b))throw"Missing function";return function(){return b.apply(a,arguments)}};nglr.Qd=function(a,b){return function(){for(var c=[this],e=0;e=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-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=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-1;){c1||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;i2&&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("')};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;ce;--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("
"+c+"
"+a+"
");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='
loading....
';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<\/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;w1)if(!window.document.getElementById(t[1]))continue;o=window.document.getElementById(y)}}e.Wc&&o&&window.Lb.nd(o);if(typeof a==="function")try{a.apply(this,arguments)}catch(g){}}})(window.onload); diff --git a/jsTestDriver.conf b/jsTestDriver.conf new file mode 100644 index 00000000..77a5f0bf --- /dev/null +++ b/jsTestDriver.conf @@ -0,0 +1,16 @@ +server: http://localhost:9876 + +load: + - lib/swfobject/swfobject.js + - lib/webtoolkit/webtoolkit.base64.js + - lib/jquery/jquery-1.3.2.js + - lib/jquery/jquery-ui-1.7.1.custom.min.js + - lib/underscore/underscore.js + - src/Loader.js + - src/*.js + - src/test/_namespace.js + - src/test/*.js + - test/testabilityPatch.js + - test/test/*.js + - test/*.js + \ No newline at end of file diff --git a/lib/compiler-closure/COPYING b/lib/compiler-closure/COPYING new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/lib/compiler-closure/COPYING @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/lib/compiler-closure/README b/lib/compiler-closure/README new file mode 100644 index 00000000..af4e6106 --- /dev/null +++ b/lib/compiler-closure/README @@ -0,0 +1,193 @@ +/* + * Copyright 2009 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// +// Contents +// + +The Closure Compiler performs checking, instrumentation, and +optimizations on JavaScript code. The purpose of this README is to +explain how to build and run the Closure Compiler. + +The Closure Compiler requires Java 6 or higher. +http://www.java.com/ + + +// +// Building The Closure Compiler +// + +There are three ways to get a Closure Compiler executable. + +1) Use one we built for you. + +Pre-built Closure binaries can be found at +http://code.google.com/p/closure-compiler/downloads/list + + +2) Check out the source and build it with Apache Ant. + +First, check out the full source tree of the Closure Compiler. There +are instructions on how to do this at the project site. +http://code.google.com/p/closure-compiler/source/checkout + +Apache Ant is a cross-platform build tool. +http://ant.apache.org/ + +At the root of the source tree, there is an Ant file named +build.xml. To use it, navigate to the same directory and type the +command + +ant jar + +This will produce a jar file called "build/compiler.jar". + + +3) Check out the source and build it with Eclipse. + +Eclipse is a cross-platform IDE. +http://www.eclipse.org/ + +Under Eclipse's File menu, click "New > Project ..." and create a +"Java Project." You will see an options screen. Give the project a +name, select "Create project from existing source," and choose the +root of the checked-out source tree as the existing directory. Verify +that you are using JRE version 6 or higher. + +Eclipse can use the build.xml file to discover rules. When you +navigate to the build.xml file, you will see all the build rules in +the "Outline" pane. Run the "jar" rule to build the compiler in +build/compiler.jar. + + +// +// Running The Closure Compiler +// + +Once you have the jar binary, running the Closure Compiler is straightforward. + +On the command line, type + +java -jar compiler.jar + +This starts the compiler in interactive mode. Type + +var x = 17 + 25; + +then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux) +and "Enter" again. The Compiler will respond: + +var x=42; + +The Closure Compiler has many options for reading input from a file, +writing output to a file, checking your code, and running +optimizations. To learn more, type + +java -jar compiler.jar --help + +You can read more detailed documentation about the many flags at +http://code.google.com/closure/compiler/docs/gettingstarted_app.html + + +// +// Compiling Multiple Scripts +// + +If you have multiple scripts, you should compile them all together with +one compile command. + +java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js + +The Closure Compiler will concatenate the files in the order they're +passed at the command line. + +If you need to compile many, many scripts together, you may start to +run into problems with managing dependencies between scripts. You +should check out the Closure Library. It contains functions for +enforcing dependencies between scripts, and a tool called calcdeps.py +that knows how to give scripts to the Closure Compiler in the right +order. + +http://code.google.com/p/closure-library/ + +// +// Licensing +// + +Unless otherwise stated, all source files are licensed under +the Apache License, Version 2.0. + + +----- +Code under: +src/com/google/javascript/rhino +test/com/google/javascript/rhino + +URL: http://www.mozilla.org/rhino +Version: 1.5R3, with heavy modifications +License: Netscape Public License and MPL / GPL dual license + +Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an +implementation of JavaScript for the JVM. The JavaScript parser and +the parse tree data structures were extracted and modified +significantly for use by Google's JavaScript compiler. + +Local Modifications: The packages have been renamespaced. All code not +relavant to parsing has been removed. A JSDoc parser and static typing +system have been added. + + +----- +Code in: +lib/libtrunk_rhino_parser_jarjared.jar + +URL: http://www.mozilla.org/rhino +Version: Trunk +License: Netscape Public License and MPL / GPL dual license + +Description: Mozilla Rhino is an implementation of JavaScript for the JVM. + +Local Modifications: None. We've used JarJar to renamespace the code +post-compilation. See: +http://code.google.com/p/jarjar/ + + +----- +Code in: +lib/google_common.jar + +URL: http://code.google.com/p/guava-libraries/ +Version: Trunk +License: Apache License 2.0 + +Description: Google's core Java libraries. + +Local Modifications: None. + + +---- +Code in: +lib/junit.jar + +URL: http://sourceforge.net/projects/junit/ +Version: 3.8.1 +License: Common Public License 1.0 + +Description: A framework for writing and running automated tests in Java. + +Local Modifications: None. + + diff --git a/lib/compiler-closure/compiler.jar b/lib/compiler-closure/compiler.jar new file mode 100644 index 00000000..da053a7d Binary files /dev/null and b/lib/compiler-closure/compiler.jar differ diff --git a/lib/jquery/jquery-1.3.2.js b/lib/jquery/jquery-1.3.2.js new file mode 100644 index 00000000..d7093948 --- /dev/null +++ b/lib/jquery/jquery-1.3.2.js @@ -0,0 +1,4376 @@ +/*! + * jQuery JavaScript Library v1.3.2 + * http://jquery.com/ + * + * Copyright (c) 2009 John Resig + * Dual licensed under the MIT and GPL licenses. + * http://docs.jquery.com/License + * + * Date: 2009-02-19 17:34:21 -0500 (Thu, 19 Feb 2009) + * Revision: 6246 + */ +(function(){ + +var + // Will speed up references to window, and allows munging its name. + window = this, + // Will speed up references to undefined, and allows munging its name. + undefined, + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + // Map over the $ in case of overwrite + _$ = window.$, + + jQuery = window.jQuery = window.$ = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); + }, + + // A simple way to check for HTML strings or ID strings + // (both of which we optimize for) + quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/, + // Is it a simple selector + isSimple = /^.[^:#\[\.,]*$/; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + // Make sure that a selection was provided + selector = selector || document; + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this[0] = selector; + this.length = 1; + this.context = selector; + return this; + } + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + var match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) + selector = jQuery.clean( [ match[1] ], context ); + + // HANDLE: $("#id") + else { + var elem = document.getElementById( match[3] ); + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem && elem.id != match[3] ) + return jQuery().find( selector ); + + // Otherwise, we inject the element directly into the jQuery object + var ret = jQuery( elem || [] ); + ret.context = document; + ret.selector = selector; + return ret; + } + + // HANDLE: $(expr, [context]) + // (which is just equivalent to: $(content).find(expr) + } else + return jQuery( context ).find( selector ); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) + return jQuery( document ).ready( selector ); + + // Make sure that old selector state is passed along + if ( selector.selector && selector.context ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return this.setArray(jQuery.isArray( selector ) ? + selector : + jQuery.makeArray(selector)); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.3.2", + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num === undefined ? + + // Return a 'clean' array + Array.prototype.slice.call( this ) : + + // Return just the object + this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = jQuery( elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) + ret.selector = this.selector + (this.selector ? " " : "") + selector; + else if ( name ) + ret.selector = this.selector + "." + name + "(" + selector + ")"; + + // Return the newly-formed element set + return ret; + }, + + // Force the current matched set of elements to become + // the specified array of elements (destroying the stack in the process) + // You should use pushStack() in order to do this, but maintain the stack + setArray: function( elems ) { + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + this.length = 0; + Array.prototype.push.apply( this, elems ); + + return this; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem && elem.jquery ? elem[0] : elem + , this ); + }, + + attr: function( name, value, type ) { + var options = name; + + // Look for the case where we're accessing a style value + if ( typeof name === "string" ) + if ( value === undefined ) + return this[0] && jQuery[ type || "attr" ]( this[0], name ); + + else { + options = {}; + options[ name ] = value; + } + + // Check to see if we're setting style values + return this.each(function(i){ + // Set all the styles + for ( name in options ) + jQuery.attr( + type ? + this.style : + this, + name, jQuery.prop( this, options[ name ], type, i, name ) + ); + }); + }, + + css: function( key, value ) { + // ignore negative width and height values + if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) + value = undefined; + return this.attr( key, value, "curCSS" ); + }, + + text: function( text ) { + if ( typeof text !== "object" && text != null ) + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + + var ret = ""; + + jQuery.each( text || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + ret += this.nodeType != 1 ? + this.nodeValue : + jQuery.fn.text( [ this ] ); + }); + }); + + return ret; + }, + + wrapAll: function( html ) { + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).clone(); + + if ( this[0].parentNode ) + wrap.insertBefore( this[0] ); + + wrap.map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }).append(this); + } + + return this; + }, + + wrapInner: function( html ) { + return this.each(function(){ + jQuery( this ).contents().wrapAll( html ); + }); + }, + + wrap: function( html ) { + return this.each(function(){ + jQuery( this ).wrapAll( html ); + }); + }, + + append: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.appendChild( elem ); + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function(elem){ + if (this.nodeType == 1) + this.insertBefore( elem, this.firstChild ); + }); + }, + + before: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this ); + }); + }, + + after: function() { + return this.domManip(arguments, false, function(elem){ + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + }, + + end: function() { + return this.prevObject || jQuery( [] ); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: [].push, + sort: [].sort, + splice: [].splice, + + find: function( selector ) { + if ( this.length === 1 ) { + var ret = this.pushStack( [], "find", selector ); + ret.length = 0; + jQuery.find( selector, this[0], ret ); + return ret; + } else { + return this.pushStack( jQuery.unique(jQuery.map(this, function(elem){ + return jQuery.find( selector, elem ); + })), "find", selector ); + } + }, + + clone: function( events ) { + // Do the clone + var ret = this.map(function(){ + if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) { + // IE copies events bound via attachEvent when + // using cloneNode. Calling detachEvent on the + // clone will also remove the events from the orignal + // In order to get around this, we use innerHTML. + // Unfortunately, this means some modifications to + // attributes in IE that are actually only stored + // as properties will not be copied (such as the + // the name attribute on an input). + var html = this.outerHTML; + if ( !html ) { + var div = this.ownerDocument.createElement("div"); + div.appendChild( this.cloneNode(true) ); + html = div.innerHTML; + } + + return jQuery.clean([html.replace(/ jQuery\d+="(?:\d+|null)"/g, "").replace(/^\s*/, "")])[0]; + } else + return this.cloneNode(true); + }); + + // Copy the events from the original to the clone + if ( events === true ) { + var orig = this.find("*").andSelf(), i = 0; + + ret.find("*").andSelf().each(function(){ + if ( this.nodeName !== orig[i].nodeName ) + return; + + var events = jQuery.data( orig[i], "events" ); + + for ( var type in events ) { + for ( var handler in events[ type ] ) { + jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data ); + } + } + + i++; + }); + } + + // Return the cloned set + return ret; + }, + + filter: function( selector ) { + return this.pushStack( + jQuery.isFunction( selector ) && + jQuery.grep(this, function(elem, i){ + return selector.call( elem, i ); + }) || + + jQuery.multiFilter( selector, jQuery.grep(this, function(elem){ + return elem.nodeType === 1; + }) ), "filter", selector ); + }, + + closest: function( selector ) { + var pos = jQuery.expr.match.POS.test( selector ) ? jQuery(selector) : null, + closer = 0; + + return this.map(function(){ + var cur = this; + while ( cur && cur.ownerDocument ) { + if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selector) ) { + jQuery.data(cur, "closest", closer); + return cur; + } + cur = cur.parentNode; + closer++; + } + }); + }, + + not: function( selector ) { + if ( typeof selector === "string" ) + // test special case where just one selector is passed in + if ( isSimple.test( selector ) ) + return this.pushStack( jQuery.multiFilter( selector, this, true ), "not", selector ); + else + selector = jQuery.multiFilter( selector, this ); + + var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; + return this.filter(function() { + return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; + }); + }, + + add: function( selector ) { + return this.pushStack( jQuery.unique( jQuery.merge( + this.get(), + typeof selector === "string" ? + jQuery( selector ) : + jQuery.makeArray( selector ) + ))); + }, + + is: function( selector ) { + return !!selector && jQuery.multiFilter( selector, this ).length > 0; + }, + + hasClass: function( selector ) { + return !!selector && this.is( "." + selector ); + }, + + val: function( value ) { + if ( value === undefined ) { + var elem = this[0]; + + if ( elem ) { + if( jQuery.nodeName( elem, 'option' ) ) + return (elem.attributes.value || {}).specified ? elem.value : elem.text; + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type == "select-one"; + + // Nothing was selected + if ( index < 0 ) + return null; + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery(option).val(); + + // We don't need an array for one selects + if ( one ) + return value; + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + } + + // Everything else, we just grab the value + return (elem.value || "").replace(/\r/g, ""); + + } + + return undefined; + } + + if ( typeof value === "number" ) + value += ''; + + return this.each(function(){ + if ( this.nodeType != 1 ) + return; + + if ( jQuery.isArray(value) && /radio|checkbox/.test( this.type ) ) + this.checked = (jQuery.inArray(this.value, value) >= 0 || + jQuery.inArray(this.name, value) >= 0); + + else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(value); + + jQuery( "option", this ).each(function(){ + this.selected = (jQuery.inArray( this.value, values ) >= 0 || + jQuery.inArray( this.text, values ) >= 0); + }); + + if ( !values.length ) + this.selectedIndex = -1; + + } else + this.value = value; + }); + }, + + html: function( value ) { + return value === undefined ? + (this[0] ? + this[0].innerHTML.replace(/ jQuery\d+="(?:\d+|null)"/g, "") : + null) : + this.empty().append( value ); + }, + + replaceWith: function( value ) { + return this.after( value ).remove(); + }, + + eq: function( i ) { + return this.slice( i, +i + 1 ); + }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ), + "slice", Array.prototype.slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function(elem, i){ + return callback.call( elem, i, elem ); + })); + }, + + andSelf: function() { + return this.add( this.prevObject ); + }, + + domManip: function( args, table, callback ) { + if ( this[0] ) { + var fragment = (this[0].ownerDocument || this[0]).createDocumentFragment(), + scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment ), + first = fragment.firstChild; + + if ( first ) + for ( var i = 0, l = this.length; i < l; i++ ) + callback.call( root(this[i], first), this.length > 1 || i > 0 ? + fragment.cloneNode(true) : fragment ); + + if ( scripts ) + jQuery.each( scripts, evalScript ); + } + + return this; + + function root( elem, cur ) { + return table && jQuery.nodeName(elem, "table") && jQuery.nodeName(cur, "tr") ? + (elem.getElementsByTagName("tbody")[0] || + elem.appendChild(elem.ownerDocument.createElement("tbody"))) : + elem; + } + } +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +function evalScript( i, elem ) { + if ( elem.src ) + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + + else + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + + if ( elem.parentNode ) + elem.parentNode.removeChild( elem ); +} + +function now(){ + return +new Date; +} + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) + target = {}; + + // extend jQuery itself if only one argument is passed + if ( length == i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) + // Extend the base object + for ( var name in options ) { + var src = target[ name ], copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) + continue; + + // Recurse if we're merging object values + if ( deep && copy && typeof copy === "object" && !copy.nodeType ) + target[ name ] = jQuery.extend( deep, + // Never move original objects, clone them + src || ( copy.length != null ? [ ] : { } ) + , copy ); + + // Don't bring in undefined values + else if ( copy !== undefined ) + target[ name ] = copy; + + } + + // Return the modified object + return target; +}; + +// exclude the following css properties to add px +var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, + // cache defaultView + defaultView = document.defaultView || {}, + toString = Object.prototype.toString; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) + window.jQuery = _jQuery; + + return jQuery; + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return toString.call(obj) === "[object Function]"; + }, + + isArray: function( obj ) { + return toString.call(obj) === "[object Array]"; + }, + + // check if an element is in a (or is an) XML document + isXMLDoc: function( elem ) { + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && jQuery.isXMLDoc( elem.ownerDocument ); + }, + + // Evalulates a script in a global context + globalEval: function( data ) { + if ( data && /\S/.test(data) ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + if ( jQuery.support.scriptEval ) + script.appendChild( document.createTextNode( data ) ); + else + script.text = data; + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, length = object.length; + + if ( args ) { + if ( length === undefined ) { + for ( name in object ) + if ( callback.apply( object[ name ], args ) === false ) + break; + } else + for ( ; i < length; ) + if ( callback.apply( object[ i++ ], args ) === false ) + break; + + // A special, fast, case for the most common use of each + } else { + if ( length === undefined ) { + for ( name in object ) + if ( callback.call( object[ name ], name, object[ name ] ) === false ) + break; + } else + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} + } + + return object; + }, + + prop: function( elem, value, type, i, name ) { + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, i ); + + // Handle passing in a number to a CSS property + return typeof value === "number" && type == "curCSS" && !exclude.test( name ) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, classNames ) { + jQuery.each((classNames || "").split(/\s+/), function(i, className){ + if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) + elem.className += (elem.className ? " " : "") + className; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, classNames ) { + if (elem.nodeType == 1) + elem.className = classNames !== undefined ? + jQuery.grep(elem.className.split(/\s+/), function(className){ + return !jQuery.className.has( classNames, className ); + }).join(" ") : + ""; + }, + + // internal only, use hasClass("class") + has: function( elem, className ) { + return elem && jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; + } + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var old = {}; + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( var name in options ) + elem.style[ name ] = old[ name ]; + }, + + css: function( elem, name, force, extra ) { + if ( name == "width" || name == "height" ) { + var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; + + function getWH() { + val = name == "width" ? elem.offsetWidth : elem.offsetHeight; + + if ( extra === "border" ) + return; + + jQuery.each( which, function() { + if ( !extra ) + val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + if ( extra === "margin" ) + val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0; + else + val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + }); + } + + if ( elem.offsetWidth !== 0 ) + getWH(); + else + jQuery.swap( elem, props, getWH ); + + return Math.max(0, Math.round(val)); + } + + return jQuery.curCSS( elem, name, force ); + }, + + curCSS: function( elem, name, force ) { + var ret, style = elem.style; + + // We need to handle opacity special in IE + if ( name == "opacity" && !jQuery.support.opacity ) { + ret = jQuery.attr( style, "opacity" ); + + return ret == "" ? + "1" : + ret; + } + + // Make sure we're using the right name for getting the float value + if ( name.match( /float/i ) ) + name = styleFloat; + + if ( !force && style && style[ name ] ) + ret = style[ name ]; + + else if ( defaultView.getComputedStyle ) { + + // Only "float" is needed here + if ( name.match( /float/i ) ) + name = "float"; + + name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); + + var computedStyle = defaultView.getComputedStyle( elem, null ); + + if ( computedStyle ) + ret = computedStyle.getPropertyValue( name ); + + // We should always get a number back from opacity + if ( name == "opacity" && ret == "" ) + ret = "1"; + + } else if ( elem.currentStyle ) { + var camelCase = name.replace(/\-(\w)/g, function(all, letter){ + return letter.toUpperCase(); + }); + + ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { + // Remember the original values + var left = style.left, rsLeft = elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + elem.runtimeStyle.left = elem.currentStyle.left; + style.left = ret || 0; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + elem.runtimeStyle.left = rsLeft; + } + } + + return ret; + }, + + clean: function( elems, context, fragment ) { + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) { + var match = /^<(\w+)\s*\/?>$/.exec(elems[0]); + if ( match ) + return [ context.createElement( match[1] ) ]; + } + + var ret = [], scripts = [], div = context.createElement("div"); + + jQuery.each(elems, function(i, elem){ + if ( typeof elem === "number" ) + elem += ''; + + if ( !elem ) + return; + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? + all : + front + ">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var tags = elem.replace(/^\s+/, "").substring(0, 10).toLowerCase(); + + var wrap = + // option or optgroup + !tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
" ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and '); + }; + + if (scriptConfig.autoLoadDependencies) { + addScript("/javascripts/webtoolkit.base64.js"); + addScript("/javascripts/swfobject.js"); + addScript("/javascripts/jQuery/jquery-1.3.2.js"); + addScript("/javascripts/jQuery/jquery-ui-1.7.1.custom.min.js"); + addScript("/javascripts/underscore/underscore.js"); + addScript("/javascripts/nglr/Loader.js"); + addScript("/javascripts/nglr/API.js"); + addScript("/javascripts/nglr/Binder.js"); + addScript("/javascripts/nglr/ControlBar.js"); + addScript("/javascripts/nglr/DataStore.js"); + addScript("/javascripts/nglr/Filters.js"); + addScript("/javascripts/nglr/JSON.js"); + addScript("/javascripts/nglr/Model.js"); + addScript("/javascripts/nglr/Parser.js"); + addScript("/javascripts/nglr/Scope.js"); + addScript("/javascripts/nglr/Server.js"); + addScript("/javascripts/nglr/Users.js"); + addScript("/javascripts/nglr/Validators.js"); + addScript("/javascripts/nglr/Widgets.js"); + } else { + addScript("/ajax/libs/swfobject/2.2/swfobject.js", "http://ajax.googleapis.com"); + addScript("/ajax/libs/jquery/1.3.2/jquery.min.js", "http://ajax.googleapis.com"); + addScript("/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js", "http://ajax.googleapis.com"); + } + + window.onload = function() { + window.angular.init = function(root, config){ + var cnfgMerged = _.clone(scriptConfig||{}); + _.extend(cnfgMerged, config); + new nglr.Loader(root, jQuery("head"), cnfgMerged).load(); + }; + + var doc = window.document; + if (scriptConfig.bindRootId) { + doc = null; + var ids = scriptConfig.bindRootId.split('|'); + for ( var i = 0; i < ids.length && !doc; i++) { + var idCond = ids[i].split('?'); + var id = idCond[0]; + if (idCond.length > 1) { + if (!window.document.getElementById(idCond[1])) { + continue; + } + } + doc = window.document.getElementById(id); + } + } + if (scriptConfig.autoBind && doc) { + window.angular.init(doc); + } + if (typeof previousOnLoad === 'function') { + try { + previousOnLoad.apply(this, arguments); + } catch (e) {} + } + }; +})(window.onload); + + diff --git a/src/test/Runner.js b/src/test/Runner.js new file mode 100644 index 00000000..478ef73e --- /dev/null +++ b/src/test/Runner.js @@ -0,0 +1,160 @@ +nglr.test.ScenarioRunner = function(scenarios, body) { + this.scenarios = scenarios; + this.body = body; +}; + +nglr.test.ScenarioRunner.prototype = { + run:function(){ + this.setUpUI(); + this.runScenarios(); + }, + setUpUI:function(){ + this.body.html( + '
' + + '
' + + '
' + + '
' + + '' + + '
'); + 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('
') + }; + current.run = current.scenario.append( + '
' + + '.' + + '.' + + '.' + + '').find(".run"); + current.log = current.scenario.append('
').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('').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('
'); + log.text(buf.join(" ")); + this.current.log.append(log); + this.console.scrollTop(this.console[0].scrollHeight); + if (level == "error") + this.current.error = buf.join(" "); + } +}; + +nglr.test.Scenario = function(name, scenario){ + this.name = name; + this.scenario = scenario; +}; +nglr.test.Scenario.prototype = { + run:function(runner, callback) { + var self = this; + _.stepper(this.scenario, function(next, steps, name){ + if (name.charAt(0) == '$') { + next(); + } else { + runner.start(self.name + "::" + name); + var allSteps = (self.scenario.$before||[]).concat(steps); + _.stepper(allSteps, function(next, step){ + self.executeStep(runner, step, next); + }, function(){ + runner.end(); + next(); + }); + } + }, callback); + }, + verb:function(step){ + var fn = null; + if (!step) fn = function (){ throw "Step is null!"; } + else if (step.Given) fn = angular.test.GIVEN[step.Given]; + else if (step.When) fn = angular.test.WHEN[step.When]; + else if (step.Then) fn = angular.test.THEN[step.Then]; + return fn || function (){ + throw "ERROR: Need Given/When/Then got: " + nglr.toJson(step); + }; + }, + context: function(runner) { + var frame = runner.frame; + var window = frame[0].contentWindow; + var document; + if (window.jQuery) + document = window.jQuery(window.document); + var context = { + frame:frame, + window:window, + log:_.bind(runner.log, runner, "info"), + document:document, + assert:function(element, path){ + if (element.size() != 1) { + throw "Expected to find '1' found '"+ + element.size()+"' for '"+path+"'."; + } + return element; + }, + element:function(path){ + var exp = path.replace("{{","[ng-bind=").replace("}}", "]"); + var element = document.find(exp); + return context.assert(element, path); + } + }; + return context; + }, + executeStep:function(runner, step, callback) { + if (!step) { + callback(); + return; + } + runner.log("info", nglr.toJson(step)); + var fn = this.verb(step); + var context = this.context(runner); + _.extend(context, step); + try { + (fn.call(context)||function(c){c();})(callback); + } catch (e) { + runner.log("error", "ERROR: " + nglr.toJson(e)); + } + } +}; diff --git a/src/test/Steps.js b/src/test/Steps.js new file mode 100644 index 00000000..af4b84d6 --- /dev/null +++ b/src/test/Steps.js @@ -0,0 +1,57 @@ +angular.test.GIVEN = { + browser:function(){ + var self = this; + if (jQuery.browser.safari && this.frame.attr('src') == this.at) { + this.window.location.reload(); + } else { + this.frame.attr('src', this.at); + } + return function(done){ + self.frame.load(function(){ + self.frame.unbind(); + done(); + }); + }; + }, + dataset:function(){ + this.frame.name="$DATASET:" + nglr.toJson({dataset:this.dataset}); + } +}; +angular.test.WHEN = { + enter:function(){ + var element = this.element(this.at); + element.attr('value', this.text); + element.change(); + }, + click:function(){ + var element = this.element(this.at); + var input = element[0]; + // emulate the browser behavior which causes it + // to be overridden at the end. + var checked = input.checked = !input.checked; + element.click(); + input.checked = checked; + }, + select:function(){ + var element = this.element(this.at); + var path = "option[value=" + this.option + "]"; + var option = this.assert(element.find(path)); + option[0].selected = !option[0].selected; + element.change(); + } +}; +angular.test.THEN = { + text:function(){ + var element = this.element(this.at); + if (typeof this.should_be != undefined ) { + var should_be = this.should_be; + if (_.isArray(this.should_be)) + should_be = JSON.stringify(should_be); + if (element.text() != should_be) + throw "Expected " + should_be + + " but was " + element.text() + "."; + } + }, + drainRequestQueue:function(){ + } +}; diff --git a/src/test/_namespace.js b/src/test/_namespace.js new file mode 100644 index 00000000..78f430f1 --- /dev/null +++ b/src/test/_namespace.js @@ -0,0 +1,5 @@ +if (!angular) angular = {}; +if (!angular.test) angular.test = {}; +if (!angular.test.GIVEN) angular.test.GIVEN = {}; +if (!angular.test.WHEN) angular.test.WHEN = {}; +if (!angular.test.THEN) angular.test.THEN = {}; diff --git a/test/ApiTest.js b/test/ApiTest.js new file mode 100644 index 00000000..250a27b1 --- /dev/null +++ b/test/ApiTest.js @@ -0,0 +1,252 @@ +ApiTest = TestCase("ApiTest"); + +ApiTest.prototype.testItShouldReturnTypeOf = function (){ + assertEquals("undefined", angular.Object.typeOf(undefined)); + assertEquals("null", angular.Object.typeOf(null)); + assertEquals("object", angular.Collection.typeOf({})); + assertEquals("array", angular.Array.typeOf([])); + assertEquals("string", angular.Object.typeOf("")); + assertEquals("date", angular.Object.typeOf(new Date())); + assertEquals("element", angular.Object.typeOf(document.body)); + assertEquals("function", angular.Object.typeOf(function(){})); +}; + +ApiTest.prototype.testItShouldReturnSize = function(){ + assertEquals(0, angular.Collection.size({})); + assertEquals(1, angular.Collection.size({a:"b"})); + assertEquals(0, angular.Object.size({})); + assertEquals(1, angular.Array.size([0])); +}; + +ApiTest.prototype.testIncludeIf = function() { + var array = []; + var obj = {}; + + angular.Array.includeIf(array, obj, true); + angular.Array.includeIf(array, obj, true); + assertTrue(_.include(array, obj)); + assertEquals(1, array.length); + + angular.Array.includeIf(array, obj, false); + assertFalse(_.include(array, obj)); + assertEquals(0, array.length); + + angular.Array.includeIf(array, obj, 'x'); + assertTrue(_.include(array, obj)); + assertEquals(1, array.length); + angular.Array.includeIf(array, obj, ''); + assertFalse(_.include(array, obj)); + assertEquals(0, array.length); +}; + +ApiTest.prototype.testSum = function(){ + assertEquals(3, angular.Array.sum([{a:"1"}, {a:"2"}], 'a')); +}; + +ApiTest.prototype.testSumContainingNaN = function(){ + assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], 'a')); + assertEquals(1, angular.Array.sum([{a:1}, {a:Number.NaN}], function($){return $.a;})); +}; + +ApiTest.prototype.testInclude = function(){ + assertTrue(angular.Array.include(['a'], 'a')); + assertTrue(angular.Array.include(['a', 'b'], 'a')); + assertTrue(!angular.Array.include(['c'], 'a')); + assertTrue(!angular.Array.include(['c', 'b'], 'a')); +}; + +ApiTest.prototype.testIndex = function(){ + assertEquals(angular.Array.indexOf(['a'], 'a'), 0); + assertEquals(angular.Array.indexOf(['a', 'b'], 'a'), 0); + assertEquals(angular.Array.indexOf(['b', 'a'], 'a'), 1); + assertEquals(angular.Array.indexOf(['b', 'b'],'x'), -1); +}; + +ApiTest.prototype.testRemove = function(){ + var items = ['a', 'b', 'c']; + assertEquals(angular.Array.remove(items, 'q'), 'q'); + assertEquals(items.length, 3); + + assertEquals(angular.Array.remove(items, 'b'), 'b'); + assertEquals(items.length, 2); + + assertEquals(angular.Array.remove(items, 'a'), 'a'); + assertEquals(items.length, 1); + + assertEquals(angular.Array.remove(items, 'c'), 'c'); + assertEquals(items.length, 0); + + assertEquals(angular.Array.remove(items, 'q'), 'q'); + assertEquals(items.length, 0); +}; + +ApiTest.prototype.testFindById = function() { + var items = [{$id:1}, {$id:2}, {$id:3}]; + assertNull(angular.Array.findById(items, 0)); + assertEquals(items[0], angular.Array.findById(items, 1)); + assertEquals(items[1], angular.Array.findById(items, 2)); + assertEquals(items[2], angular.Array.findById(items, 3)); +}; + +ApiTest.prototype.testFilter = function() { + var items = ["MIsKO", {name:"shyam"}, ["adam"], 1234]; + assertEquals(4, angular.Array.filter(items, "").length); + assertEquals(4, angular.Array.filter(items, undefined).length); + + assertEquals(1, angular.Array.filter(items, 'iSk').length); + assertEquals("MIsKO", angular.Array.filter(items, 'isk')[0]); + + assertEquals(1, angular.Array.filter(items, 'yam').length); + assertEquals(items[1], angular.Array.filter(items, 'yam')[0]); + + assertEquals(1, angular.Array.filter(items, 'da').length); + assertEquals(items[2], angular.Array.filter(items, 'da')[0]); + + assertEquals(1, angular.Array.filter(items, '34').length); + assertEquals(1234, angular.Array.filter(items, '34')[0]); + + assertEquals(0, angular.Array.filter(items, "I don't exist").length); +}; + +ApiTest.prototype.testShouldNotFilterOnSystemData = function() { + assertEquals("", "".charAt(0)); // assumption + var items = [{$name:"misko"}]; + assertEquals(0, angular.Array.filter(items, "misko").length); +}; + +ApiTest.prototype.testFilterOnSpecificProperty = function() { + var items = [{ignore:"a", name:"a"}, {ignore:"a", name:"abc"}]; + assertEquals(2, angular.Array.filter(items, {}).length); + + assertEquals(2, angular.Array.filter(items, {name:'a'}).length); + + assertEquals(1, angular.Array.filter(items, {name:'b'}).length); + assertEquals("abc", angular.Array.filter(items, {name:'b'})[0].name); +}; + +ApiTest.prototype.testFilterOnFunction = function() { + var items = [{name:"a"}, {name:"abc", done:true}]; + assertEquals(1, angular.Array.filter(items, function(i){return i.done;}).length); +}; + +ApiTest.prototype.testFilterIsAndFunction = function() { + var items = [{first:"misko", last:"hevery"}, + {first:"adam", last:"abrons"}]; + + assertEquals(2, angular.Array.filter(items, {first:'', last:''}).length); + assertEquals(1, angular.Array.filter(items, {first:'', last:'hevery'}).length); + assertEquals(0, angular.Array.filter(items, {first:'adam', last:'hevery'}).length); + assertEquals(1, angular.Array.filter(items, {first:'misko', last:'hevery'}).length); + assertEquals(items[0], angular.Array.filter(items, {first:'misko', last:'hevery'})[0]); +}; + +ApiTest.prototype.testFilterNot = function() { + var items = ["misko", "adam"]; + + assertEquals(1, angular.Array.filter(items, '!isk').length); + assertEquals(items[1], angular.Array.filter(items, '!isk')[0]); +}; + +ApiTest.prototype.testAdd = function() { + var add = angular.Array.add; + assertJsonEquals([{}, "a"], add(add([]),"a")); +}; + +ApiTest.prototype.testCount = function() { + var array = [{name:'a'},{name:'b'},{name:''}]; + var obj = {}; + + assertEquals(3, angular.Array.count(array)); + assertEquals(2, angular.Array.count(array, 'name')); + assertEquals(1, angular.Array.count(array, 'name=="a"')); +}; + +ApiTest.prototype.testFind = function() { + var array = [{name:'a'},{name:'b'},{name:''}]; + var obj = {}; + + assertEquals(undefined, angular.Array.find(array, 'false')); + assertEquals('default', angular.Array.find(array, 'false', 'default')); + assertEquals('a', angular.Array.find(array, 'name == "a"').name); + assertEquals('', angular.Array.find(array, 'name == ""').name); +}; + +ApiTest.prototype.testItShouldSortArray = function() { + assertEquals([2,15], angular.Array.orderBy([15,2])); + assertEquals(["a","B", "c"], angular.Array.orderBy(["c","B", "a"])); + assertEquals([15,"2"], angular.Array.orderBy([15,"2"])); + assertEquals(["15","2"], angular.Array.orderBy(["15","2"])); + assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a')); + assertJsonEquals([{a:2},{a:15}], angular.Array.orderBy([{a:15},{a:2}], 'a', "F")); +}; + +ApiTest.prototype.testItShouldSortArrayInReverse = function() { + assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', true)); + assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "T")); + assertJsonEquals([{a:15},{a:2}], angular.Array.orderBy([{a:15},{a:2}], 'a', "reverse")); +}; + +ApiTest.prototype.testItShouldSortArrayByPredicate = function() { + assertJsonEquals([{a:2, b:1},{a:15, b:1}], + angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['a', 'b'])); + assertJsonEquals([{a:2, b:1},{a:15, b:1}], + angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['b', 'a'])); + assertJsonEquals([{a:15, b:1},{a:2, b:1}], + angular.Array.orderBy([{a:15, b:1},{a:2, b:1}], ['+b', '-a'])); +}; + +ApiTest.prototype.testQuoteString = function(){ + assertEquals(angular.String.quote('a'), '"a"'); + assertEquals(angular.String.quote('\\'), '"\\\\"'); + assertEquals(angular.String.quote("'a'"), '"\'a\'"'); + assertEquals(angular.String.quote('"a"'), '"\\"a\\""'); + assertEquals(angular.String.quote('\n\f\r\t'), '"\\n\\f\\r\\t"'); +}; + +ApiTest.prototype.testQuoteStringBug = function(){ + assertEquals(angular.String.quote('"7\\\\\\\"7"', "7\\\"7")); +}; + +ApiTest.prototype.testQuoteUnicode = function(){ + assertEquals('"abc\\u00a0def"', angular.String.quoteUnicode('abc\u00A0def')); +}; + +ApiTest.prototype.testMerge = function() { + var array = [{name:"misko"}]; + angular.Array.merge(array, 0, {name:"", email:"email1"}); + angular.Array.merge(array, 1, {name:"adam", email:"email2"}); + assertJsonEquals([{"email":"email1","name":"misko"},{"email":"email2","name":"adam"}], array); +}; + +ApiTest.prototype.testOrderByToggle = function() { + var orderByToggle = angular.Array.orderByToggle; + var predicate = []; + assertEquals(['+a'], orderByToggle(predicate, 'a')); + assertEquals(['-a'], orderByToggle(predicate, 'a')); + + assertEquals(['-a', '-b'], orderByToggle(['-b', 'a'], 'a')); +}; + +ApiTest.prototype.testOrderByToggle = function() { + var orderByDirection = angular.Array.orderByDirection; + assertEquals("", orderByDirection(['+a','b'], 'x')); + assertEquals("", orderByDirection(['+a','b'], 'b')); + assertEquals('ng-ascend', orderByDirection(['a','b'], 'a')); + assertEquals('ng-ascend', orderByDirection(['+a','b'], 'a')); + assertEquals('ng-descend', orderByDirection(['-a','b'], 'a')); + assertEquals('up', orderByDirection(['+a','b'], 'a', 'up', 'down')); + assertEquals('down', orderByDirection(['-a','b'], 'a', 'up', 'down')); +}; + +ApiTest.prototype.testDateToUTC = function(){ + var date = new Date("Sep 10 2003 13:02:03 GMT"); + assertEquals("date", angular.Object.typeOf(date)); + assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date)); +}; + +ApiTest.prototype.testStringFromUTC = function(){ + var date = angular.String.toDate("2003-09-10T13:02:03Z"); + assertEquals("date", angular.Object.typeOf(date)); + assertEquals("2003-09-10T13:02:03Z", angular.Date.toString(date)); + assertEquals("str", angular.String.toDate("str")); +}; diff --git a/test/Base64Test.js b/test/Base64Test.js new file mode 100644 index 00000000..a9353186 --- /dev/null +++ b/test/Base64Test.js @@ -0,0 +1,5 @@ +Base64Test = TestCase('Base64Test'); + +Base64Test.prototype.testEncodeDecode = function(){ + assertEquals(Base64.decode(Base64.encode('hello')), 'hello'); +}; diff --git a/test/BinderTest.js b/test/BinderTest.js new file mode 100644 index 00000000..d033996d --- /dev/null +++ b/test/BinderTest.js @@ -0,0 +1,1001 @@ +BinderTest = TestCase('BinderTest'); + +function compile(content, initialScope, config) { + var h = html(content); + config = config || {autoSubmit:true}; + var scope = new nglr.Scope(initialScope, "ROOT"); + h.data('scope', scope); + var binder = new nglr.Binder(h[0], new nglr.WidgetFactory(), new MockUrlWatcher(), config); + var datastore = new nglr.DataStore(); + scope.set("$datastore", datastore); + scope.set("$binder", binder); + scope.set("$anchor", binder.anchor); + binder.entity(scope); + binder.compile(); + return {node:h, binder:binder, scope:scope}; +} + +function compileToHtml(content) { + return compile(content).node.sortedHtml(); +} + + +BinderTest.prototype.testParseTextWithNoBindings = function(){ + var parts = nglr.Binder.parseBindings("a"); + assertEquals(parts.length, 1); + assertEquals(parts[0], "a"); + assertTrue(!nglr.Binder.binding(parts[0])); +}; + +BinderTest.prototype.testParseEmptyText = function(){ + var parts = nglr.Binder.parseBindings(""); + assertEquals(parts.length, 1); + assertEquals(parts[0], ""); + assertTrue(!nglr.Binder.binding(parts[0])); +}; + +BinderTest.prototype.testParseInnerBinding = function(){ + var parts = nglr.Binder.parseBindings("a{{b}}c"); + assertEquals(parts.length, 3); + assertEquals(parts[0], "a"); + assertTrue(!nglr.Binder.binding(parts[0])); + assertEquals(parts[1], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[1]), "b"); + assertEquals(parts[2], "c"); + assertTrue(!nglr.Binder.binding(parts[2])); +}; + +BinderTest.prototype.testParseEndingBinding = function(){ + var parts = nglr.Binder.parseBindings("a{{b}}"); + assertEquals(parts.length, 2); + assertEquals(parts[0], "a"); + assertTrue(!nglr.Binder.binding(parts[0])); + assertEquals(parts[1], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[1]), "b"); +}; + +BinderTest.prototype.testParseBeggingBinding = function(){ + var parts = nglr.Binder.parseBindings("{{b}}c"); + assertEquals(parts.length, 2); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); + assertEquals(parts[1], "c"); + assertTrue(!nglr.Binder.binding(parts[1])); +}; + +BinderTest.prototype.testParseLoanBinding = function(){ + var parts = nglr.Binder.parseBindings("{{b}}"); + assertEquals(parts.length, 1); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); +}; + +BinderTest.prototype.testParseTwoBindings = function(){ + var parts = nglr.Binder.parseBindings("{{b}}{{c}}"); + assertEquals(parts.length, 2); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); + assertEquals(parts[1], "{{c}}"); + assertEquals(nglr.Binder.binding(parts[1]), "c"); +}; + +BinderTest.prototype.testParseTwoBindingsWithTextInMiddle = function(){ + var parts = nglr.Binder.parseBindings("{{b}}x{{c}}"); + assertEquals(parts.length, 3); + assertEquals(parts[0], "{{b}}"); + assertEquals(nglr.Binder.binding(parts[0]), "b"); + assertEquals(parts[1], "x"); + assertTrue(!nglr.Binder.binding(parts[1])); + assertEquals(parts[2], "{{c}}"); + assertEquals(nglr.Binder.binding(parts[2]), "c"); +}; + +BinderTest.prototype.testParseMultiline = function(){ + var parts = nglr.Binder.parseBindings('"X\nY{{A\nB}}C\nD"'); + assertTrue(!!nglr.Binder.binding('{{A\nB}}')); + assertEquals(parts.length, 3); + assertEquals(parts[0], '"X\nY'); + assertEquals(parts[1], '{{A\nB}}'); + assertEquals(parts[2], 'C\nD"'); +}; + +BinderTest.prototype.testHasBinding = function(){ + assertTrue(nglr.Binder.hasBinding("{{a}}")); + assertTrue(!nglr.Binder.hasBinding("a")); + assertTrue(nglr.Binder.hasBinding("{{b}}x{{c}}")); +}; + + +BinderTest.prototype.tearDown = function(){ + jQuery("*", document).die(); + jQuery(document).unbind(); +}; + +BinderTest.prototype.testChangingTextfieldUpdatesModel = function(){ + var state = compile('', {model:{}}); + state.binder.updateView(); + assertEquals('abc', state.scope.get('model').price); +}; + +BinderTest.prototype.testChangingTextareaUpdatesModel = function(){ + var form = html(''); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals(scope.get('model').note, 'abc'); +}; + +BinderTest.prototype.testChangingRadioUpdatesModel = function(){ + var form = html('' + + ''); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals(scope.get('model').price, 'A'); +}; + +BinderTest.prototype.testChangingCheckboxUpdatesModel = function(){ + var form = html(''); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals('A', scope.get('model').price); +}; + +BinderTest.prototype.testBindUpdate = function() { + var c = compile('
'); + c.binder.updateView(); + assertEquals(123, c.scope.get('a')); +}; + +BinderTest.prototype.testChangingSelectNonSelectedUpdatesModel = function(){ + var form = html(''); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals('A', scope.get('model').price); +}; + +BinderTest.prototype.testChangingMultiselectUpdatesModel = function(){ + var form = html(''); + var scope = new nglr.Scope({Invoice:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertJsonEquals(["A", "B"], scope.get('Invoice').options); +}; + +BinderTest.prototype.testChangingSelectSelectedUpdatesModel = function(){ + var form = html(''); + var scope = new nglr.Scope({model:{}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), new nglr.WidgetFactory(), new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals(scope.get('model').price, 'b'); +}; + +BinderTest.prototype.testExecuteInitialization = function() { + var form = html('
'); + var scope = new nglr.Scope(); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0)); + binder.executeInit(); + assertEquals(scope.get('a'), 123); +}; + +BinderTest.prototype.testExecuteInitializationStatements = function() { + var form = html('
'); + var scope = new nglr.Scope(); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0)); + binder.executeInit(); + assertEquals(scope.get('a'), 123); + assertEquals(scope.get('b'), 345); +}; + +BinderTest.prototype.testApplyTextBindings = function(){ + var form = html('
x
'); + var scope = new nglr.Scope({model:{a:123}}); + form.data('scope', scope); + var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + assertEquals('123', form.text()); +}; + +BinderTest.prototype.testReplaceBindingInTextWithSpan = function() { + assertEquals(compileToHtml("a{{b}}c"), 'ac'); + assertEquals(compileToHtml("{{b}}"), ''); +}; + +BinderTest.prototype.testReplaceBindingCreatesCorrectNumberOfWidgets = function() { + var h = html("space{{a}}{{a}}a{{a}}{{a}}"); + h.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(h.get(0), new nglr.WidgetFactory()); + binder.compile(); + + assertEquals(4, h.scope().widgets.length); +}; + +BinderTest.prototype.testBindingSpaceConfusesIE = function() { + if (!nglr.msie) return; + var span = document.createElement("span"); + span.innerHTML = ' '; + var nbsp = span.firstChild.nodeValue; + assertEquals( + ''+nbsp+'', + compileToHtml("{{a}} {{b}}")); + assertEquals( + ''+nbsp+'x '+nbsp+'(', + compileToHtml("{{A}} x {{B}} ({{C}})")); +}; + +BinderTest.prototype.testBindingOfAttributes = function() { + var form = html(""); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var attrbinding = form.find("a").attr("ng-bind-attr"); + var bindings = nglr.fromJson(attrbinding); + assertEquals("http://s/a{{b}}c", decodeURI(bindings.href)); + assertTrue(!bindings.foo); +}; + +BinderTest.prototype.testMarkMultipleAttributes = function() { + var form = html(""); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var attrbinding = form.find("a").attr("ng-bind-attr"); + var bindings = nglr.fromJson(attrbinding); + assertEquals(decodeURI(bindings.href), "http://s/a{{b}}c"); + assertEquals(bindings.foo, "{{d}}"); +}; + +BinderTest.prototype.testAttributesNoneBound = function() { + var form = html(""); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var a = form.find("a"); + assertEquals(a.get(0).nodeName, "A"); + assertTrue(!a.attr("ng-bind-attr")); +}; + +BinderTest.prototype.testExistingAttrbindingIsAppended = function() { + var form = html(""); + form.data('scope', new nglr.Scope()); + var binder = new nglr.Binder(form.get(0)); + binder.compile(); + var a = form.find("a"); + assertEquals('{"b":"{{def}}","href":"http://s/{{abc}}"}', a.attr('ng-bind-attr')); +}; + +BinderTest.prototype.testAttributesAreEvaluated = function(){ + var form = html(''); + form.data('scope', new nglr.Scope({a:1, b:2})); + var binder = new nglr.Binder(form.get(0), null, new MockUrlWatcher()); + binder.compile(); + binder.updateView(); + var a = form.find("a"); + assertEquals(a.attr('a'), 'a'); + assertEquals(a.attr('b'), 'a+b=3'); +}; + +BinderTest.prototype.testInputsAreUpdated = function(){ + var form = + html('' + + '