├── .gitignore ├── README.md ├── app ├── css │ ├── .gitignore │ ├── bootstrap.css │ └── style.css ├── img │ ├── .gitignore │ ├── glyphicons-halflings-white.png │ └── glyphicons-halflings.png ├── index.html ├── js │ ├── Controllers │ │ └── controllers.js │ ├── Directives │ │ └── directives.js │ ├── Filters │ │ └── filters.js │ ├── Services │ │ └── services.js │ ├── app.js │ ├── lib │ │ ├── angular │ │ │ ├── angular.js │ │ │ ├── angular.min.js │ │ │ └── version.txt │ │ └── require.js │ └── main.js └── templates │ └── Main.html ├── config └── testacular.conf.js ├── package.json ├── scripts ├── test.bat ├── test.sh ├── watchr.rb └── web-server.js └── test ├── lib └── angular │ ├── angular-mocks.js │ └── angular-scenario.js ├── main.js ├── test.test.js └── unit ├── controllers.test.js ├── directives.test.js ├── filters.test.js └── services.test.js /.gitignore: -------------------------------------------------------------------------------- 1 | **/.DS_Store 2 | nbproject 3 | manifest.mf 4 | build.xml 5 | 6 | .project 7 | .settings 8 | .idea/* 9 | 10 | 11 | node_modules -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ======= 2 | Angularjs + RequireJs + Express Seed 3 | ====================== 4 | 5 | ## Directory Layout 6 | 7 | src/ --> all of the files to be used in production 8 | public/ --> AngularJs application 9 | css/ --> css files 10 | bootstrap.css --> bootstrap stylesheet 11 | style.css --> application stylesheet 12 | img/ --> image files 13 | index.html --> app layout file (the main html template file of the app) 14 | js/ --> javascript files 15 | Controllers --> application controllers 16 | Directives --> application directives 17 | filters --> custom angular filters 18 | Services --> custom angular services 19 | app.js --> application 20 | lib/ --> angular and 3rd party javascript libraries 21 | angular/ 22 | angular.js --> the latest angular js 23 | angular.min.js --> the latest minified angular js 24 | angular-*.js --> angular add-on modules 25 | version.txt --> version number 26 | require.js --> require.js library 27 | template/ --> angular view partials (partial html templates) 28 | Main.html 29 | 30 | config/testacular.conf.js --> config file for running unit tests with Testacular 31 | 32 | scripts/ --> handy shell/js/ruby scripts 33 | test.bat --> autotests unit tests with Testacular (windows) 34 | test.sh --> autotests unit tests with Testacular (*nix) 35 | 36 | test/ --> test source files and libraries 37 | lib/ 38 | angular/ --> angular testing libraries 39 | angular-mocks.js --> mocks that replace certain angular services in tests 40 | angular-scenario.js --> angular's scenario (end-to-end) test runner library 41 | unit/ --> unit level specs/tests 42 | controllers.test.js --> specs for controllers 43 | directives.test.js --> specs for directives 44 | filters.test.js --> specs for filters 45 | services.test.js --> specs for services -------------------------------------------------------------------------------- /app/css/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxdow/angularjs-requirejs-seed/694dd0f0fb371e425493f65f4469ef48caa94132/app/css/.gitignore -------------------------------------------------------------------------------- /app/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background: #e9e0e9; 3 | } -------------------------------------------------------------------------------- /app/img/.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxdow/angularjs-requirejs-seed/694dd0f0fb371e425493f65f4469ef48caa94132/app/img/.gitignore -------------------------------------------------------------------------------- /app/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxdow/angularjs-requirejs-seed/694dd0f0fb371e425493f65f4469ef48caa94132/app/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /app/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxdow/angularjs-requirejs-seed/694dd0f0fb371e425493f65f4469ef48caa94132/app/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Title 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/js/Controllers/controllers.js: -------------------------------------------------------------------------------- 1 | /* Controllers */ 2 | 3 | function MainCtrl($scope) { 4 | 5 | $scope.world = "World"; 6 | } -------------------------------------------------------------------------------- /app/js/Directives/directives.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | define([],function(){ 4 | 5 | var directives = {} ; 6 | 7 | 8 | directives.mydirective = function() { 9 | return { 10 | restrict : "EA", 11 | template : "myAmazingDirective", 12 | replace : true 13 | }; 14 | 15 | }; 16 | 17 | directives.appVersion = function(version){ 18 | return function(scope, elm, attrs) { 19 | elm.text(version); 20 | }; 21 | }; 22 | 23 | return directives; 24 | }); 25 | 26 | -------------------------------------------------------------------------------- /app/js/Filters/filters.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Filters */ 4 | 5 | define([],function() { 6 | 7 | function initialize(app) { 8 | app.filter(('interpolate'),["version",function(version){ 9 | return function(text) { 10 | return String(text).replace(/\%VERSION\%/mg, version); 11 | }; 12 | }]); 13 | } 14 | 15 | return {initialize : initialize}; 16 | }); 17 | -------------------------------------------------------------------------------- /app/js/Services/services.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Services */ 4 | 5 | define([],function(){ 6 | 7 | var services = {} ; 8 | services.version = function() { 9 | return "0.1" ; 10 | }; 11 | 12 | return services ; 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /app/js/app.js: -------------------------------------------------------------------------------- 1 | define( 2 | ["angular", 3 | "Services/services", 4 | "Directives/directives", 5 | "Filters/filters", 6 | "Controllers/controllers" 7 | ], 8 | 9 | function BaseManager(angular,Services,Directives,Filters){ 10 | var initialize = function () { 11 | 12 | var app = angular.module("myApp", [], function($routeProvider, $locationProvider) { 13 | 14 | $routeProvider.when('/', { 15 | templateUrl: '/templates/Main.html', 16 | controller: MainCtrl 17 | }); 18 | 19 | $routeProvider.otherwise( { redirectTo: '/'} ); 20 | 21 | $locationProvider.html5Mode(true); 22 | }); 23 | 24 | Filters.initialize(app); 25 | 26 | app.factory(Services); 27 | app.directive(Directives); 28 | 29 | angular.bootstrap(document,["myApp"]); 30 | 31 | }; 32 | return { 33 | initialize : initialize 34 | }; 35 | }); -------------------------------------------------------------------------------- /app/js/lib/angular/angular.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | AngularJS v1.0.7 3 | (c) 2010-2012 Google, Inc. http://angularjs.org 4 | License: MIT 5 | */ 6 | (function(P,T,q){'use strict';function m(b,a,c){var d;if(b)if(H(b))for(d in b)d!="prototype"&&d!="length"&&d!="name"&&b.hasOwnProperty(d)&&a.call(c,b[d],d);else if(b.forEach&&b.forEach!==m)b.forEach(a,c);else if(!b||typeof b.length!=="number"?0:typeof b.hasOwnProperty!="function"&&typeof b.constructor!="function"||b instanceof K||ca&&b instanceof ca||wa.call(b)!=="[object Object]"||typeof b.callee==="function")for(d=0;d=0&&b.splice(c,1);return a}function U(b,a){if(oa(b)||b&&b.$evalAsync&&b.$watch)throw Error("Can't copy Window or Scope");if(a){if(b===a)throw Error("Can't copy equivalent objects or arrays");if(E(b))for(var c=a.length=0;c2?ha.call(arguments,2):[];return H(a)&&!(a instanceof RegExp)?c.length?function(){return arguments.length?a.apply(b,c.concat(ha.call(arguments,0))):a.apply(b,c)}:function(){return arguments.length?a.apply(b,arguments):a.call(b)}:a}function ic(b,a){var c=a;/^\$+/.test(b)?c=q:oa(a)?c="$WINDOW":a&&T===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function da(b,a){return JSON.stringify(b, 13 | ic,a?" ":null)}function pb(b){return B(b)?JSON.parse(b):b}function Ua(b){b&&b.length!==0?(b=z(""+b),b=!(b=="f"||b=="0"||b=="false"||b=="no"||b=="n"||b=="[]")):b=!1;return b}function pa(b){b=u(b).clone();try{b.html("")}catch(a){}var c=u("
").append(b).html();try{return b[0].nodeType===3?z(c):c.match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+z(b)})}catch(d){return z(c)}}function Va(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]), 14 | a[d]=y(c[1])?decodeURIComponent(c[1]):!0)});return a}function qb(b){var a=[];m(b,function(b,d){a.push(Wa(d,!0)+(b===!0?"":"="+Wa(b,!0)))});return a.length?a.join("&"):""}function Xa(b){return Wa(b,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+")}function Wa(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,a?"%20":"+")}function jc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,h=["ng:app","ng-app","x-ng-app", 15 | "data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;m(h,function(a){h[a]=!0;c(T.getElementById(a));a=a.replace(":","\\:");b.querySelectorAll&&(m(b.querySelectorAll("."+a),c),m(b.querySelectorAll("."+a+"\\:"),c),m(b.querySelectorAll("["+a+"]"),c))});m(d,function(a){if(!e){var b=f.exec(" "+a.className+" ");b?(e=a,g=(b[2]||"").replace(/\s+/g,",")):m(a.attributes,function(b){if(!e&&h[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function rb(b,a){var c=function(){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement", 16 | b)}]);a.unshift("ng");var c=sb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,d){a.$apply(function(){b.data("$injector",d);c(b)(a)})}]);return c},d=/^NG_DEFER_BOOTSTRAP!/;if(P&&!d.test(P.name))return c();P.name=P.name.replace(d,"");Ya.resumeBootstrap=function(b){m(b,function(b){a.push(b)});c()}}function Za(b,a){a=a||"_";return b.replace(kc,function(b,d){return(d?a:"")+b.toLowerCase()})}function $a(b,a,c){if(!b)throw Error("Argument '"+(a||"?")+"' is "+(c||"required")); 17 | return b}function qa(b,a,c){c&&E(b)&&(b=b[b.length-1]);$a(H(b),a,"not a function, got "+(b&&typeof b=="object"?b.constructor.name||"Object":typeof b));return b}function lc(b){function a(a,b,e){return a[b]||(a[b]=e())}return a(a(b,"angular",Object),"module",function(){var b={};return function(d,e,g){e&&b.hasOwnProperty(d)&&(b[d]=null);return a(b,d,function(){function a(c,d,e){return function(){b[e||"push"]([c,d,arguments]);return k}}if(!e)throw Error("No module: "+d);var b=[],c=[],j=a("$injector", 18 | "invoke"),k={_invokeQueue:b,_runBlocks:c,requires:e,name:d,provider:a("$provide","provider"),factory:a("$provide","factory"),service:a("$provide","service"),value:a("$provide","value"),constant:a("$provide","constant","unshift"),filter:a("$filterProvider","register"),controller:a("$controllerProvider","register"),directive:a("$compileProvider","directive"),config:j,run:function(a){c.push(a);return this}};g&&j(g);return k})}})}function tb(b){return b.replace(mc,function(a,b,d,e){return e?d.toUpperCase(): 19 | d}).replace(nc,"Moz$1")}function ab(b,a){function c(){var e;for(var b=[this],c=a,h,f,i,j,k,l;b.length;){h=b.shift();f=0;for(i=h.length;f-1}function xb(b,a){a&&m(a.split(" "),function(a){b.className=Q((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+Q(a)+" "," "))})} 22 | function yb(b,a){a&&m(a.split(" "),function(a){if(!Ca(b,a))b.className=Q(b.className+" "+Q(a))})}function bb(b,a){if(a)for(var a=!a.nodeName&&y(a.length)&&!oa(a)?a:[a],c=0;c4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!")}else{if(i.cookie!==$){$=i.cookie;d=$.split("; ");r={};for(f=0;f0&&(a=unescape(e.substring(0,j)),r[a]===q&&(r[a]=unescape(e.substring(j+1))))}return r}};f.defer=function(a,b){var c; 34 | p++;c=l(function(){delete o[c];e(a)},b||0);o[c]=!0;return c};f.defer.cancel=function(a){return o[a]?(delete o[a],n(a),e(C),!0):!1}}function wc(){this.$get=["$window","$log","$sniffer","$document",function(b,a,c,d){return new vc(b,d,a,c)}]}function xc(){this.$get=function(){function b(b,d){function e(a){if(a!=l){if(n){if(n==a)n=a.n}else n=a;g(a.n,a.p);g(a,l);l=a;l.n=null}}function g(a,b){if(a!=b){if(a)a.p=b;if(b)b.n=a}}if(b in a)throw Error("cacheId "+b+" taken");var h=0,f=v({},d,{id:b}),i={},j=d&& 35 | d.capacity||Number.MAX_VALUE,k={},l=null,n=null;return a[b]={put:function(a,b){var c=k[a]||(k[a]={key:a});e(c);w(b)||(a in i||h++,i[a]=b,h>j&&this.remove(n.key))},get:function(a){var b=k[a];if(b)return e(b),i[a]},remove:function(a){var b=k[a];if(b){if(b==l)l=b.p;if(b==n)n=b.n;g(b.n,b.p);delete k[a];delete i[a];h--}},removeAll:function(){i={};h=0;k={};l=n=null},destroy:function(){k=f=i=null;delete a[b]},info:function(){return v({},f,{size:h})}}}var a={};b.info=function(){var b={};m(a,function(a,e){b[e]= 36 | a.info()});return b};b.get=function(b){return a[b]};return b}}function yc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Db(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: ",h=/^\s*(https?|ftp|mailto|file):/;this.directive=function i(d,e){B(d)?($a(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];m(a[d], 37 | function(a){try{var g=b.invoke(a);if(H(g))g={compile:I(g)};else if(!g.compile&&g.link)g.compile=I(g.link);g.priority=g.priority||0;g.name=g.name||d;g.require=g.require||g.controller&&g.name;g.restrict=g.restrict||"A";e.push(g)}catch(h){c(h)}});return e}])),a[d].push(e)):m(d,nb(i));return this};this.urlSanitizationWhitelist=function(a){return y(a)?(h=a,this):h};this.$get=["$injector","$interpolate","$exceptionHandler","$http","$templateCache","$parse","$controller","$rootScope","$document",function(b, 38 | j,k,l,n,o,p,s,t){function x(a,b,c){a instanceof u||(a=u(a));m(a,function(b,c){b.nodeType==3&&b.nodeValue.match(/\S+/)&&(a[c]=u(b).wrap("").parent()[0])});var d=A(a,b,a,c);return function(b,c){$a(b,"scope");for(var e=c?ua.clone.call(a):a,j=0,g=e.length;jr.priority)break;if(Y=r.scope)ta("isolated scope",J,r,D),L(Y)&&(M(D,"ng-isolate-scope"),J=r),M(D,"ng-scope"),s=s||r;F=r.name;if(Y=r.controller)y=y||{},ta("'"+F+"' controller",y[F],r,D),y[F]=r;if(Y=r.transclude)ta("transclusion",ja,r,D),ja=r,l=r.priority,Y=="element"?(W=u(b),D=c.$$element=u(T.createComment(" "+ 45 | F+": "+c[F]+" ")),b=D[0],C(e,u(W[0]),b),V=x(W,d,l)):(W=u(cb(b)).contents(),D.html(""),V=x(W,d));if(Y=r.template)if(ta("template",A,r,D),A=r,Y=Fb(Y),r.replace){W=u("
"+Q(Y)+"
").contents();b=W[0];if(W.length!=1||b.nodeType!==1)throw Error(g+Y);C(e,D,b);F={$attr:{}};a=a.concat(N(b,a.splice(v+1,a.length-(v+1)),F));$(c,F);z=a.length}else D.html(Y);if(r.templateUrl)ta("template",A,r,D),A=r,i=R(a.splice(v,a.length-v),i,D,c,e,r.replace,V),z=a.length;else if(r.compile)try{w=r.compile(D,c,V),H(w)? 46 | j(null,w):w&&j(w.pre,w.post)}catch(G){k(G,pa(D))}if(r.terminal)i.terminal=!0,l=Math.max(l,r.priority)}i.scope=s&&s.scope;i.transclude=ja&&V;return i}function r(d,e,g,j){var h=!1;if(a.hasOwnProperty(e))for(var o,e=b.get(e+c),l=0,p=e.length;lo.priority)&&o.restrict.indexOf(g)!=-1)d.push(o),h=!0}catch(n){k(n)}return h}function $(a,b){var c=b.$attr,d=a.$attr,e=a.$$element;m(a,function(d,e){e.charAt(0)!="$"&&(b[e]&&(d+=(e==="style"?";":" ")+b[e]),a.$set(e,d,!0,c[e]))});m(b, 47 | function(b,g){g=="class"?(M(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):g=="style"?e.attr("style",e.attr("style")+";"+b):g.charAt(0)!="$"&&!a.hasOwnProperty(g)&&(a[g]=b,d[g]=c[g])})}function R(a,b,c,d,e,j,h){var i=[],k,o,p=c[0],t=a.shift(),s=v({},t,{controller:null,templateUrl:null,transclude:null,scope:null});c.html("");l.get(t.templateUrl,{cache:n}).success(function(l){var n,t,l=Fb(l);if(j){t=u("
"+Q(l)+"
").contents();n=t[0];if(t.length!=1||n.nodeType!==1)throw Error(g+l);l={$attr:{}}; 48 | C(e,c,n);N(n,a,l);$(d,l)}else n=p,c.html(l);a.unshift(s);k=J(a,n,d,h);for(o=A(c[0].childNodes,h);i.length;){var r=i.pop(),l=i.pop();t=i.pop();var ia=i.pop(),D=n;t!==p&&(D=cb(n),C(l,u(t),D));k(function(){b(o,ia,D,e,r)},ia,D,e,r)}i=null}).error(function(a,b,c,d){throw Error("Failed to load template: "+d.url);});return function(a,c,d,e,g){i?(i.push(c),i.push(d),i.push(e),i.push(g)):k(function(){b(o,c,d,e,g)},c,d,e,g)}}function F(a,b){return b.priority-a.priority}function ta(a,b,c,d){if(b)throw Error("Multiple directives ["+ 49 | b.name+", "+c.name+"] asking for "+a+" on: "+pa(d));}function y(a,b){var c=j(b,!0);c&&a.push({priority:0,compile:I(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);M(d.data("$binding",e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function V(a,b,c,d){var e=j(c,!0);e&&b.push({priority:100,compile:I(function(a,b,c){b=c.$$observers||(c.$$observers={});d==="class"&&(e=j(c[d],!0));c[d]=q;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e, 50 | function(a){c.$set(d,a)})})})}function C(a,b,c){var d=b[0],e=d.parentNode,g,j;if(a){g=0;for(j=a.length;g 68 | 0){var e=R[0],f=e.text;if(f==a||f==b||f==c||f==d||!a&&!b&&!c&&!d)return e}return!1}function f(b,c,d,f){return(b=h(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),R.shift(),b):!1}function i(a){f(a)||e("is unexpected, expecting ["+a+"]",h())}function j(a,b){return function(c,d){return a(c,d,b)}}function k(a,b,c){return function(d,e){return b(d,e,a,c)}}function l(){for(var a=[];;)if(R.length>0&&!h("}",")",";","]")&&a.push(w()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d,e=0;e","<=",">="))a=k(a,b.fn,t());return a}function x(){for(var a=m(),b;b=f("*","/","%");)a=k(a,b.fn,m());return a}function m(){var a;return f("+")?A():(a=f("-"))?k(r,a.fn,m()):(a=f("!"))?j(a.fn,m()):A()}function A(){var a;if(f("("))a=w(),i(")");else if(f("["))a=N();else if(f("{"))a=J();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=y(a,c),c=null):b.text==="["?(c=a,a=V(a)):b.text==="."?(c=a,a=u(a)):e("IMPOSSIBLE");return a}function N(){var a= 71 | [];if(g().text!="]"){do a.push(F());while(f(","))}i("]");return function(b,c){for(var d=[],e=0;e1;d++){var e=a.shift(),g=b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]= 74 | c}function gb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,h=0;h7),hasEvent:function(c){if(c=="input"&&Z==9)return!1;if(w(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Vc(){this.$get=I(P)}function Ob(b){var a={},c,d,e;if(!b)return a;m(b.split("\n"),function(b){e=b.indexOf(":");c=z(Q(b.substr(0, 92 | e)));d=Q(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Pb(b){var a=L(b)?b:q;return function(c){a||(a=Ob(b));return c?a[z(c)]||null:a}}function Qb(b,a,c){if(H(c))return c(b,a);m(c,function(c){b=c(b,a)});return b}function Wc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){B(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=pb(d,!0)));return d}],transformRequest:[function(a){return L(a)&&wa.apply(a)!=="[object File]"?da(a):a}], 93 | headers:{common:{Accept:"application/json, text/plain, */*","X-Requested-With":"XMLHttpRequest"},post:{"Content-Type":"application/json;charset=utf-8"},put:{"Content-Type":"application/json;charset=utf-8"}}},e=this.responseInterceptors=[];this.$get=["$httpBackend","$browser","$cacheFactory","$rootScope","$q","$injector",function(a,b,c,i,j,k){function l(a){function c(a){var b=v({},a,{data:Qb(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:j.reject(b)}a.method=la(a.method);var e=a.transformRequest|| 94 | d.transformRequest,f=a.transformResponse||d.transformResponse,g=d.headers,g=v({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},g.common,g[z(a.method)],a.headers),e=Qb(a.data,Pb(g),e),i;w(a.data)&&delete g["Content-Type"];i=n(a,e,g);i=i.then(c,c);m(s,function(a){i=a(i)});i.success=function(b){i.then(function(c){b(c.data,c.status,c.headers,a)});return i};i.error=function(b){i.then(null,function(c){b(c.data,c.status,c.headers,a)});return i};return i}function n(b,c,d){function e(a,b,c){m&&(200<=a&&a<300?m.put(q, 95 | [a,b,Ob(c)]):m.remove(q));f(b,a,c);i.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?k.resolve:k.reject)({data:a,status:c,headers:Pb(d),config:b})}function h(){var a=za(l.pendingRequests,b);a!==-1&&l.pendingRequests.splice(a,1)}var k=j.defer(),n=k.promise,m,s,q=o(b.url,b.params);l.pendingRequests.push(b);n.then(h,h);b.cache&&b.method=="GET"&&(m=L(b.cache)?b.cache:p);if(m)if(s=m.get(q))if(s.then)return s.then(h,h),s;else E(s)?f(s[1],s[0],U(s[2])):f(s,200,{});else m.put(q,n);s||a(b.method, 96 | q,c,e,d,b.timeout,b.withCredentials);return n}function o(a,b){if(!b)return a;var c=[];fc(b,function(a,b){a==null||a==q||(L(a)&&(a=da(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var p=c("$http"),s=[];m(e,function(a){s.push(B(a)?k.get(a):k.invoke(a))});l.pendingRequests=[];(function(a){m(arguments,function(a){l[a]=function(b,c){return l(v(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){l[a]= 97 | function(b,c,d){return l(v(d||{},{method:a,url:b,data:c}))}})})("post","put");l.defaults=d;return l}]}function Xc(){this.$get=["$browser","$window","$document",function(b,a,c){return Yc(b,Zc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Yc(b,a,c,d,e,g){function h(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;Z?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror= 98 | d;e.body.appendChild(c)}return function(e,i,j,k,l,n,o){function p(a,c,d,e){c=(i.match(Hb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(C)}b.$$incOutstandingRequestCount();i=i||b.url();if(z(e)=="jsonp"){var s="_"+(d.counter++).toString(36);d[s]=function(a){d[s].data=a};h(i.replace("JSON_CALLBACK","angular.callbacks."+s),function(){d[s].data?p(k,200,d[s].data):p(k,-2);delete d[s]})}else{var t=new a;t.open(e,i,!0);m(l,function(a,b){a&&t.setRequestHeader(b,a)}); 99 | var q;t.onreadystatechange=function(){if(t.readyState==4){var a=t.getAllResponseHeaders(),b=["Cache-Control","Content-Language","Content-Type","Expires","Last-Modified","Pragma"];a||(a="",m(b,function(b){var c=t.getResponseHeader(b);c&&(a+=b+": "+c+"\n")}));p(k,q||t.status,t.responseText,a)}};if(o)t.withCredentials=!0;t.send(j||"");n>0&&c(function(){q=-1;t.abort()},n)}}}function $c(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0, 100 | maxFrac:3,posPre:"",posSuf:"",negPre:"-",negSuf:"",gSize:3,lgSize:3},{minInt:1,minFrac:2,maxFrac:2,posPre:"\u00a4",posSuf:"",negPre:"(\u00a4",negSuf:")",gSize:3,lgSize:3}],CURRENCY_SYM:"$"},DATETIME_FORMATS:{MONTH:"January,February,March,April,May,June,July,August,September,October,November,December".split(","),SHORTMONTH:"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec".split(","),DAY:"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday".split(","),SHORTDAY:"Sun,Mon,Tue,Wed,Thu,Fri,Sat".split(","), 101 | AMPMS:["AM","PM"],medium:"MMM d, y h:mm:ss a","short":"M/d/yy h:mm a",fullDate:"EEEE, MMMM d, y",longDate:"MMMM d, y",mediumDate:"MMM d, y",shortDate:"M/d/yy",mediumTime:"h:mm:ss a",shortTime:"h:mm a"},pluralCat:function(b){return b===1?"one":"other"}}}}function ad(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,i){var j=c.defer(),k=j.promise,l=y(i)&&!i,f=a.defer(function(){try{j.resolve(e())}catch(a){j.reject(a),d(a)}l||b.$apply()},f),i=function(){delete g[k.$$timeoutId]}; 102 | k.$$timeoutId=f;g[f]=j;k.then(i,i);return k}var g={};e.cancel=function(b){return b&&b.$$timeoutId in g?(g[b.$$timeoutId].reject("canceled"),a.defer.cancel(b.$$timeoutId)):!1};return e}]}function Rb(b){function a(a,e){return b.factory(a+c,e)}var c="Filter";this.register=a;this.$get=["$injector",function(a){return function(b){return a.get(b+c)}}];a("currency",Sb);a("date",Tb);a("filter",bd);a("json",cd);a("limitTo",dd);a("lowercase",ed);a("number",Ub);a("orderBy",Vb);a("uppercase",fd)}function bd(){return function(b, 103 | a){if(!E(b))return b;var c=[];c.check=function(a){for(var b=0;b-1;case "object":for(var c in a)if(c.charAt(0)!=="$"&&d(a[c],b))return!0;return!1;case "array":for(c=0;ce+1?h="0":(f=h,j=!0)}if(!j){h=(h.split(Xb)[1]||"").length;w(e)&&(e=Math.min(Math.max(a.minFrac,h),a.maxFrac));var h=Math.pow(10,e),b=Math.round(b*h)/h,b=(""+b).split(Xb),h=b[0],b=b[1]||"",j=0,k=a.lgSize, 106 | l=a.gSize;if(h.length>=k+l)for(var j=h.length-k,n=0;n0||e> 107 | -c)e+=c;e===0&&c==-12&&(e=12);return jb(e,a,d)}}function Ja(b,a){return function(c,d){var e=c["get"+b](),g=la(a?"SHORT"+b:b);return d[g][e]}}function Tb(b){function a(a){var b;if(b=a.match(c)){var a=new Date(0),g=0,h=0;b[9]&&(g=G(b[9]+b[10]),h=G(b[9]+b[11]));a.setUTCFullYear(G(b[1]),G(b[2])-1,G(b[3]));a.setUTCHours(G(b[4]||0)-g,G(b[5]||0)-h,G(b[6]||0),G(b[7]||0))}return a}var c=/^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;return function(c, 108 | e){var g="",h=[],f,i,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;B(c)&&(c=gd.test(c)?G(c):a(c));Qa(c)&&(c=new Date(c));if(!na(c))return c;for(;e;)(i=hd.exec(e))?(h=h.concat(ha.call(i,1)),e=h.pop()):(h.push(e),e=null);m(h,function(a){f=id[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function cd(){return function(b){return da(b,!0)}}function dd(){return function(b,a){if(!(b instanceof Array))return b;var a=G(a),c=[],d,e;if(!b||!(b instanceof Array))return c; 109 | a>b.length?a=b.length:a<-b.length&&(a=-b.length);a>0?(d=0,e=a):(d=b.length+a,e=b.length);for(;dn?(d.$setValidity("maxlength",!1),q):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function kb(b,a){b="ngClass"+b;return S(function(c,d,e){function g(b){if(a===!0||c.$index%2===a)i&&!fa(b,i)&&h(i),f(b);i=U(b)}function h(a){L(a)&& 115 | !E(a)&&(a=Ra(a,function(a,b){if(a)return b}));d.removeClass(E(a)?a.join(" "):a)}function f(a){L(a)&&!E(a)&&(a=Ra(a,function(a,b){if(a)return b}));a&&d.addClass(E(a)?a.join(" "):a)}var i=q;c.$watch(e[b],g,!0);e.$observe("class",function(){var a=c.$eval(e[b]);g(a,a)});b!=="ngClass"&&c.$watch("$index",function(d,g){var i=d&1;i!==g&1&&(i===a?f(c.$eval(e[b])):h(c.$eval(e[b])))})})}var z=function(b){return B(b)?b.toLowerCase():b},la=function(b){return B(b)?b.toUpperCase():b},Z=G((/msie (\d+)/.exec(z(navigator.userAgent))|| 116 | [])[1]),u,ca,ha=[].slice,Pa=[].push,wa=Object.prototype.toString,Ya=P.angular||(P.angular={}),sa,fb,aa=["0","0","0"];C.$inject=[];ma.$inject=[];fb=Z<9?function(b){b=b.nodeName?b:b[0];return b.scopeName&&b.scopeName!="HTML"?la(b.scopeName+":"+b.nodeName):b.nodeName}:function(b){return b.nodeName?b.nodeName:b[0].nodeName};var kc=/[A-Z]/g,jd={full:"1.0.7",major:1,minor:0,dot:7,codeName:"monochromatic-rainbow"},Ba=K.cache={},Aa=K.expando="ng-"+(new Date).getTime(),oc=1,$b=P.document.addEventListener? 117 | function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},db=P.document.removeEventListener?function(b,a,c){b.removeEventListener(a,c,!1)}:function(b,a,c){b.detachEvent("on"+a,c)},mc=/([\:\-\_]+(.))/g,nc=/^moz([A-Z])/,ua=K.prototype={ready:function(b){function a(){c||(c=!0,b())}var c=!1;this.bind("DOMContentLoaded",a);K(P).bind("load",a)},toString:function(){var b=[];m(this,function(a){b.push(""+a)});return"["+b.join(", ")+"]"},eq:function(b){return b>=0?u(this[b]):u(this[this.length+ 118 | b])},length:0,push:Pa,sort:[].sort,splice:[].splice},Ea={};m("multiple,selected,checked,disabled,readOnly,required".split(","),function(b){Ea[z(b)]=b});var Bb={};m("input,select,option,textarea,button,form".split(","),function(b){Bb[la(b)]=!0});m({data:wb,inheritedData:Da,scope:function(b){return Da(b,"$scope")},controller:zb,injector:function(b){return Da(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ca,css:function(b,a,c){a=tb(a);if(y(c))b.style[a]=c;else{var d;Z<=8&&(d= 119 | b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];Z<=8&&(d=d===""?q:d);return d}},attr:function(b,a,c){var d=z(a);if(Ea[d])if(y(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||C).specified?d:q;else if(y(c))b.setAttribute(a,c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?q:b},prop:function(b,a,c){if(y(c))b[a]=c;else return b[a]},text:v(Z<9?function(b,a){if(b.nodeType==1){if(w(a))return b.innerText; 120 | b.innerText=a}else{if(w(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(w(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(w(a))return b.value;b.value=a},html:function(b,a){if(w(a))return b.innerHTML;for(var c=0,d=b.childNodes;c":function(a,c,d,e){return d(a,c)>e(a,c)},"<=":function(a,c,d,e){return d(a,c)<=e(a,c)},">=":function(a,c,d,e){return d(a,c)>=e(a,c)},"&&":function(a,c,d,e){return d(a,c)&&e(a,c)},"||":function(a,c,d,e){return d(a,c)||e(a,c)},"&":function(a,c,d,e){return d(a,c)&e(a,c)},"|":function(a,c,d,e){return e(a,c)(a,c,d(a,c))},"!":function(a,c,d){return!d(a,c)}},Mc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},ib={},Zc=P.XMLHttpRequest||function(){try{return new ActiveXObject("Msxml2.XMLHTTP.6.0")}catch(a){}try{return new ActiveXObject("Msxml2.XMLHTTP.3.0")}catch(c){}try{return new ActiveXObject("Msxml2.XMLHTTP")}catch(d){}throw Error("This browser does not support XMLHttpRequest."); 130 | };Rb.$inject=["$provide"];Sb.$inject=["$locale"];Ub.$inject=["$locale"];var Xb=".",id={yyyy:O("FullYear",4),yy:O("FullYear",2,0,!0),y:O("FullYear",1),MMMM:Ja("Month"),MMM:Ja("Month",!0),MM:O("Month",2,1),M:O("Month",1,1),dd:O("Date",2),d:O("Date",1),HH:O("Hours",2),H:O("Hours",1),hh:O("Hours",2,-12),h:O("Hours",1,-12),mm:O("Minutes",2),m:O("Minutes",1),ss:O("Seconds",2),s:O("Seconds",1),EEEE:Ja("Day"),EEE:Ja("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){var a= 131 | -1*a.getTimezoneOffset(),c=a>=0?"+":"";c+=jb(Math[a>0?"floor":"ceil"](a/60),2)+jb(Math.abs(a%60),2);return c}},hd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,gd=/^\d+$/;Tb.$inject=["$locale"];var ed=I(z),fd=I(la);Vb.$inject=["$parse"];var kd=I({restrict:"E",compile:function(a,c){Z<=8&&(!c.href&&!c.name&&c.$set("href",""),a.append(T.createComment("IE fix")));return function(a,c){c.bind("click",function(a){c.attr("href")||a.preventDefault()})}}}),lb={};m(Ea,function(a, 132 | c){var d=ea("ng-"+c);lb[d]=function(){return{priority:100,compile:function(){return function(a,g,h){a.$watch(h[d],function(a){h.$set(c,!!a)})}}}}});m(["src","href"],function(a){var c=ea("ng-"+a);lb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),Z&&e.prop(a,g[a]))})}}}});var Ma={$addControl:C,$removeControl:C,$setValidity:C,$setDirty:C};Yb.$inject=["$element","$attrs","$scope"];var Pa=function(a){return["$timeout",function(c){var d={name:"form",restrict:"E", 133 | controller:Yb,compile:function(){return{pre:function(a,d,h,f){if(!h.action){var i=function(a){a.preventDefault?a.preventDefault():a.returnValue=!1};$b(d[0],"submit",i);d.bind("$destroy",function(){c(function(){db(d[0],"submit",i)},0,!1)})}var j=d.parent().controller("form"),k=h.name||h.ngForm;k&&(a[k]=f);j&&d.bind("$destroy",function(){j.$removeControl(f);k&&(a[k]=q);v(f,Ma)})}}}};return a?v(U(d),{restrict:"EAC"}):d}]},ld=Pa(),md=Pa(!0),nd=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/, 134 | od=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,pd=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/,bc={text:Oa,number:function(a,c,d,e,g,h){Oa(a,c,d,e,g,h);e.$parsers.push(function(a){var c=X(a);return c||pd.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),q)});e.$formatters.push(function(a){return X(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!X(a)&&ai?(e.$setValidity("max",!1),q):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return X(a)||Qa(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),q)})},url:function(a,c,d,e,g,h){Oa(a,c,d,e,g,h);a=function(a){return X(a)||nd.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a, 136 | c,d,e,g,h){Oa(a,c,d,e,g,h);a=function(a){return X(a)||od.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email",!1),q)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){w(d.name)&&c.attr("name",xa());c.bind("click",function(){c[0].checked&&a.$apply(function(){e.$setViewValue(d.value)})});e.$render=function(){c[0].checked=d.value==e.$viewValue};d.$observe("value",e.$render)},checkbox:function(a,c,d,e){var g=d.ngTrueValue,h=d.ngFalseValue;B(g)||(g=!0);B(h)||(h=!1);c.bind("click", 137 | function(){a.$apply(function(){e.$setViewValue(c[0].checked)})});e.$render=function(){c[0].checked=e.$viewValue};e.$formatters.push(function(a){return a===g});e.$parsers.push(function(a){return a?g:h})},hidden:C,button:C,submit:C,reset:C},cc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,h){h&&(bc[z(g.type)]||bc.text)(d,e,g,h,c,a)}}}],La="ng-valid",Ka="ng-invalid",Na="ng-pristine",Zb="ng-dirty",qd=["$scope","$exceptionHandler","$attrs","$element","$parse", 138 | function(a,c,d,e,g){function h(a,c){c=c?"-"+Za(c,"-"):"";e.removeClass((a?Ka:La)+c).addClass((a?La:Ka)+c)}this.$modelValue=this.$viewValue=Number.NaN;this.$parsers=[];this.$formatters=[];this.$viewChangeListeners=[];this.$pristine=!0;this.$dirty=!1;this.$valid=!0;this.$invalid=!1;this.$name=d.name;var f=g(d.ngModel),i=f.assign;if(!i)throw Error(Eb+d.ngModel+" ("+pa(e)+")");this.$render=C;var j=e.inheritedData("$formController")||Ma,k=0,l=this.$error={};e.addClass(Na);h(!0);this.$setValidity=function(a, 139 | c){if(l[a]!==!c){if(c){if(l[a]&&k--,!k)h(!0),this.$valid=!0,this.$invalid=!1}else h(!1),this.$invalid=!0,this.$valid=!1,k++;l[a]=!c;h(c,a);j.$setValidity(a,c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,e.removeClass(Na).addClass(Zb),j.$setDirty();m(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,i(a,d),m(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};var n=this;a.$watch(function(){var c= 140 | f(a);if(n.$modelValue!==c){var d=n.$formatters,e=d.length;for(n.$modelValue=c;e--;)c=d[e](c);if(n.$viewValue!==c)n.$viewValue=c,n.$render()}})}],rd=function(){return{require:["ngModel","^?form"],controller:qd,link:function(a,c,d,e){var g=e[0],h=e[1]||Ma;h.$addControl(g);c.bind("$destroy",function(){h.$removeControl(g)})}}},sd=I({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),dc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required= 141 | !0;var g=function(a){if(d.required&&(X(a)||a===!1))e.$setValidity("required",!1);else return e.$setValidity("required",!0),a};e.$formatters.push(g);e.$parsers.unshift(g);d.$observe("required",function(){g(e.$viewValue)})}}}},td=function(){return{require:"ngModel",link:function(a,c,d,e){var g=(a=/\/(.*)\//.exec(d.ngList))&&RegExp(a[1])||d.ngList||",";e.$parsers.push(function(a){var c=[];a&&m(a.split(g),function(a){a&&c.push(Q(a))});return c});e.$formatters.push(function(a){return E(a)?a.join(", "): 142 | q})}}},ud=/^(true|false|\d+)$/,vd=function(){return{priority:100,compile:function(a,c){return ud.test(c.ngValue)?function(a,c,g){g.$set("value",a.$eval(g.ngValue))}:function(a,c,g){a.$watch(g.ngValue,function(a){g.$set("value",a,!1)})}}}},wd=S(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==q?"":a)})}),xd=["$interpolate",function(a){return function(c,d,e){c=a(d.attr(e.$attr.ngBindTemplate));d.addClass("ng-binding").data("$binding",c);e.$observe("ngBindTemplate", 143 | function(a){d.text(a)})}}],yd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe,function(a){c.html(a||"")})}}],zd=kb("",!0),Ad=kb("Odd",0),Bd=kb("Even",1),Cd=S({compile:function(a,c){c.$set("ngCloak",q);a.removeClass("ng-cloak")}}),Dd=[function(){return{scope:!0,controller:"@"}}],Ed=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],ec={};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "), 144 | function(a){var c=ea("ng-"+a);ec[c]=["$parse",function(d){return function(e,g,h){var f=d(h[c]);g.bind(z(a),function(a){e.$apply(function(){f(e,{$event:a})})})}}]});var Fd=S(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Gd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,h){var f=h.ngInclude||h.src,i=h.onload||"",j=h.autoscroll;return function(g,h){var n=0,o,p=function(){o&&(o.$destroy(),o=null);h.html("")}; 145 | g.$watch(f,function(f){var m=++n;f?a.get(f,{cache:c}).success(function(a){m===n&&(o&&o.$destroy(),o=g.$new(),h.html(a),e(h.contents())(o),y(j)&&(!j||g.$eval(j))&&d(),o.$emit("$includeContentLoaded"),g.$eval(i))}).error(function(){m===n&&p()}):p()})}}}}],Hd=S({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Id=S({terminal:!0,priority:1E3}),Jd=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,h){var f=h.count,i=g.attr(h.$attr.when),j=h.offset|| 146 | 0,k=e.$eval(i),l={},n=c.startSymbol(),o=c.endSymbol();m(k,function(a,e){l[e]=c(a.replace(d,n+f+"-"+j+o))});e.$watch(function(){var c=parseFloat(e.$eval(f));return isNaN(c)?"":(c in k||(c=a.pluralCat(c-j)),l[c](e,g,!0))},function(a){g.text(a)})}}}],Kd=S({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,h){var f=h.ngRepeat,h=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),i,j,k;if(!h)throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f= 147 | h[1];i=h[2];h=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!h)throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '"+f+"'.");j=h[3]||h[1];k=h[2];var l=new eb;a.$watch(function(a){var e,f,h=a.$eval(i),m=c,q=new eb,y,A,u,w,r,v;if(E(h))r=h||[];else{r=[];for(u in h)h.hasOwnProperty(u)&&u.charAt(0)!="$"&&r.push(u);r.sort()}y=r.length-1;e=0;for(f=r.length;ez;)u.pop().element.remove()}for(;r.length> 157 | x;)r.pop()[0].element.remove()}var i;if(!(i=s.match(d)))throw Error("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+s+"'.");var j=c(i[2]||i[1]),k=i[4]||i[6],l=i[5],m=c(i[3]||""),n=c(i[2]?i[1]:k),o=c(i[7]),r=[[{element:f,label:""}]];t&&(a(t)(e),t.removeClass("ng-scope"),t.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=o(e)||[],d={},h,i,j,m,s,t;if(p){i=[];m=0;for(t=r.length;m@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}'); -------------------------------------------------------------------------------- /app/js/lib/angular/version.txt: -------------------------------------------------------------------------------- 1 | 1.0.7 -------------------------------------------------------------------------------- /app/js/lib/require.js: -------------------------------------------------------------------------------- 1 | /* 2 | RequireJS 2.1.4 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved. 3 | Available via the MIT or new BSD license. 4 | see: http://github.com/jrburke/requirejs for details 5 | */ 6 | var requirejs,require,define; 7 | (function(Y){function I(b){return"[object Function]"===L.call(b)}function J(b){return"[object Array]"===L.call(b)}function x(b,c){if(b){var d;for(d=0;dthis.depCount&&!this.defined){if(I(n)){if(this.events.error)try{e=k.execCb(c,n,b,e)}catch(d){a=d}else e=k.execCb(c,n,b,e);this.map.isDefine&&((b=this.module)&&void 0!==b.exports&&b.exports!==this.exports?e=b.exports:void 0===e&&this.usingExports&&(e=this.exports));if(a)return a.requireMap=this.map,a.requireModules=[this.map.id],a.requireType="define",A(this.error=a)}else e=n;this.exports=e;if(this.map.isDefine&& 19 | !this.ignore&&(p[c]=e,l.onResourceLoad))l.onResourceLoad(k,this.map,this.depMaps);delete j[c];this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a=this.map,b=a.id,d=h(a.prefix);this.depMaps.push(d);s(d,"defined",t(this,function(e){var n,d;d=this.map.name;var v=this.map.parentMap?this.map.parentMap.name:null,g=k.makeRequire(a.parentMap,{enableBuildCallback:!0}); 20 | if(this.map.unnormalized){if(e.normalize&&(d=e.normalize(d,function(a){return c(a,v,!0)})||""),e=h(a.prefix+"!"+d,this.map.parentMap),s(e,"defined",t(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=i(j,e.id)){this.depMaps.push(e);if(this.events.error)d.on("error",t(this,function(a){this.emit("error",a)}));d.enable()}}else n=t(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),n.error=t(this,function(a){this.inited=!0;this.error=a;a.requireModules= 21 | [b];E(j,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&delete j[a.map.id]});A(a)}),n.fromText=t(this,function(e,c){var d=a.name,u=h(d),v=O;c&&(e=c);v&&(O=!1);q(u);r(m.config,b)&&(m.config[d]=m.config[b]);try{l.exec(e)}catch(j){return A(F("fromtexteval","fromText eval for "+b+" failed: "+j,j,[b]))}v&&(O=!0);this.depMaps.push(u);k.completeLoad(d);g([d],n)}),e.load(a.name,g,n,m)}));k.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){this.enabling=this.enabled=!0;x(this.depMaps,t(this,function(a, 22 | b){var c,e;if("string"===typeof a){a=h(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=i(N,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;s(a,"defined",t(this,function(a){this.defineDep(b,a);this.check()}));this.errback&&s(a,"error",this.errback)}c=a.id;e=j[c];!r(N,c)&&(e&&!e.enabled)&&k.enable(a,this)}));E(this.pluginMaps,t(this,function(a){var b=i(j,a.id);b&&!b.enabled&&k.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c= 23 | this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){x(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};k={config:m,contextName:b,registry:j,defined:p,urlFetched:S,defQueue:G,Module:W,makeModuleMap:h,nextTick:l.nextTick,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=m.pkgs,c=m.shim,e={paths:!0,config:!0,map:!0};E(a,function(a,b){e[b]?"map"===b?Q(m[b],a,!0,!0):Q(m[b],a,!0):m[b]=a});a.shim&&(E(a.shim,function(a, 24 | b){J(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=k.makeShimExports(a);c[b]=a}),m.shim=c);a.packages&&(x(a.packages,function(a){a="string"===typeof a?{name:a}:a;b[a.name]={name:a.name,location:a.location||a.name,main:(a.main||"main").replace(ga,"").replace(aa,"")}}),m.pkgs=b);E(j,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=h(b))});if(a.deps||a.callback)k.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(Y,arguments)); 25 | return b||a.exports&&Z(a.exports)}},makeRequire:function(a,d){function g(e,c,u){var i,m;d.enableBuildCallback&&(c&&I(c))&&(c.__requireJsBuild=!0);if("string"===typeof e){if(I(c))return A(F("requireargs","Invalid require call"),u);if(a&&r(N,e))return N[e](j[a.id]);if(l.get)return l.get(k,e,a);i=h(e,a,!1,!0);i=i.id;return!r(p,i)?A(F("notloaded",'Module name "'+i+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):p[i]}K();k.nextTick(function(){K();m=q(h(null,a));m.skipMap=d.skipMap; 26 | m.init(e,c,u,{enabled:!0});C()});return g}d=d||{};Q(g,{isBrowser:z,toUrl:function(b){var d,f=b.lastIndexOf("."),h=b.split("/")[0];if(-1!==f&&(!("."===h||".."===h)||1g.attachEvent.toString().indexOf("[native code"))&& 33 | !V?(O=!0,g.attachEvent("onreadystatechange",b.onScriptLoad)):(g.addEventListener("load",b.onScriptLoad,!1),g.addEventListener("error",b.onScriptError,!1)),g.src=d,K=g,D?B.insertBefore(g,D):B.appendChild(g),K=null,g;$&&(importScripts(d),b.completeLoad(c))};z&&M(document.getElementsByTagName("script"),function(b){B||(B=b.parentNode);if(s=b.getAttribute("data-main"))return q.baseUrl||(H=s.split("/"),ba=H.pop(),ca=H.length?H.join("/")+"/":"./",q.baseUrl=ca,s=ba),s=s.replace(aa,""),q.deps=q.deps?q.deps.concat(s): 34 | [s],!0});define=function(b,c,d){var i,g;"string"!==typeof b&&(d=c,c=b,b=null);J(c)||(d=c,c=[]);!c.length&&I(d)&&d.length&&(d.toString().replace(ia,"").replace(ja,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c));if(O){if(!(i=K))P&&"interactive"===P.readyState||M(document.getElementsByTagName("script"),function(b){if("interactive"===b.readyState)return P=b}),i=P;i&&(b||(b=i.getAttribute("data-requiremodule")),g=C[i.getAttribute("data-requirecontext")])}(g? 35 | g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};l.exec=function(b){return eval(b)};l(q)}})(this); -------------------------------------------------------------------------------- /app/js/main.js: -------------------------------------------------------------------------------- 1 | // Require JS Config File 2 | 3 | require.config({ 4 | paths : { 5 | "angular" : "lib/angular/angular" 6 | }, 7 | shim : { 8 | angular :{ 9 | exports : "angular" 10 | } 11 | }, 12 | baseUrl: 'js/' 13 | }); 14 | 15 | 16 | require(["app"], 17 | function(App) { 18 | App.initialize(); 19 | } 20 | ); -------------------------------------------------------------------------------- /app/templates/Main.html: -------------------------------------------------------------------------------- 1 |

Hello {{world}}

2 | 3 | 4 | {{ 'Current version is v%VERSION%.' | interpolate }} 5 | 6 |
Angular seed app: v
7 | 8 | 9 | -------------------------------------------------------------------------------- /config/testacular.conf.js: -------------------------------------------------------------------------------- 1 | 2 | // base path, that will be used to resolve files and exclude 3 | basePath = '../'; 4 | 5 | 6 | // list of files / patterns to load in the browser 7 | files = [ 8 | 9 | JASMINE, 10 | JASMINE_ADAPTER, 11 | REQUIRE, 12 | REQUIRE_ADAPTER, 13 | 14 | 'app/js/lib/angular/angular.js', 15 | 'test/main.js', 16 | 17 | {pattern: 'test/lib/angular/angular-mocks.js', included: false}, 18 | {pattern: 'app/js/**/*.js', included: false}, 19 | {pattern: 'test/unit/*.test.js', included: false}, 20 | {pattern: 'test/test.test.js', included: false} 21 | ]; 22 | 23 | // test results reporter to use 24 | // possible values: dots || progress 25 | reporter = 'dots'; 26 | 27 | 28 | // web server port 29 | port = 9876; 30 | 31 | 32 | // cli runner port 33 | runnerPort = 9100; 34 | 35 | 36 | // enable / disable colors in the output (reporters and logs) 37 | colors = true; 38 | 39 | 40 | // level of logging 41 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 42 | logLevel = LOG_INFO; 43 | 44 | 45 | // enable / disable watching file and executing tests whenever any file changes 46 | autoWatch = true; 47 | 48 | 49 | // Start these browsers, currently available: 50 | // - Chrome 51 | // - ChromeCanary 52 | // - Firefox 53 | // - Opera 54 | // - Safari 55 | // - PhantomJS 56 | browsers = []; 57 | 58 | 59 | // Continuous Integration mode 60 | // if true, it capture browsers, run tests and exit 61 | singleRun = false; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angularjs-requirejs-seed", 3 | "version": "0.1.0", 4 | "description": "Application skeleton for an angular app with express server.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/maxdow/angularjs-requirejs-seed.git" 8 | }, 9 | "keywords": [ 10 | "angularjs", 11 | "seed", 12 | "express" 13 | ], 14 | "author": "Maxime Warnier", 15 | "license": "BSD", 16 | "readmeFilename": "README.md", 17 | "dependencies":{ 18 | "express":"latest" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /scripts/test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | REM Windows script for running unit tests 4 | REM You have to run server and capture some browser first 5 | REM 6 | REM Requirements: 7 | REM - NodeJS (http://nodejs.org/) 8 | REM - Testacular (npm install -g testacular) 9 | 10 | set BASE_DIR=%~dp0 11 | testacular start "%BASE_DIR%\..\config\testacular.conf.js" %* 12 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BASE_DIR=`dirname $0` 4 | 5 | echo "" 6 | echo "Starting Testacular Server (http://vojtajina.github.com/testacular)" 7 | echo "-------------------------------------------------------------------" 8 | 9 | testacular start $BASE_DIR/../config/testacular.conf.js $* 10 | -------------------------------------------------------------------------------- /scripts/watchr.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env watchr 2 | 3 | # config file for watchr http://github.com/mynyml/watchr 4 | # install: gem install watchr 5 | # run: watch watchr.rb 6 | # note: make sure that you have jstd server running (server.sh) and a browser captured 7 | 8 | log_file = File.expand_path(File.dirname(__FILE__) + '/../logs/jstd.log') 9 | 10 | `cd ..` 11 | `touch #{log_file}` 12 | 13 | puts "String watchr... log file: #{log_file}" 14 | 15 | watch( '(app/js|test/unit)' ) do 16 | `echo "\n\ntest run started @ \`date\`" > #{log_file}` 17 | `scripts/test.sh &> #{log_file}` 18 | end 19 | 20 | -------------------------------------------------------------------------------- /scripts/web-server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | app = express(); 3 | 4 | 5 | app.configure(function(){ 6 | 7 | app.use(express.static(__dirname + './../app')); 8 | 9 | }); 10 | 11 | 12 | app.listen(3000); 13 | console.log('Listening on port 3000'); -------------------------------------------------------------------------------- /test/lib/angular/angular-mocks.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license AngularJS v1.0.7 3 | * (c) 2010-2012 Google, Inc. http://angularjs.org 4 | * License: MIT 5 | * 6 | * TODO(vojta): wrap whole file into closure during build 7 | */ 8 | 9 | /** 10 | * @ngdoc overview 11 | * @name angular.mock 12 | * @description 13 | * 14 | * Namespace from 'angular-mocks.js' which contains testing related code. 15 | */ 16 | angular.mock = {}; 17 | 18 | /** 19 | * ! This is a private undocumented service ! 20 | * 21 | * @name ngMock.$browser 22 | * 23 | * @description 24 | * This service is a mock implementation of {@link ng.$browser}. It provides fake 25 | * implementation for commonly used browser apis that are hard to test, e.g. setTimeout, xhr, 26 | * cookies, etc... 27 | * 28 | * The api of this service is the same as that of the real {@link ng.$browser $browser}, except 29 | * that there are several helper methods available which can be used in tests. 30 | */ 31 | angular.mock.$BrowserProvider = function() { 32 | this.$get = function(){ 33 | return new angular.mock.$Browser(); 34 | }; 35 | }; 36 | 37 | angular.mock.$Browser = function() { 38 | var self = this; 39 | 40 | this.isMock = true; 41 | self.$$url = "http://server/"; 42 | self.$$lastUrl = self.$$url; // used by url polling fn 43 | self.pollFns = []; 44 | 45 | // TODO(vojta): remove this temporary api 46 | self.$$completeOutstandingRequest = angular.noop; 47 | self.$$incOutstandingRequestCount = angular.noop; 48 | 49 | 50 | // register url polling fn 51 | 52 | self.onUrlChange = function(listener) { 53 | self.pollFns.push( 54 | function() { 55 | if (self.$$lastUrl != self.$$url) { 56 | self.$$lastUrl = self.$$url; 57 | listener(self.$$url); 58 | } 59 | } 60 | ); 61 | 62 | return listener; 63 | }; 64 | 65 | self.cookieHash = {}; 66 | self.lastCookieHash = {}; 67 | self.deferredFns = []; 68 | self.deferredNextId = 0; 69 | 70 | self.defer = function(fn, delay) { 71 | delay = delay || 0; 72 | self.deferredFns.push({time:(self.defer.now + delay), fn:fn, id: self.deferredNextId}); 73 | self.deferredFns.sort(function(a,b){ return a.time - b.time;}); 74 | return self.deferredNextId++; 75 | }; 76 | 77 | 78 | self.defer.now = 0; 79 | 80 | 81 | self.defer.cancel = function(deferId) { 82 | var fnIndex; 83 | 84 | angular.forEach(self.deferredFns, function(fn, index) { 85 | if (fn.id === deferId) fnIndex = index; 86 | }); 87 | 88 | if (fnIndex !== undefined) { 89 | self.deferredFns.splice(fnIndex, 1); 90 | return true; 91 | } 92 | 93 | return false; 94 | }; 95 | 96 | 97 | /** 98 | * @name ngMock.$browser#defer.flush 99 | * @methodOf ngMock.$browser 100 | * 101 | * @description 102 | * Flushes all pending requests and executes the defer callbacks. 103 | * 104 | * @param {number=} number of milliseconds to flush. See {@link #defer.now} 105 | */ 106 | self.defer.flush = function(delay) { 107 | if (angular.isDefined(delay)) { 108 | self.defer.now += delay; 109 | } else { 110 | if (self.deferredFns.length) { 111 | self.defer.now = self.deferredFns[self.deferredFns.length-1].time; 112 | } else { 113 | throw Error('No deferred tasks to be flushed'); 114 | } 115 | } 116 | 117 | while (self.deferredFns.length && self.deferredFns[0].time <= self.defer.now) { 118 | self.deferredFns.shift().fn(); 119 | } 120 | }; 121 | /** 122 | * @name ngMock.$browser#defer.now 123 | * @propertyOf ngMock.$browser 124 | * 125 | * @description 126 | * Current milliseconds mock time. 127 | */ 128 | 129 | self.$$baseHref = ''; 130 | self.baseHref = function() { 131 | return this.$$baseHref; 132 | }; 133 | }; 134 | angular.mock.$Browser.prototype = { 135 | 136 | /** 137 | * @name ngMock.$browser#poll 138 | * @methodOf ngMock.$browser 139 | * 140 | * @description 141 | * run all fns in pollFns 142 | */ 143 | poll: function poll() { 144 | angular.forEach(this.pollFns, function(pollFn){ 145 | pollFn(); 146 | }); 147 | }, 148 | 149 | addPollFn: function(pollFn) { 150 | this.pollFns.push(pollFn); 151 | return pollFn; 152 | }, 153 | 154 | url: function(url, replace) { 155 | if (url) { 156 | this.$$url = url; 157 | return this; 158 | } 159 | 160 | return this.$$url; 161 | }, 162 | 163 | cookies: function(name, value) { 164 | if (name) { 165 | if (value == undefined) { 166 | delete this.cookieHash[name]; 167 | } else { 168 | if (angular.isString(value) && //strings only 169 | value.length <= 4096) { //strict cookie storage limits 170 | this.cookieHash[name] = value; 171 | } 172 | } 173 | } else { 174 | if (!angular.equals(this.cookieHash, this.lastCookieHash)) { 175 | this.lastCookieHash = angular.copy(this.cookieHash); 176 | this.cookieHash = angular.copy(this.cookieHash); 177 | } 178 | return this.cookieHash; 179 | } 180 | }, 181 | 182 | notifyWhenNoOutstandingRequests: function(fn) { 183 | fn(); 184 | } 185 | }; 186 | 187 | 188 | /** 189 | * @ngdoc object 190 | * @name ngMock.$exceptionHandlerProvider 191 | * 192 | * @description 193 | * Configures the mock implementation of {@link ng.$exceptionHandler} to rethrow or to log errors passed 194 | * into the `$exceptionHandler`. 195 | */ 196 | 197 | /** 198 | * @ngdoc object 199 | * @name ngMock.$exceptionHandler 200 | * 201 | * @description 202 | * Mock implementation of {@link ng.$exceptionHandler} that rethrows or logs errors passed 203 | * into it. See {@link ngMock.$exceptionHandlerProvider $exceptionHandlerProvider} for configuration 204 | * information. 205 | * 206 | * 207 | *
 208 |  *   describe('$exceptionHandlerProvider', function() {
 209 |  *
 210 |  *     it('should capture log messages and exceptions', function() {
 211 |  *
 212 |  *       module(function($exceptionHandlerProvider) {
 213 |  *         $exceptionHandlerProvider.mode('log');
 214 |  *       });
 215 |  *
 216 |  *       inject(function($log, $exceptionHandler, $timeout) {
 217 |  *         $timeout(function() { $log.log(1); });
 218 |  *         $timeout(function() { $log.log(2); throw 'banana peel'; });
 219 |  *         $timeout(function() { $log.log(3); });
 220 |  *         expect($exceptionHandler.errors).toEqual([]);
 221 |  *         expect($log.assertEmpty());
 222 |  *         $timeout.flush();
 223 |  *         expect($exceptionHandler.errors).toEqual(['banana peel']);
 224 |  *         expect($log.log.logs).toEqual([[1], [2], [3]]);
 225 |  *       });
 226 |  *     });
 227 |  *   });
 228 |  * 
229 | */ 230 | 231 | angular.mock.$ExceptionHandlerProvider = function() { 232 | var handler; 233 | 234 | /** 235 | * @ngdoc method 236 | * @name ngMock.$exceptionHandlerProvider#mode 237 | * @methodOf ngMock.$exceptionHandlerProvider 238 | * 239 | * @description 240 | * Sets the logging mode. 241 | * 242 | * @param {string} mode Mode of operation, defaults to `rethrow`. 243 | * 244 | * - `rethrow`: If any errors are passed into the handler in tests, it typically 245 | * means that there is a bug in the application or test, so this mock will 246 | * make these tests fail. 247 | * - `log`: Sometimes it is desirable to test that an error is thrown, for this case the `log` mode stores an 248 | * array of errors in `$exceptionHandler.errors`, to allow later assertion of them. 249 | * See {@link ngMock.$log#assertEmpty assertEmpty()} and 250 | * {@link ngMock.$log#reset reset()} 251 | */ 252 | this.mode = function(mode) { 253 | switch(mode) { 254 | case 'rethrow': 255 | handler = function(e) { 256 | throw e; 257 | }; 258 | break; 259 | case 'log': 260 | var errors = []; 261 | 262 | handler = function(e) { 263 | if (arguments.length == 1) { 264 | errors.push(e); 265 | } else { 266 | errors.push([].slice.call(arguments, 0)); 267 | } 268 | }; 269 | 270 | handler.errors = errors; 271 | break; 272 | default: 273 | throw Error("Unknown mode '" + mode + "', only 'log'/'rethrow' modes are allowed!"); 274 | } 275 | }; 276 | 277 | this.$get = function() { 278 | return handler; 279 | }; 280 | 281 | this.mode('rethrow'); 282 | }; 283 | 284 | 285 | /** 286 | * @ngdoc service 287 | * @name ngMock.$log 288 | * 289 | * @description 290 | * Mock implementation of {@link ng.$log} that gathers all logged messages in arrays 291 | * (one array per logging level). These arrays are exposed as `logs` property of each of the 292 | * level-specific log function, e.g. for level `error` the array is exposed as `$log.error.logs`. 293 | * 294 | */ 295 | angular.mock.$LogProvider = function() { 296 | 297 | function concat(array1, array2, index) { 298 | return array1.concat(Array.prototype.slice.call(array2, index)); 299 | } 300 | 301 | 302 | this.$get = function () { 303 | var $log = { 304 | log: function() { $log.log.logs.push(concat([], arguments, 0)); }, 305 | warn: function() { $log.warn.logs.push(concat([], arguments, 0)); }, 306 | info: function() { $log.info.logs.push(concat([], arguments, 0)); }, 307 | error: function() { $log.error.logs.push(concat([], arguments, 0)); } 308 | }; 309 | 310 | /** 311 | * @ngdoc method 312 | * @name ngMock.$log#reset 313 | * @methodOf ngMock.$log 314 | * 315 | * @description 316 | * Reset all of the logging arrays to empty. 317 | */ 318 | $log.reset = function () { 319 | /** 320 | * @ngdoc property 321 | * @name ngMock.$log#log.logs 322 | * @propertyOf ngMock.$log 323 | * 324 | * @description 325 | * Array of messages logged using {@link ngMock.$log#log}. 326 | * 327 | * @example 328 | *
 329 |        * $log.log('Some Log');
 330 |        * var first = $log.log.logs.unshift();
 331 |        * 
332 | */ 333 | $log.log.logs = []; 334 | /** 335 | * @ngdoc property 336 | * @name ngMock.$log#warn.logs 337 | * @propertyOf ngMock.$log 338 | * 339 | * @description 340 | * Array of messages logged using {@link ngMock.$log#warn}. 341 | * 342 | * @example 343 | *
 344 |        * $log.warn('Some Warning');
 345 |        * var first = $log.warn.logs.unshift();
 346 |        * 
347 | */ 348 | $log.warn.logs = []; 349 | /** 350 | * @ngdoc property 351 | * @name ngMock.$log#info.logs 352 | * @propertyOf ngMock.$log 353 | * 354 | * @description 355 | * Array of messages logged using {@link ngMock.$log#info}. 356 | * 357 | * @example 358 | *
 359 |        * $log.info('Some Info');
 360 |        * var first = $log.info.logs.unshift();
 361 |        * 
362 | */ 363 | $log.info.logs = []; 364 | /** 365 | * @ngdoc property 366 | * @name ngMock.$log#error.logs 367 | * @propertyOf ngMock.$log 368 | * 369 | * @description 370 | * Array of messages logged using {@link ngMock.$log#error}. 371 | * 372 | * @example 373 | *
 374 |        * $log.log('Some Error');
 375 |        * var first = $log.error.logs.unshift();
 376 |        * 
377 | */ 378 | $log.error.logs = []; 379 | }; 380 | 381 | /** 382 | * @ngdoc method 383 | * @name ngMock.$log#assertEmpty 384 | * @methodOf ngMock.$log 385 | * 386 | * @description 387 | * Assert that the all of the logging methods have no logged messages. If messages present, an exception is thrown. 388 | */ 389 | $log.assertEmpty = function() { 390 | var errors = []; 391 | angular.forEach(['error', 'warn', 'info', 'log'], function(logLevel) { 392 | angular.forEach($log[logLevel].logs, function(log) { 393 | angular.forEach(log, function (logItem) { 394 | errors.push('MOCK $log (' + logLevel + '): ' + String(logItem) + '\n' + (logItem.stack || '')); 395 | }); 396 | }); 397 | }); 398 | if (errors.length) { 399 | errors.unshift("Expected $log to be empty! Either a message was logged unexpectedly, or an expected " + 400 | "log message was not checked and removed:"); 401 | errors.push(''); 402 | throw new Error(errors.join('\n---------\n')); 403 | } 404 | }; 405 | 406 | $log.reset(); 407 | return $log; 408 | }; 409 | }; 410 | 411 | 412 | (function() { 413 | var R_ISO8061_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?:\:?(\d\d)(?:\:?(\d\d)(?:\.(\d{3}))?)?)?(Z|([+-])(\d\d):?(\d\d)))?$/; 414 | 415 | function jsonStringToDate(string){ 416 | var match; 417 | if (match = string.match(R_ISO8061_STR)) { 418 | var date = new Date(0), 419 | tzHour = 0, 420 | tzMin = 0; 421 | if (match[9]) { 422 | tzHour = int(match[9] + match[10]); 423 | tzMin = int(match[9] + match[11]); 424 | } 425 | date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); 426 | date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); 427 | return date; 428 | } 429 | return string; 430 | } 431 | 432 | function int(str) { 433 | return parseInt(str, 10); 434 | } 435 | 436 | function padNumber(num, digits, trim) { 437 | var neg = ''; 438 | if (num < 0) { 439 | neg = '-'; 440 | num = -num; 441 | } 442 | num = '' + num; 443 | while(num.length < digits) num = '0' + num; 444 | if (trim) 445 | num = num.substr(num.length - digits); 446 | return neg + num; 447 | } 448 | 449 | 450 | /** 451 | * @ngdoc object 452 | * @name angular.mock.TzDate 453 | * @description 454 | * 455 | * *NOTE*: this is not an injectable instance, just a globally available mock class of `Date`. 456 | * 457 | * Mock of the Date type which has its timezone specified via constructor arg. 458 | * 459 | * The main purpose is to create Date-like instances with timezone fixed to the specified timezone 460 | * offset, so that we can test code that depends on local timezone settings without dependency on 461 | * the time zone settings of the machine where the code is running. 462 | * 463 | * @param {number} offset Offset of the *desired* timezone in hours (fractions will be honored) 464 | * @param {(number|string)} timestamp Timestamp representing the desired time in *UTC* 465 | * 466 | * @example 467 | * !!!! WARNING !!!!! 468 | * This is not a complete Date object so only methods that were implemented can be called safely. 469 | * To make matters worse, TzDate instances inherit stuff from Date via a prototype. 470 | * 471 | * We do our best to intercept calls to "unimplemented" methods, but since the list of methods is 472 | * incomplete we might be missing some non-standard methods. This can result in errors like: 473 | * "Date.prototype.foo called on incompatible Object". 474 | * 475 | *
 476 |    * var newYearInBratislava = new TzDate(-1, '2009-12-31T23:00:00Z');
 477 |    * newYearInBratislava.getTimezoneOffset() => -60;
 478 |    * newYearInBratislava.getFullYear() => 2010;
 479 |    * newYearInBratislava.getMonth() => 0;
 480 |    * newYearInBratislava.getDate() => 1;
 481 |    * newYearInBratislava.getHours() => 0;
 482 |    * newYearInBratislava.getMinutes() => 0;
 483 |    * 
484 | * 485 | */ 486 | angular.mock.TzDate = function (offset, timestamp) { 487 | var self = new Date(0); 488 | if (angular.isString(timestamp)) { 489 | var tsStr = timestamp; 490 | 491 | self.origDate = jsonStringToDate(timestamp); 492 | 493 | timestamp = self.origDate.getTime(); 494 | if (isNaN(timestamp)) 495 | throw { 496 | name: "Illegal Argument", 497 | message: "Arg '" + tsStr + "' passed into TzDate constructor is not a valid date string" 498 | }; 499 | } else { 500 | self.origDate = new Date(timestamp); 501 | } 502 | 503 | var localOffset = new Date(timestamp).getTimezoneOffset(); 504 | self.offsetDiff = localOffset*60*1000 - offset*1000*60*60; 505 | self.date = new Date(timestamp + self.offsetDiff); 506 | 507 | self.getTime = function() { 508 | return self.date.getTime() - self.offsetDiff; 509 | }; 510 | 511 | self.toLocaleDateString = function() { 512 | return self.date.toLocaleDateString(); 513 | }; 514 | 515 | self.getFullYear = function() { 516 | return self.date.getFullYear(); 517 | }; 518 | 519 | self.getMonth = function() { 520 | return self.date.getMonth(); 521 | }; 522 | 523 | self.getDate = function() { 524 | return self.date.getDate(); 525 | }; 526 | 527 | self.getHours = function() { 528 | return self.date.getHours(); 529 | }; 530 | 531 | self.getMinutes = function() { 532 | return self.date.getMinutes(); 533 | }; 534 | 535 | self.getSeconds = function() { 536 | return self.date.getSeconds(); 537 | }; 538 | 539 | self.getTimezoneOffset = function() { 540 | return offset * 60; 541 | }; 542 | 543 | self.getUTCFullYear = function() { 544 | return self.origDate.getUTCFullYear(); 545 | }; 546 | 547 | self.getUTCMonth = function() { 548 | return self.origDate.getUTCMonth(); 549 | }; 550 | 551 | self.getUTCDate = function() { 552 | return self.origDate.getUTCDate(); 553 | }; 554 | 555 | self.getUTCHours = function() { 556 | return self.origDate.getUTCHours(); 557 | }; 558 | 559 | self.getUTCMinutes = function() { 560 | return self.origDate.getUTCMinutes(); 561 | }; 562 | 563 | self.getUTCSeconds = function() { 564 | return self.origDate.getUTCSeconds(); 565 | }; 566 | 567 | self.getUTCMilliseconds = function() { 568 | return self.origDate.getUTCMilliseconds(); 569 | }; 570 | 571 | self.getDay = function() { 572 | return self.date.getDay(); 573 | }; 574 | 575 | // provide this method only on browsers that already have it 576 | if (self.toISOString) { 577 | self.toISOString = function() { 578 | return padNumber(self.origDate.getUTCFullYear(), 4) + '-' + 579 | padNumber(self.origDate.getUTCMonth() + 1, 2) + '-' + 580 | padNumber(self.origDate.getUTCDate(), 2) + 'T' + 581 | padNumber(self.origDate.getUTCHours(), 2) + ':' + 582 | padNumber(self.origDate.getUTCMinutes(), 2) + ':' + 583 | padNumber(self.origDate.getUTCSeconds(), 2) + '.' + 584 | padNumber(self.origDate.getUTCMilliseconds(), 3) + 'Z' 585 | } 586 | } 587 | 588 | //hide all methods not implemented in this mock that the Date prototype exposes 589 | var unimplementedMethods = ['getMilliseconds', 'getUTCDay', 590 | 'getYear', 'setDate', 'setFullYear', 'setHours', 'setMilliseconds', 591 | 'setMinutes', 'setMonth', 'setSeconds', 'setTime', 'setUTCDate', 'setUTCFullYear', 592 | 'setUTCHours', 'setUTCMilliseconds', 'setUTCMinutes', 'setUTCMonth', 'setUTCSeconds', 593 | 'setYear', 'toDateString', 'toGMTString', 'toJSON', 'toLocaleFormat', 'toLocaleString', 594 | 'toLocaleTimeString', 'toSource', 'toString', 'toTimeString', 'toUTCString', 'valueOf']; 595 | 596 | angular.forEach(unimplementedMethods, function(methodName) { 597 | self[methodName] = function() { 598 | throw Error("Method '" + methodName + "' is not implemented in the TzDate mock"); 599 | }; 600 | }); 601 | 602 | return self; 603 | }; 604 | 605 | //make "tzDateInstance instanceof Date" return true 606 | angular.mock.TzDate.prototype = Date.prototype; 607 | })(); 608 | 609 | 610 | /** 611 | * @ngdoc function 612 | * @name angular.mock.dump 613 | * @description 614 | * 615 | * *NOTE*: this is not an injectable instance, just a globally available function. 616 | * 617 | * Method for serializing common angular objects (scope, elements, etc..) into strings, useful for debugging. 618 | * 619 | * This method is also available on window, where it can be used to display objects on debug console. 620 | * 621 | * @param {*} object - any object to turn into string. 622 | * @return {string} a serialized string of the argument 623 | */ 624 | angular.mock.dump = function(object) { 625 | return serialize(object); 626 | 627 | function serialize(object) { 628 | var out; 629 | 630 | if (angular.isElement(object)) { 631 | object = angular.element(object); 632 | out = angular.element('
'); 633 | angular.forEach(object, function(element) { 634 | out.append(angular.element(element).clone()); 635 | }); 636 | out = out.html(); 637 | } else if (angular.isArray(object)) { 638 | out = []; 639 | angular.forEach(object, function(o) { 640 | out.push(serialize(o)); 641 | }); 642 | out = '[ ' + out.join(', ') + ' ]'; 643 | } else if (angular.isObject(object)) { 644 | if (angular.isFunction(object.$eval) && angular.isFunction(object.$apply)) { 645 | out = serializeScope(object); 646 | } else if (object instanceof Error) { 647 | out = object.stack || ('' + object.name + ': ' + object.message); 648 | } else { 649 | out = angular.toJson(object, true); 650 | } 651 | } else { 652 | out = String(object); 653 | } 654 | 655 | return out; 656 | } 657 | 658 | function serializeScope(scope, offset) { 659 | offset = offset || ' '; 660 | var log = [offset + 'Scope(' + scope.$id + '): {']; 661 | for ( var key in scope ) { 662 | if (scope.hasOwnProperty(key) && !key.match(/^(\$|this)/)) { 663 | log.push(' ' + key + ': ' + angular.toJson(scope[key])); 664 | } 665 | } 666 | var child = scope.$$childHead; 667 | while(child) { 668 | log.push(serializeScope(child, offset + ' ')); 669 | child = child.$$nextSibling; 670 | } 671 | log.push('}'); 672 | return log.join('\n' + offset); 673 | } 674 | }; 675 | 676 | /** 677 | * @ngdoc object 678 | * @name ngMock.$httpBackend 679 | * @description 680 | * Fake HTTP backend implementation suitable for unit testing applications that use the 681 | * {@link ng.$http $http service}. 682 | * 683 | * *Note*: For fake HTTP backend implementation suitable for end-to-end testing or backend-less 684 | * development please see {@link ngMockE2E.$httpBackend e2e $httpBackend mock}. 685 | * 686 | * During unit testing, we want our unit tests to run quickly and have no external dependencies so 687 | * we don’t want to send {@link https://developer.mozilla.org/en/xmlhttprequest XHR} or 688 | * {@link http://en.wikipedia.org/wiki/JSONP JSONP} requests to a real server. All we really need is 689 | * to verify whether a certain request has been sent or not, or alternatively just let the 690 | * application make requests, respond with pre-trained responses and assert that the end result is 691 | * what we expect it to be. 692 | * 693 | * This mock implementation can be used to respond with static or dynamic responses via the 694 | * `expect` and `when` apis and their shortcuts (`expectGET`, `whenPOST`, etc). 695 | * 696 | * When an Angular application needs some data from a server, it calls the $http service, which 697 | * sends the request to a real server using $httpBackend service. With dependency injection, it is 698 | * easy to inject $httpBackend mock (which has the same API as $httpBackend) and use it to verify 699 | * the requests and respond with some testing data without sending a request to real server. 700 | * 701 | * There are two ways to specify what test data should be returned as http responses by the mock 702 | * backend when the code under test makes http requests: 703 | * 704 | * - `$httpBackend.expect` - specifies a request expectation 705 | * - `$httpBackend.when` - specifies a backend definition 706 | * 707 | * 708 | * # Request Expectations vs Backend Definitions 709 | * 710 | * Request expectations provide a way to make assertions about requests made by the application and 711 | * to define responses for those requests. The test will fail if the expected requests are not made 712 | * or they are made in the wrong order. 713 | * 714 | * Backend definitions allow you to define a fake backend for your application which doesn't assert 715 | * if a particular request was made or not, it just returns a trained response if a request is made. 716 | * The test will pass whether or not the request gets made during testing. 717 | * 718 | * 719 | * 720 | * 721 | * 722 | * 723 | * 724 | * 725 | * 726 | * 727 | * 728 | * 729 | * 730 | * 731 | * 732 | * 733 | * 734 | * 735 | * 736 | * 737 | * 738 | * 739 | * 740 | * 741 | * 742 | * 743 | * 744 | * 745 | * 746 | * 747 | * 748 | * 749 | * 750 | * 751 | *
Request expectationsBackend definitions
Syntax.expect(...).respond(...).when(...).respond(...)
Typical usagestrict unit testsloose (black-box) unit testing
Fulfills multiple requestsNOYES
Order of requests mattersYESNO
Request requiredYESNO
Response requiredoptional (see below)YES
752 | * 753 | * In cases where both backend definitions and request expectations are specified during unit 754 | * testing, the request expectations are evaluated first. 755 | * 756 | * If a request expectation has no response specified, the algorithm will search your backend 757 | * definitions for an appropriate response. 758 | * 759 | * If a request didn't match any expectation or if the expectation doesn't have the response 760 | * defined, the backend definitions are evaluated in sequential order to see if any of them match 761 | * the request. The response from the first matched definition is returned. 762 | * 763 | * 764 | * # Flushing HTTP requests 765 | * 766 | * The $httpBackend used in production, always responds to requests with responses asynchronously. 767 | * If we preserved this behavior in unit testing, we'd have to create async unit tests, which are 768 | * hard to write, follow and maintain. At the same time the testing mock, can't respond 769 | * synchronously because that would change the execution of the code under test. For this reason the 770 | * mock $httpBackend has a `flush()` method, which allows the test to explicitly flush pending 771 | * requests and thus preserving the async api of the backend, while allowing the test to execute 772 | * synchronously. 773 | * 774 | * 775 | * # Unit testing with mock $httpBackend 776 | * 777 | *
 778 |    // controller
 779 |    function MyController($scope, $http) {
 780 |      $http.get('/auth.py').success(function(data) {
 781 |        $scope.user = data;
 782 |      });
 783 | 
 784 |      this.saveMessage = function(message) {
 785 |        $scope.status = 'Saving...';
 786 |        $http.post('/add-msg.py', message).success(function(response) {
 787 |          $scope.status = '';
 788 |        }).error(function() {
 789 |          $scope.status = 'ERROR!';
 790 |        });
 791 |      };
 792 |    }
 793 | 
 794 |    // testing controller
 795 |    var $httpBackend;
 796 | 
 797 |    beforeEach(inject(function($injector) {
 798 |      $httpBackend = $injector.get('$httpBackend');
 799 | 
 800 |      // backend definition common for all tests
 801 |      $httpBackend.when('GET', '/auth.py').respond({userId: 'userX'}, {'A-Token': 'xxx'});
 802 |    }));
 803 | 
 804 | 
 805 |    afterEach(function() {
 806 |      $httpBackend.verifyNoOutstandingExpectation();
 807 |      $httpBackend.verifyNoOutstandingRequest();
 808 |    });
 809 | 
 810 | 
 811 |    it('should fetch authentication token', function() {
 812 |      $httpBackend.expectGET('/auth.py');
 813 |      var controller = scope.$new(MyController);
 814 |      $httpBackend.flush();
 815 |    });
 816 | 
 817 | 
 818 |    it('should send msg to server', function() {
 819 |      // now you don’t care about the authentication, but
 820 |      // the controller will still send the request and
 821 |      // $httpBackend will respond without you having to
 822 |      // specify the expectation and response for this request
 823 |      $httpBackend.expectPOST('/add-msg.py', 'message content').respond(201, '');
 824 | 
 825 |      var controller = scope.$new(MyController);
 826 |      $httpBackend.flush();
 827 |      controller.saveMessage('message content');
 828 |      expect(controller.status).toBe('Saving...');
 829 |      $httpBackend.flush();
 830 |      expect(controller.status).toBe('');
 831 |    });
 832 | 
 833 | 
 834 |    it('should send auth header', function() {
 835 |      $httpBackend.expectPOST('/add-msg.py', undefined, function(headers) {
 836 |        // check if the header was send, if it wasn't the expectation won't
 837 |        // match the request and the test will fail
 838 |        return headers['Authorization'] == 'xxx';
 839 |      }).respond(201, '');
 840 | 
 841 |      var controller = scope.$new(MyController);
 842 |      controller.saveMessage('whatever');
 843 |      $httpBackend.flush();
 844 |    });
 845 |    
846 | */ 847 | angular.mock.$HttpBackendProvider = function() { 848 | this.$get = [createHttpBackendMock]; 849 | }; 850 | 851 | /** 852 | * General factory function for $httpBackend mock. 853 | * Returns instance for unit testing (when no arguments specified): 854 | * - passing through is disabled 855 | * - auto flushing is disabled 856 | * 857 | * Returns instance for e2e testing (when `$delegate` and `$browser` specified): 858 | * - passing through (delegating request to real backend) is enabled 859 | * - auto flushing is enabled 860 | * 861 | * @param {Object=} $delegate Real $httpBackend instance (allow passing through if specified) 862 | * @param {Object=} $browser Auto-flushing enabled if specified 863 | * @return {Object} Instance of $httpBackend mock 864 | */ 865 | function createHttpBackendMock($delegate, $browser) { 866 | var definitions = [], 867 | expectations = [], 868 | responses = [], 869 | responsesPush = angular.bind(responses, responses.push); 870 | 871 | function createResponse(status, data, headers) { 872 | if (angular.isFunction(status)) return status; 873 | 874 | return function() { 875 | return angular.isNumber(status) 876 | ? [status, data, headers] 877 | : [200, status, data]; 878 | }; 879 | } 880 | 881 | // TODO(vojta): change params to: method, url, data, headers, callback 882 | function $httpBackend(method, url, data, callback, headers) { 883 | var xhr = new MockXhr(), 884 | expectation = expectations[0], 885 | wasExpected = false; 886 | 887 | function prettyPrint(data) { 888 | return (angular.isString(data) || angular.isFunction(data) || data instanceof RegExp) 889 | ? data 890 | : angular.toJson(data); 891 | } 892 | 893 | if (expectation && expectation.match(method, url)) { 894 | if (!expectation.matchData(data)) 895 | throw Error('Expected ' + expectation + ' with different data\n' + 896 | 'EXPECTED: ' + prettyPrint(expectation.data) + '\nGOT: ' + data); 897 | 898 | if (!expectation.matchHeaders(headers)) 899 | throw Error('Expected ' + expectation + ' with different headers\n' + 900 | 'EXPECTED: ' + prettyPrint(expectation.headers) + '\nGOT: ' + 901 | prettyPrint(headers)); 902 | 903 | expectations.shift(); 904 | 905 | if (expectation.response) { 906 | responses.push(function() { 907 | var response = expectation.response(method, url, data, headers); 908 | xhr.$$respHeaders = response[2]; 909 | callback(response[0], response[1], xhr.getAllResponseHeaders()); 910 | }); 911 | return; 912 | } 913 | wasExpected = true; 914 | } 915 | 916 | var i = -1, definition; 917 | while ((definition = definitions[++i])) { 918 | if (definition.match(method, url, data, headers || {})) { 919 | if (definition.response) { 920 | // if $browser specified, we do auto flush all requests 921 | ($browser ? $browser.defer : responsesPush)(function() { 922 | var response = definition.response(method, url, data, headers); 923 | xhr.$$respHeaders = response[2]; 924 | callback(response[0], response[1], xhr.getAllResponseHeaders()); 925 | }); 926 | } else if (definition.passThrough) { 927 | $delegate(method, url, data, callback, headers); 928 | } else throw Error('No response defined !'); 929 | return; 930 | } 931 | } 932 | throw wasExpected ? 933 | Error('No response defined !') : 934 | Error('Unexpected request: ' + method + ' ' + url + '\n' + 935 | (expectation ? 'Expected ' + expectation : 'No more request expected')); 936 | } 937 | 938 | /** 939 | * @ngdoc method 940 | * @name ngMock.$httpBackend#when 941 | * @methodOf ngMock.$httpBackend 942 | * @description 943 | * Creates a new backend definition. 944 | * 945 | * @param {string} method HTTP method. 946 | * @param {string|RegExp} url HTTP url. 947 | * @param {(string|RegExp)=} data HTTP request body. 948 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 949 | * object and returns true if the headers match the current definition. 950 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 951 | * request is handled. 952 | * 953 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` 954 | * – The respond method takes a set of static data to be returned or a function that can return 955 | * an array containing response status (number), response data (string) and response headers 956 | * (Object). 957 | */ 958 | $httpBackend.when = function(method, url, data, headers) { 959 | var definition = new MockHttpExpectation(method, url, data, headers), 960 | chain = { 961 | respond: function(status, data, headers) { 962 | definition.response = createResponse(status, data, headers); 963 | } 964 | }; 965 | 966 | if ($browser) { 967 | chain.passThrough = function() { 968 | definition.passThrough = true; 969 | }; 970 | } 971 | 972 | definitions.push(definition); 973 | return chain; 974 | }; 975 | 976 | /** 977 | * @ngdoc method 978 | * @name ngMock.$httpBackend#whenGET 979 | * @methodOf ngMock.$httpBackend 980 | * @description 981 | * Creates a new backend definition for GET requests. For more info see `when()`. 982 | * 983 | * @param {string|RegExp} url HTTP url. 984 | * @param {(Object|function(Object))=} headers HTTP headers. 985 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 986 | * request is handled. 987 | */ 988 | 989 | /** 990 | * @ngdoc method 991 | * @name ngMock.$httpBackend#whenHEAD 992 | * @methodOf ngMock.$httpBackend 993 | * @description 994 | * Creates a new backend definition for HEAD requests. For more info see `when()`. 995 | * 996 | * @param {string|RegExp} url HTTP url. 997 | * @param {(Object|function(Object))=} headers HTTP headers. 998 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 999 | * request is handled. 1000 | */ 1001 | 1002 | /** 1003 | * @ngdoc method 1004 | * @name ngMock.$httpBackend#whenDELETE 1005 | * @methodOf ngMock.$httpBackend 1006 | * @description 1007 | * Creates a new backend definition for DELETE requests. For more info see `when()`. 1008 | * 1009 | * @param {string|RegExp} url HTTP url. 1010 | * @param {(Object|function(Object))=} headers HTTP headers. 1011 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1012 | * request is handled. 1013 | */ 1014 | 1015 | /** 1016 | * @ngdoc method 1017 | * @name ngMock.$httpBackend#whenPOST 1018 | * @methodOf ngMock.$httpBackend 1019 | * @description 1020 | * Creates a new backend definition for POST requests. For more info see `when()`. 1021 | * 1022 | * @param {string|RegExp} url HTTP url. 1023 | * @param {(string|RegExp)=} data HTTP request body. 1024 | * @param {(Object|function(Object))=} headers HTTP headers. 1025 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1026 | * request is handled. 1027 | */ 1028 | 1029 | /** 1030 | * @ngdoc method 1031 | * @name ngMock.$httpBackend#whenPUT 1032 | * @methodOf ngMock.$httpBackend 1033 | * @description 1034 | * Creates a new backend definition for PUT requests. For more info see `when()`. 1035 | * 1036 | * @param {string|RegExp} url HTTP url. 1037 | * @param {(string|RegExp)=} data HTTP request body. 1038 | * @param {(Object|function(Object))=} headers HTTP headers. 1039 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1040 | * request is handled. 1041 | */ 1042 | 1043 | /** 1044 | * @ngdoc method 1045 | * @name ngMock.$httpBackend#whenJSONP 1046 | * @methodOf ngMock.$httpBackend 1047 | * @description 1048 | * Creates a new backend definition for JSONP requests. For more info see `when()`. 1049 | * 1050 | * @param {string|RegExp} url HTTP url. 1051 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1052 | * request is handled. 1053 | */ 1054 | createShortMethods('when'); 1055 | 1056 | 1057 | /** 1058 | * @ngdoc method 1059 | * @name ngMock.$httpBackend#expect 1060 | * @methodOf ngMock.$httpBackend 1061 | * @description 1062 | * Creates a new request expectation. 1063 | * 1064 | * @param {string} method HTTP method. 1065 | * @param {string|RegExp} url HTTP url. 1066 | * @param {(string|RegExp)=} data HTTP request body. 1067 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 1068 | * object and returns true if the headers match the current expectation. 1069 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1070 | * request is handled. 1071 | * 1072 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` 1073 | * – The respond method takes a set of static data to be returned or a function that can return 1074 | * an array containing response status (number), response data (string) and response headers 1075 | * (Object). 1076 | */ 1077 | $httpBackend.expect = function(method, url, data, headers) { 1078 | var expectation = new MockHttpExpectation(method, url, data, headers); 1079 | expectations.push(expectation); 1080 | return { 1081 | respond: function(status, data, headers) { 1082 | expectation.response = createResponse(status, data, headers); 1083 | } 1084 | }; 1085 | }; 1086 | 1087 | 1088 | /** 1089 | * @ngdoc method 1090 | * @name ngMock.$httpBackend#expectGET 1091 | * @methodOf ngMock.$httpBackend 1092 | * @description 1093 | * Creates a new request expectation for GET requests. For more info see `expect()`. 1094 | * 1095 | * @param {string|RegExp} url HTTP url. 1096 | * @param {Object=} headers HTTP headers. 1097 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1098 | * request is handled. See #expect for more info. 1099 | */ 1100 | 1101 | /** 1102 | * @ngdoc method 1103 | * @name ngMock.$httpBackend#expectHEAD 1104 | * @methodOf ngMock.$httpBackend 1105 | * @description 1106 | * Creates a new request expectation for HEAD requests. For more info see `expect()`. 1107 | * 1108 | * @param {string|RegExp} url HTTP url. 1109 | * @param {Object=} headers HTTP headers. 1110 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1111 | * request is handled. 1112 | */ 1113 | 1114 | /** 1115 | * @ngdoc method 1116 | * @name ngMock.$httpBackend#expectDELETE 1117 | * @methodOf ngMock.$httpBackend 1118 | * @description 1119 | * Creates a new request expectation for DELETE requests. For more info see `expect()`. 1120 | * 1121 | * @param {string|RegExp} url HTTP url. 1122 | * @param {Object=} headers HTTP headers. 1123 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1124 | * request is handled. 1125 | */ 1126 | 1127 | /** 1128 | * @ngdoc method 1129 | * @name ngMock.$httpBackend#expectPOST 1130 | * @methodOf ngMock.$httpBackend 1131 | * @description 1132 | * Creates a new request expectation for POST requests. For more info see `expect()`. 1133 | * 1134 | * @param {string|RegExp} url HTTP url. 1135 | * @param {(string|RegExp)=} data HTTP request body. 1136 | * @param {Object=} headers HTTP headers. 1137 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1138 | * request is handled. 1139 | */ 1140 | 1141 | /** 1142 | * @ngdoc method 1143 | * @name ngMock.$httpBackend#expectPUT 1144 | * @methodOf ngMock.$httpBackend 1145 | * @description 1146 | * Creates a new request expectation for PUT requests. For more info see `expect()`. 1147 | * 1148 | * @param {string|RegExp} url HTTP url. 1149 | * @param {(string|RegExp)=} data HTTP request body. 1150 | * @param {Object=} headers HTTP headers. 1151 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1152 | * request is handled. 1153 | */ 1154 | 1155 | /** 1156 | * @ngdoc method 1157 | * @name ngMock.$httpBackend#expectPATCH 1158 | * @methodOf ngMock.$httpBackend 1159 | * @description 1160 | * Creates a new request expectation for PATCH requests. For more info see `expect()`. 1161 | * 1162 | * @param {string|RegExp} url HTTP url. 1163 | * @param {(string|RegExp)=} data HTTP request body. 1164 | * @param {Object=} headers HTTP headers. 1165 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1166 | * request is handled. 1167 | */ 1168 | 1169 | /** 1170 | * @ngdoc method 1171 | * @name ngMock.$httpBackend#expectJSONP 1172 | * @methodOf ngMock.$httpBackend 1173 | * @description 1174 | * Creates a new request expectation for JSONP requests. For more info see `expect()`. 1175 | * 1176 | * @param {string|RegExp} url HTTP url. 1177 | * @returns {requestHandler} Returns an object with `respond` method that control how a matched 1178 | * request is handled. 1179 | */ 1180 | createShortMethods('expect'); 1181 | 1182 | 1183 | /** 1184 | * @ngdoc method 1185 | * @name ngMock.$httpBackend#flush 1186 | * @methodOf ngMock.$httpBackend 1187 | * @description 1188 | * Flushes all pending requests using the trained responses. 1189 | * 1190 | * @param {number=} count Number of responses to flush (in the order they arrived). If undefined, 1191 | * all pending requests will be flushed. If there are no pending requests when the flush method 1192 | * is called an exception is thrown (as this typically a sign of programming error). 1193 | */ 1194 | $httpBackend.flush = function(count) { 1195 | if (!responses.length) throw Error('No pending request to flush !'); 1196 | 1197 | if (angular.isDefined(count)) { 1198 | while (count--) { 1199 | if (!responses.length) throw Error('No more pending request to flush !'); 1200 | responses.shift()(); 1201 | } 1202 | } else { 1203 | while (responses.length) { 1204 | responses.shift()(); 1205 | } 1206 | } 1207 | $httpBackend.verifyNoOutstandingExpectation(); 1208 | }; 1209 | 1210 | 1211 | /** 1212 | * @ngdoc method 1213 | * @name ngMock.$httpBackend#verifyNoOutstandingExpectation 1214 | * @methodOf ngMock.$httpBackend 1215 | * @description 1216 | * Verifies that all of the requests defined via the `expect` api were made. If any of the 1217 | * requests were not made, verifyNoOutstandingExpectation throws an exception. 1218 | * 1219 | * Typically, you would call this method following each test case that asserts requests using an 1220 | * "afterEach" clause. 1221 | * 1222 | *
1223 |    *   afterEach($httpBackend.verifyExpectations);
1224 |    * 
1225 | */ 1226 | $httpBackend.verifyNoOutstandingExpectation = function() { 1227 | if (expectations.length) { 1228 | throw Error('Unsatisfied requests: ' + expectations.join(', ')); 1229 | } 1230 | }; 1231 | 1232 | 1233 | /** 1234 | * @ngdoc method 1235 | * @name ngMock.$httpBackend#verifyNoOutstandingRequest 1236 | * @methodOf ngMock.$httpBackend 1237 | * @description 1238 | * Verifies that there are no outstanding requests that need to be flushed. 1239 | * 1240 | * Typically, you would call this method following each test case that asserts requests using an 1241 | * "afterEach" clause. 1242 | * 1243 | *
1244 |    *   afterEach($httpBackend.verifyNoOutstandingRequest);
1245 |    * 
1246 | */ 1247 | $httpBackend.verifyNoOutstandingRequest = function() { 1248 | if (responses.length) { 1249 | throw Error('Unflushed requests: ' + responses.length); 1250 | } 1251 | }; 1252 | 1253 | 1254 | /** 1255 | * @ngdoc method 1256 | * @name ngMock.$httpBackend#resetExpectations 1257 | * @methodOf ngMock.$httpBackend 1258 | * @description 1259 | * Resets all request expectations, but preserves all backend definitions. Typically, you would 1260 | * call resetExpectations during a multiple-phase test when you want to reuse the same instance of 1261 | * $httpBackend mock. 1262 | */ 1263 | $httpBackend.resetExpectations = function() { 1264 | expectations.length = 0; 1265 | responses.length = 0; 1266 | }; 1267 | 1268 | return $httpBackend; 1269 | 1270 | 1271 | function createShortMethods(prefix) { 1272 | angular.forEach(['GET', 'DELETE', 'JSONP'], function(method) { 1273 | $httpBackend[prefix + method] = function(url, headers) { 1274 | return $httpBackend[prefix](method, url, undefined, headers) 1275 | } 1276 | }); 1277 | 1278 | angular.forEach(['PUT', 'POST', 'PATCH'], function(method) { 1279 | $httpBackend[prefix + method] = function(url, data, headers) { 1280 | return $httpBackend[prefix](method, url, data, headers) 1281 | } 1282 | }); 1283 | } 1284 | } 1285 | 1286 | function MockHttpExpectation(method, url, data, headers) { 1287 | 1288 | this.data = data; 1289 | this.headers = headers; 1290 | 1291 | this.match = function(m, u, d, h) { 1292 | if (method != m) return false; 1293 | if (!this.matchUrl(u)) return false; 1294 | if (angular.isDefined(d) && !this.matchData(d)) return false; 1295 | if (angular.isDefined(h) && !this.matchHeaders(h)) return false; 1296 | return true; 1297 | }; 1298 | 1299 | this.matchUrl = function(u) { 1300 | if (!url) return true; 1301 | if (angular.isFunction(url.test)) return url.test(u); 1302 | return url == u; 1303 | }; 1304 | 1305 | this.matchHeaders = function(h) { 1306 | if (angular.isUndefined(headers)) return true; 1307 | if (angular.isFunction(headers)) return headers(h); 1308 | return angular.equals(headers, h); 1309 | }; 1310 | 1311 | this.matchData = function(d) { 1312 | if (angular.isUndefined(data)) return true; 1313 | if (data && angular.isFunction(data.test)) return data.test(d); 1314 | if (data && !angular.isString(data)) return angular.toJson(data) == d; 1315 | return data == d; 1316 | }; 1317 | 1318 | this.toString = function() { 1319 | return method + ' ' + url; 1320 | }; 1321 | } 1322 | 1323 | function MockXhr() { 1324 | 1325 | // hack for testing $http, $httpBackend 1326 | MockXhr.$$lastInstance = this; 1327 | 1328 | this.open = function(method, url, async) { 1329 | this.$$method = method; 1330 | this.$$url = url; 1331 | this.$$async = async; 1332 | this.$$reqHeaders = {}; 1333 | this.$$respHeaders = {}; 1334 | }; 1335 | 1336 | this.send = function(data) { 1337 | this.$$data = data; 1338 | }; 1339 | 1340 | this.setRequestHeader = function(key, value) { 1341 | this.$$reqHeaders[key] = value; 1342 | }; 1343 | 1344 | this.getResponseHeader = function(name) { 1345 | // the lookup must be case insensitive, that's why we try two quick lookups and full scan at last 1346 | var header = this.$$respHeaders[name]; 1347 | if (header) return header; 1348 | 1349 | name = angular.lowercase(name); 1350 | header = this.$$respHeaders[name]; 1351 | if (header) return header; 1352 | 1353 | header = undefined; 1354 | angular.forEach(this.$$respHeaders, function(headerVal, headerName) { 1355 | if (!header && angular.lowercase(headerName) == name) header = headerVal; 1356 | }); 1357 | return header; 1358 | }; 1359 | 1360 | this.getAllResponseHeaders = function() { 1361 | var lines = []; 1362 | 1363 | angular.forEach(this.$$respHeaders, function(value, key) { 1364 | lines.push(key + ': ' + value); 1365 | }); 1366 | return lines.join('\n'); 1367 | }; 1368 | 1369 | this.abort = angular.noop; 1370 | } 1371 | 1372 | 1373 | /** 1374 | * @ngdoc function 1375 | * @name ngMock.$timeout 1376 | * @description 1377 | * 1378 | * This service is just a simple decorator for {@link ng.$timeout $timeout} service 1379 | * that adds a "flush" method. 1380 | */ 1381 | 1382 | /** 1383 | * @ngdoc method 1384 | * @name ngMock.$timeout#flush 1385 | * @methodOf ngMock.$timeout 1386 | * @description 1387 | * 1388 | * Flushes the queue of pending tasks. 1389 | */ 1390 | 1391 | /** 1392 | * 1393 | */ 1394 | angular.mock.$RootElementProvider = function() { 1395 | this.$get = function() { 1396 | return angular.element('
'); 1397 | } 1398 | }; 1399 | 1400 | /** 1401 | * @ngdoc overview 1402 | * @name ngMock 1403 | * @description 1404 | * 1405 | * The `ngMock` is an angular module which is used with `ng` module and adds unit-test configuration as well as useful 1406 | * mocks to the {@link AUTO.$injector $injector}. 1407 | */ 1408 | angular.module('ngMock', ['ng']).provider({ 1409 | $browser: angular.mock.$BrowserProvider, 1410 | $exceptionHandler: angular.mock.$ExceptionHandlerProvider, 1411 | $log: angular.mock.$LogProvider, 1412 | $httpBackend: angular.mock.$HttpBackendProvider, 1413 | $rootElement: angular.mock.$RootElementProvider 1414 | }).config(function($provide) { 1415 | $provide.decorator('$timeout', function($delegate, $browser) { 1416 | $delegate.flush = function() { 1417 | $browser.defer.flush(); 1418 | }; 1419 | return $delegate; 1420 | }); 1421 | }); 1422 | 1423 | 1424 | /** 1425 | * @ngdoc overview 1426 | * @name ngMockE2E 1427 | * @description 1428 | * 1429 | * The `ngMockE2E` is an angular module which contains mocks suitable for end-to-end testing. 1430 | * Currently there is only one mock present in this module - 1431 | * the {@link ngMockE2E.$httpBackend e2e $httpBackend} mock. 1432 | */ 1433 | angular.module('ngMockE2E', ['ng']).config(function($provide) { 1434 | $provide.decorator('$httpBackend', angular.mock.e2e.$httpBackendDecorator); 1435 | }); 1436 | 1437 | /** 1438 | * @ngdoc object 1439 | * @name ngMockE2E.$httpBackend 1440 | * @description 1441 | * Fake HTTP backend implementation suitable for end-to-end testing or backend-less development of 1442 | * applications that use the {@link ng.$http $http service}. 1443 | * 1444 | * *Note*: For fake http backend implementation suitable for unit testing please see 1445 | * {@link ngMock.$httpBackend unit-testing $httpBackend mock}. 1446 | * 1447 | * This implementation can be used to respond with static or dynamic responses via the `when` api 1448 | * and its shortcuts (`whenGET`, `whenPOST`, etc) and optionally pass through requests to the 1449 | * real $httpBackend for specific requests (e.g. to interact with certain remote apis or to fetch 1450 | * templates from a webserver). 1451 | * 1452 | * As opposed to unit-testing, in an end-to-end testing scenario or in scenario when an application 1453 | * is being developed with the real backend api replaced with a mock, it is often desirable for 1454 | * certain category of requests to bypass the mock and issue a real http request (e.g. to fetch 1455 | * templates or static files from the webserver). To configure the backend with this behavior 1456 | * use the `passThrough` request handler of `when` instead of `respond`. 1457 | * 1458 | * Additionally, we don't want to manually have to flush mocked out requests like we do during unit 1459 | * testing. For this reason the e2e $httpBackend automatically flushes mocked out requests 1460 | * automatically, closely simulating the behavior of the XMLHttpRequest object. 1461 | * 1462 | * To setup the application to run with this http backend, you have to create a module that depends 1463 | * on the `ngMockE2E` and your application modules and defines the fake backend: 1464 | * 1465 | *
1466 |  *   myAppDev = angular.module('myAppDev', ['myApp', 'ngMockE2E']);
1467 |  *   myAppDev.run(function($httpBackend) {
1468 |  *     phones = [{name: 'phone1'}, {name: 'phone2'}];
1469 |  *
1470 |  *     // returns the current list of phones
1471 |  *     $httpBackend.whenGET('/phones').respond(phones);
1472 |  *
1473 |  *     // adds a new phone to the phones array
1474 |  *     $httpBackend.whenPOST('/phones').respond(function(method, url, data) {
1475 |  *       phones.push(angular.fromJSON(data));
1476 |  *     });
1477 |  *     $httpBackend.whenGET(/^\/templates\//).passThrough();
1478 |  *     //...
1479 |  *   });
1480 |  * 
1481 | * 1482 | * Afterwards, bootstrap your app with this new module. 1483 | */ 1484 | 1485 | /** 1486 | * @ngdoc method 1487 | * @name ngMockE2E.$httpBackend#when 1488 | * @methodOf ngMockE2E.$httpBackend 1489 | * @description 1490 | * Creates a new backend definition. 1491 | * 1492 | * @param {string} method HTTP method. 1493 | * @param {string|RegExp} url HTTP url. 1494 | * @param {(string|RegExp)=} data HTTP request body. 1495 | * @param {(Object|function(Object))=} headers HTTP headers or function that receives http header 1496 | * object and returns true if the headers match the current definition. 1497 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1498 | * control how a matched request is handled. 1499 | * 1500 | * - respond – `{function([status,] data[, headers])|function(function(method, url, data, headers)}` 1501 | * – The respond method takes a set of static data to be returned or a function that can return 1502 | * an array containing response status (number), response data (string) and response headers 1503 | * (Object). 1504 | * - passThrough – `{function()}` – Any request matching a backend definition with `passThrough` 1505 | * handler, will be pass through to the real backend (an XHR request will be made to the 1506 | * server. 1507 | */ 1508 | 1509 | /** 1510 | * @ngdoc method 1511 | * @name ngMockE2E.$httpBackend#whenGET 1512 | * @methodOf ngMockE2E.$httpBackend 1513 | * @description 1514 | * Creates a new backend definition for GET requests. For more info see `when()`. 1515 | * 1516 | * @param {string|RegExp} url HTTP url. 1517 | * @param {(Object|function(Object))=} headers HTTP headers. 1518 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1519 | * control how a matched request is handled. 1520 | */ 1521 | 1522 | /** 1523 | * @ngdoc method 1524 | * @name ngMockE2E.$httpBackend#whenHEAD 1525 | * @methodOf ngMockE2E.$httpBackend 1526 | * @description 1527 | * Creates a new backend definition for HEAD requests. For more info see `when()`. 1528 | * 1529 | * @param {string|RegExp} url HTTP url. 1530 | * @param {(Object|function(Object))=} headers HTTP headers. 1531 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1532 | * control how a matched request is handled. 1533 | */ 1534 | 1535 | /** 1536 | * @ngdoc method 1537 | * @name ngMockE2E.$httpBackend#whenDELETE 1538 | * @methodOf ngMockE2E.$httpBackend 1539 | * @description 1540 | * Creates a new backend definition for DELETE requests. For more info see `when()`. 1541 | * 1542 | * @param {string|RegExp} url HTTP url. 1543 | * @param {(Object|function(Object))=} headers HTTP headers. 1544 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1545 | * control how a matched request is handled. 1546 | */ 1547 | 1548 | /** 1549 | * @ngdoc method 1550 | * @name ngMockE2E.$httpBackend#whenPOST 1551 | * @methodOf ngMockE2E.$httpBackend 1552 | * @description 1553 | * Creates a new backend definition for POST requests. For more info see `when()`. 1554 | * 1555 | * @param {string|RegExp} url HTTP url. 1556 | * @param {(string|RegExp)=} data HTTP request body. 1557 | * @param {(Object|function(Object))=} headers HTTP headers. 1558 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1559 | * control how a matched request is handled. 1560 | */ 1561 | 1562 | /** 1563 | * @ngdoc method 1564 | * @name ngMockE2E.$httpBackend#whenPUT 1565 | * @methodOf ngMockE2E.$httpBackend 1566 | * @description 1567 | * Creates a new backend definition for PUT requests. For more info see `when()`. 1568 | * 1569 | * @param {string|RegExp} url HTTP url. 1570 | * @param {(string|RegExp)=} data HTTP request body. 1571 | * @param {(Object|function(Object))=} headers HTTP headers. 1572 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1573 | * control how a matched request is handled. 1574 | */ 1575 | 1576 | /** 1577 | * @ngdoc method 1578 | * @name ngMockE2E.$httpBackend#whenPATCH 1579 | * @methodOf ngMockE2E.$httpBackend 1580 | * @description 1581 | * Creates a new backend definition for PATCH requests. For more info see `when()`. 1582 | * 1583 | * @param {string|RegExp} url HTTP url. 1584 | * @param {(string|RegExp)=} data HTTP request body. 1585 | * @param {(Object|function(Object))=} headers HTTP headers. 1586 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1587 | * control how a matched request is handled. 1588 | */ 1589 | 1590 | /** 1591 | * @ngdoc method 1592 | * @name ngMockE2E.$httpBackend#whenJSONP 1593 | * @methodOf ngMockE2E.$httpBackend 1594 | * @description 1595 | * Creates a new backend definition for JSONP requests. For more info see `when()`. 1596 | * 1597 | * @param {string|RegExp} url HTTP url. 1598 | * @returns {requestHandler} Returns an object with `respond` and `passThrough` methods that 1599 | * control how a matched request is handled. 1600 | */ 1601 | angular.mock.e2e = {}; 1602 | angular.mock.e2e.$httpBackendDecorator = ['$delegate', '$browser', createHttpBackendMock]; 1603 | 1604 | 1605 | angular.mock.clearDataCache = function() { 1606 | var key, 1607 | cache = angular.element.cache; 1608 | 1609 | for(key in cache) { 1610 | if (cache.hasOwnProperty(key)) { 1611 | var handle = cache[key].handle; 1612 | 1613 | handle && angular.element(handle.elem).unbind(); 1614 | delete cache[key]; 1615 | } 1616 | } 1617 | }; 1618 | 1619 | 1620 | window.jstestdriver && (function(window) { 1621 | /** 1622 | * Global method to output any number of objects into JSTD console. Useful for debugging. 1623 | */ 1624 | window.dump = function() { 1625 | var args = []; 1626 | angular.forEach(arguments, function(arg) { 1627 | args.push(angular.mock.dump(arg)); 1628 | }); 1629 | jstestdriver.console.log.apply(jstestdriver.console, args); 1630 | if (window.console) { 1631 | window.console.log.apply(window.console, args); 1632 | } 1633 | }; 1634 | })(window); 1635 | 1636 | 1637 | window.jasmine && (function(window) { 1638 | 1639 | afterEach(function() { 1640 | var spec = getCurrentSpec(); 1641 | var injector = spec.$injector; 1642 | 1643 | spec.$injector = null; 1644 | spec.$modules = null; 1645 | 1646 | if (injector) { 1647 | injector.get('$rootElement').unbind(); 1648 | injector.get('$browser').pollFns.length = 0; 1649 | } 1650 | 1651 | angular.mock.clearDataCache(); 1652 | 1653 | // clean up jquery's fragment cache 1654 | angular.forEach(angular.element.fragments, function(val, key) { 1655 | delete angular.element.fragments[key]; 1656 | }); 1657 | 1658 | MockXhr.$$lastInstance = null; 1659 | 1660 | angular.forEach(angular.callbacks, function(val, key) { 1661 | delete angular.callbacks[key]; 1662 | }); 1663 | angular.callbacks.counter = 0; 1664 | }); 1665 | 1666 | function getCurrentSpec() { 1667 | return jasmine.getEnv().currentSpec; 1668 | } 1669 | 1670 | function isSpecRunning() { 1671 | var spec = getCurrentSpec(); 1672 | return spec && spec.queue.running; 1673 | } 1674 | 1675 | /** 1676 | * @ngdoc function 1677 | * @name angular.mock.module 1678 | * @description 1679 | * 1680 | * *NOTE*: This function is also published on window for easy access.
1681 | * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}. 1682 | * 1683 | * This function registers a module configuration code. It collects the configuration information 1684 | * which will be used when the injector is created by {@link angular.mock.inject inject}. 1685 | * 1686 | * See {@link angular.mock.inject inject} for usage example 1687 | * 1688 | * @param {...(string|Function)} fns any number of modules which are represented as string 1689 | * aliases or as anonymous module initialization functions. The modules are used to 1690 | * configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. 1691 | */ 1692 | window.module = angular.mock.module = function() { 1693 | var moduleFns = Array.prototype.slice.call(arguments, 0); 1694 | return isSpecRunning() ? workFn() : workFn; 1695 | ///////////////////// 1696 | function workFn() { 1697 | var spec = getCurrentSpec(); 1698 | if (spec.$injector) { 1699 | throw Error('Injector already created, can not register a module!'); 1700 | } else { 1701 | var modules = spec.$modules || (spec.$modules = []); 1702 | angular.forEach(moduleFns, function(module) { 1703 | modules.push(module); 1704 | }); 1705 | } 1706 | } 1707 | }; 1708 | 1709 | /** 1710 | * @ngdoc function 1711 | * @name angular.mock.inject 1712 | * @description 1713 | * 1714 | * *NOTE*: This function is also published on window for easy access.
1715 | * *NOTE*: Only available with {@link http://pivotal.github.com/jasmine/ jasmine}. 1716 | * 1717 | * The inject function wraps a function into an injectable function. The inject() creates new 1718 | * instance of {@link AUTO.$injector $injector} per test, which is then used for 1719 | * resolving references. 1720 | * 1721 | * See also {@link angular.mock.module module} 1722 | * 1723 | * Example of what a typical jasmine tests looks like with the inject method. 1724 | *
1725 |    *
1726 |    *   angular.module('myApplicationModule', [])
1727 |    *       .value('mode', 'app')
1728 |    *       .value('version', 'v1.0.1');
1729 |    *
1730 |    *
1731 |    *   describe('MyApp', function() {
1732 |    *
1733 |    *     // You need to load modules that you want to test,
1734 |    *     // it loads only the "ng" module by default.
1735 |    *     beforeEach(module('myApplicationModule'));
1736 |    *
1737 |    *
1738 |    *     // inject() is used to inject arguments of all given functions
1739 |    *     it('should provide a version', inject(function(mode, version) {
1740 |    *       expect(version).toEqual('v1.0.1');
1741 |    *       expect(mode).toEqual('app');
1742 |    *     }));
1743 |    *
1744 |    *
1745 |    *     // The inject and module method can also be used inside of the it or beforeEach
1746 |    *     it('should override a version and test the new version is injected', function() {
1747 |    *       // module() takes functions or strings (module aliases)
1748 |    *       module(function($provide) {
1749 |    *         $provide.value('version', 'overridden'); // override version here
1750 |    *       });
1751 |    *
1752 |    *       inject(function(version) {
1753 |    *         expect(version).toEqual('overridden');
1754 |    *       });
1755 |    *     ));
1756 |    *   });
1757 |    *
1758 |    * 
1759 | * 1760 | * @param {...Function} fns any number of functions which will be injected using the injector. 1761 | */ 1762 | window.inject = angular.mock.inject = function() { 1763 | var blockFns = Array.prototype.slice.call(arguments, 0); 1764 | var errorForStack = new Error('Declaration Location'); 1765 | return isSpecRunning() ? workFn() : workFn; 1766 | ///////////////////// 1767 | function workFn() { 1768 | var spec = getCurrentSpec(); 1769 | var modules = spec.$modules || []; 1770 | modules.unshift('ngMock'); 1771 | modules.unshift('ng'); 1772 | var injector = spec.$injector; 1773 | if (!injector) { 1774 | injector = spec.$injector = angular.injector(modules); 1775 | } 1776 | for(var i = 0, ii = blockFns.length; i < ii; i++) { 1777 | try { 1778 | injector.invoke(blockFns[i] || angular.noop, this); 1779 | } catch (e) { 1780 | if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack; 1781 | throw e; 1782 | } finally { 1783 | errorForStack = null; 1784 | } 1785 | } 1786 | } 1787 | }; 1788 | })(window); -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | var allTestFiles = []; 2 | var TEST_REGEXP = /test/; 3 | 4 | Object.keys(window.__testacular__.files).forEach(function(file) { 5 | if (TEST_REGEXP.test(file)) { 6 | allTestFiles.push(file); 7 | } 8 | }); 9 | 10 | // Remove main.js from the list 11 | allTestFiles.shift(); 12 | 13 | require.config({ 14 | // Testacular serves files from '/base' 15 | baseUrl: '/base', 16 | 17 | paths : { 18 | "angular" : "src/public/js/lib/angular/angular" 19 | }, 20 | 21 | shim : { 22 | angular :{ 23 | exports : "angular" 24 | } 25 | }, 26 | 27 | // ask requirejs to load these files (all our tests) 28 | deps: allTestFiles, 29 | callback : window.__testacular__.start 30 | 31 | }); -------------------------------------------------------------------------------- /test/test.test.js: -------------------------------------------------------------------------------- 1 | define([], function () { 2 | 3 | describe('Testing', function () { 4 | it('should be true', function() { 5 | expect(false).toBe(false); 6 | }); 7 | it('should be false', function() { 8 | expect(false).toBe(false); 9 | }); 10 | }); 11 | }); -------------------------------------------------------------------------------- /test/unit/controllers.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* jasmine specs for controllers go here */ 3 | 4 | define(['src/public/js/Controllers/controllers'], function () { 5 | 6 | describe('MainCtrl', function () { 7 | var scope, ctrl; 8 | beforeEach(function() { 9 | scope = {}, 10 | ctrl = new MainCtrl(scope); 11 | }); 12 | 13 | 14 | it('should create a world variable set to "World"', function() { 15 | expect(scope.world).toBe("World"); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /test/unit/directives.test.js: -------------------------------------------------------------------------------- 1 | //'use strict'; 2 | 3 | /* jasmine specs for directives go here */ 4 | 5 | 6 | 7 | define(['angular','src/public/js/Directives/directives'], function (angular,Directives) { 8 | 9 | describe('directives', function() { 10 | 11 | var provide ; 12 | var app ; 13 | 14 | beforeEach(function() { 15 | app = angular.module("myApp", []); 16 | app.directive(Directives); 17 | angular.mock.module('myApp'); 18 | }); 19 | 20 | 21 | describe('app-version', function() { 22 | 23 | it('should print current version', function() { 24 | 25 | module('myApp',function($provide) { 26 | $provide.value('version', 'TEST_VER'); 27 | }); 28 | 29 | inject(function($compile, $rootScope) { 30 | var element = $compile('')($rootScope); 31 | expect(element.text()).toEqual('TEST_VER'); 32 | }); 33 | }); 34 | }); 35 | }); 36 | 37 | 38 | }); -------------------------------------------------------------------------------- /test/unit/filters.test.js: -------------------------------------------------------------------------------- 1 | //'use strict'; 2 | 3 | /* jasmine specs for filters go here */ 4 | 5 | define(['angular','src/public/js/Filters/filters'], function (angular, Filters) { 6 | 7 | describe('filter', function() { 8 | 9 | var app; 10 | 11 | beforeEach(function() { 12 | app = angular.module("myApp", []); 13 | Filters.initialize(app); 14 | angular.mock.module('myApp'); 15 | }); 16 | 17 | 18 | describe('interpolate', function() { 19 | beforeEach(module(function($provide) { 20 | $provide.value('version', 'TEST_VER'); 21 | })); 22 | 23 | 24 | it('should replace VERSION', inject(function(interpolateFilter) { 25 | expect(interpolateFilter('before %VERSION% after')).toEqual('before TEST_VER after'); 26 | })); 27 | }); 28 | }); 29 | }); -------------------------------------------------------------------------------- /test/unit/services.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* jasmine specs for services go here */ 3 | 4 | define(['angular','src/public/js/Services/services'], function (angular, Services) { 5 | 6 | describe('service', function() { 7 | 8 | var app ; 9 | 10 | beforeEach(function() { 11 | app = angular.module("myApp", []); 12 | app.factory(Services); 13 | angular.mock.module('myApp'); 14 | }); 15 | 16 | 17 | 18 | describe('version', function() { 19 | it('should contain a version service', inject(function(version) { 20 | expect(version).not.toBe(null); 21 | })); 22 | 23 | it('should return current version', inject(function(version) { 24 | expect(version).toEqual('0.1'); 25 | })); 26 | }); 27 | 28 | }); 29 | 30 | }); --------------------------------------------------------------------------------