diff options
Diffstat (limited to 'src/main/webapp/external/angular-1.0.3')
-rw-r--r-- | src/main/webapp/external/angular-1.0.3/angular-resource.js | 435 | ||||
-rw-r--r-- | src/main/webapp/external/angular-1.0.3/angular-resource.min.js | 10 | ||||
-rw-r--r-- | src/main/webapp/external/angular-1.0.3/angular.js | 14531 | ||||
-rw-r--r-- | src/main/webapp/external/angular-1.0.3/angular.min.js | 159 |
4 files changed, 0 insertions, 15135 deletions
diff --git a/src/main/webapp/external/angular-1.0.3/angular-resource.js b/src/main/webapp/external/angular-1.0.3/angular-resource.js deleted file mode 100644 index 8ebe5c8..0000000 --- a/src/main/webapp/external/angular-1.0.3/angular-resource.js +++ /dev/null @@ -1,435 +0,0 @@ -/** - * @license AngularJS v1.0.3 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, angular, undefined) { -'use strict'; - -/** - * @ngdoc overview - * @name ngResource - * @description - */ - - /** - * @ngdoc object - * @name ngResource.$resource - * @requires $http - * - * @description - * A factory which creates a resource object that lets you interact with - * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources. - * - * The returned resource object has action methods which provide high-level behaviors without - * the need to interact with the low level {@link ng.$http $http} service. - * - * @param {string} url A parameterized URL template with parameters prefixed by `:` as in - * `/user/:username`. - * - * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in - * `actions` methods. - * - * Each key value in the parameter object is first bound to url template if present and then any - * excess keys are appended to the url search query after the `?`. - * - * Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in - * URL `/path/greet?salutation=Hello`. - * - * If the parameter value is prefixed with `@` then the value of that parameter is extracted from - * the data object (useful for non-GET operations). - * - * @param {Object.<Object>=} actions Hash with declaration of custom action that should extend the - * default set of resource actions. The declaration should be created in the following format: - * - * {action1: {method:?, params:?, isArray:?}, - * action2: {method:?, params:?, isArray:?}, - * ...} - * - * Where: - * - * - `action` – {string} – The name of action. This name becomes the name of the method on your - * resource object. - * - `method` – {string} – HTTP request method. Valid methods are: `GET`, `POST`, `PUT`, `DELETE`, - * and `JSONP` - * - `params` – {object=} – Optional set of pre-bound parameters for this action. - * - isArray – {boolean=} – If true then the returned object for this action is an array, see - * `returns` section. - * - * @returns {Object} A resource "class" object with methods for the default set of resource actions - * optionally extended with custom `actions`. The default set contains these actions: - * - * { 'get': {method:'GET'}, - * 'save': {method:'POST'}, - * 'query': {method:'GET', isArray:true}, - * 'remove': {method:'DELETE'}, - * 'delete': {method:'DELETE'} }; - * - * Calling these methods invoke an {@link ng.$http} with the specified http method, - * destination and parameters. When the data is returned from the server then the object is an - * instance of the resource class `save`, `remove` and `delete` actions are available on it as - * methods with the `$` prefix. This allows you to easily perform CRUD operations (create, read, - * update, delete) on server-side data like this: - * <pre> - var User = $resource('/user/:userId', {userId:'@id'}); - var user = User.get({userId:123}, function() { - user.abc = true; - user.$save(); - }); - </pre> - * - * It is important to realize that invoking a $resource object method immediately returns an - * empty reference (object or array depending on `isArray`). Once the data is returned from the - * server the existing reference is populated with the actual data. This is a useful trick since - * usually the resource is assigned to a model which is then rendered by the view. Having an empty - * object results in no rendering, once the data arrives from the server then the object is - * populated with the data and the view automatically re-renders itself showing the new data. This - * means that in most case one never has to write a callback function for the action methods. - * - * The action methods on the class object or instance object can be invoked with the following - * parameters: - * - * - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])` - * - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])` - * - non-GET instance actions: `instance.$action([parameters], [success], [error])` - * - * - * @example - * - * # Credit card resource - * - * <pre> - // Define CreditCard class - var CreditCard = $resource('/user/:userId/card/:cardId', - {userId:123, cardId:'@id'}, { - charge: {method:'POST', params:{charge:true}} - }); - - // We can retrieve a collection from the server - var cards = CreditCard.query(function() { - // GET: /user/123/card - // server returns: [ {id:456, number:'1234', name:'Smith'} ]; - - var card = cards[0]; - // each item is an instance of CreditCard - expect(card instanceof CreditCard).toEqual(true); - card.name = "J. Smith"; - // non GET methods are mapped onto the instances - card.$save(); - // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'} - // server returns: {id:456, number:'1234', name: 'J. Smith'}; - - // our custom method is mapped as well. - card.$charge({amount:9.99}); - // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'} - }); - - // we can create an instance as well - var newCard = new CreditCard({number:'0123'}); - newCard.name = "Mike Smith"; - newCard.$save(); - // POST: /user/123/card {number:'0123', name:'Mike Smith'} - // server returns: {id:789, number:'01234', name: 'Mike Smith'}; - expect(newCard.id).toEqual(789); - * </pre> - * - * The object returned from this function execution is a resource "class" which has "static" method - * for each action in the definition. - * - * Calling these methods invoke `$http` on the `url` template with the given `method` and `params`. - * When the data is returned from the server then the object is an instance of the resource type and - * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD - * operations (create, read, update, delete) on server-side data. - - <pre> - var User = $resource('/user/:userId', {userId:'@id'}); - var user = User.get({userId:123}, function() { - user.abc = true; - user.$save(); - }); - </pre> - * - * It's worth noting that the success callback for `get`, `query` and other method gets passed - * in the response that came from the server as well as $http header getter function, so one - * could rewrite the above example and get access to http headers as: - * - <pre> - var User = $resource('/user/:userId', {userId:'@id'}); - User.get({userId:123}, function(u, getResponseHeaders){ - u.abc = true; - u.$save(function(u, putResponseHeaders) { - //u => saved user object - //putResponseHeaders => $http header getter - }); - }); - </pre> - - * # Buzz client - - Let's look at what a buzz client created with the `$resource` service looks like: - <doc:example> - <doc:source jsfiddle="false"> - <script> - function BuzzController($resource) { - this.userId = 'googlebuzz'; - this.Activity = $resource( - 'https://www.googleapis.com/buzz/v1/activities/:userId/:visibility/:activityId/:comments', - {alt:'json', callback:'JSON_CALLBACK'}, - {get:{method:'JSONP', params:{visibility:'@self'}}, replies: {method:'JSONP', params:{visibility:'@self', comments:'@comments'}}} - ); - } - - BuzzController.prototype = { - fetch: function() { - this.activities = this.Activity.get({userId:this.userId}); - }, - expandReplies: function(activity) { - activity.replies = this.Activity.replies({userId:this.userId, activityId:activity.id}); - } - }; - BuzzController.$inject = ['$resource']; - </script> - - <div ng-controller="BuzzController"> - <input ng-model="userId"/> - <button ng-click="fetch()">fetch</button> - <hr/> - <div ng-repeat="item in activities.data.items"> - <h1 style="font-size: 15px;"> - <img src="{{item.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/> - <a href="{{item.actor.profileUrl}}">{{item.actor.name}}</a> - <a href ng-click="expandReplies(item)" style="float: right;">Expand replies: {{item.links.replies[0].count}}</a> - </h1> - {{item.object.content | html}} - <div ng-repeat="reply in item.replies.data.items" style="margin-left: 20px;"> - <img src="{{reply.actor.thumbnailUrl}}" style="max-height:30px;max-width:30px;"/> - <a href="{{reply.actor.profileUrl}}">{{reply.actor.name}}</a>: {{reply.content | html}} - </div> - </div> - </div> - </doc:source> - <doc:scenario> - </doc:scenario> - </doc:example> - */ -angular.module('ngResource', ['ng']). - factory('$resource', ['$http', '$parse', function($http, $parse) { - var DEFAULT_ACTIONS = { - 'get': {method:'GET'}, - 'save': {method:'POST'}, - 'query': {method:'GET', isArray:true}, - 'remove': {method:'DELETE'}, - 'delete': {method:'DELETE'} - }; - var noop = angular.noop, - forEach = angular.forEach, - extend = angular.extend, - copy = angular.copy, - isFunction = angular.isFunction, - getter = function(obj, path) { - return $parse(path)(obj); - }; - - /** - * We need our custom mehtod because encodeURIComponent is too aggressive and doesn't follow - * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path - * segments: - * segment = *pchar - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * pct-encoded = "%" HEXDIG HEXDIG - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ - function encodeUriSegment(val) { - return encodeUriQuery(val, true). - replace(/%26/gi, '&'). - replace(/%3D/gi, '='). - replace(/%2B/gi, '+'); - } - - - /** - * This method is intended for encoding *key* or *value* parts of query component. We need a custom - * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be - * encoded per http://tools.ietf.org/html/rfc3986: - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ - function encodeUriQuery(val, pctEncodeSpaces) { - return encodeURIComponent(val). - replace(/%40/gi, '@'). - replace(/%3A/gi, ':'). - replace(/%24/g, '$'). - replace(/%2C/gi, ','). - replace((pctEncodeSpaces ? null : /%20/g), '+'); - } - - function Route(template, defaults) { - this.template = template = template + '#'; - this.defaults = defaults || {}; - var urlParams = this.urlParams = {}; - forEach(template.split(/\W/), function(param){ - if (param && template.match(new RegExp("[^\\\\]:" + param + "\\W"))) { - urlParams[param] = true; - } - }); - this.template = template.replace(/\\:/g, ':'); - } - - Route.prototype = { - url: function(params) { - var self = this, - url = this.template, - val, - encodedVal; - - params = params || {}; - forEach(this.urlParams, function(_, urlParam){ - val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam]; - if (angular.isDefined(val) && val !== null) { - encodedVal = encodeUriSegment(val); - url = url.replace(new RegExp(":" + urlParam + "(\\W)", "g"), encodedVal + "$1"); - } else { - url = url.replace(new RegExp("/?:" + urlParam + "(\\W)", "g"), '$1'); - } - }); - url = url.replace(/\/?#$/, ''); - var query = []; - forEach(params, function(value, key){ - if (!self.urlParams[key]) { - query.push(encodeUriQuery(key) + '=' + encodeUriQuery(value)); - } - }); - query.sort(); - url = url.replace(/\/*$/, ''); - return url + (query.length ? '?' + query.join('&') : ''); - } - }; - - - function ResourceFactory(url, paramDefaults, actions) { - var route = new Route(url); - - actions = extend({}, DEFAULT_ACTIONS, actions); - - function extractParams(data, actionParams){ - var ids = {}; - actionParams = extend({}, paramDefaults, actionParams); - forEach(actionParams, function(value, key){ - ids[key] = value.charAt && value.charAt(0) == '@' ? getter(data, value.substr(1)) : value; - }); - return ids; - } - - function Resource(value){ - copy(value || {}, this); - } - - forEach(actions, function(action, name) { - var hasBody = action.method == 'POST' || action.method == 'PUT' || action.method == 'PATCH'; - Resource[name] = function(a1, a2, a3, a4) { - var params = {}; - var data; - var success = noop; - var error = null; - switch(arguments.length) { - case 4: - error = a4; - success = a3; - //fallthrough - case 3: - case 2: - if (isFunction(a2)) { - if (isFunction(a1)) { - success = a1; - error = a2; - break; - } - - success = a2; - error = a3; - //fallthrough - } else { - params = a1; - data = a2; - success = a3; - break; - } - case 1: - if (isFunction(a1)) success = a1; - else if (hasBody) data = a1; - else params = a1; - break; - case 0: break; - default: - throw "Expected between 0-4 arguments [params, data, success, error], got " + - arguments.length + " arguments."; - } - - var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data)); - $http({ - method: action.method, - url: route.url(extend({}, extractParams(data, action.params || {}), params)), - data: data - }).then(function(response) { - var data = response.data; - - if (data) { - if (action.isArray) { - value.length = 0; - forEach(data, function(item) { - value.push(new Resource(item)); - }); - } else { - copy(data, value); - } - } - (success||noop)(value, response.headers); - }, error); - - return value; - }; - - - Resource.bind = function(additionalParamDefaults){ - return ResourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions); - }; - - - Resource.prototype['$' + name] = function(a1, a2, a3) { - var params = extractParams(this), - success = noop, - error; - - switch(arguments.length) { - case 3: params = a1; success = a2; error = a3; break; - case 2: - case 1: - if (isFunction(a1)) { - success = a1; - error = a2; - } else { - params = a1; - success = a2 || noop; - } - case 0: break; - default: - throw "Expected between 1-3 arguments [params, success, error], got " + - arguments.length + " arguments."; - } - var data = hasBody ? this : undefined; - Resource[name].call(this, params, data, success, error); - }; - }); - return Resource; - } - - return ResourceFactory; - }]); - -})(window, window.angular); diff --git a/src/main/webapp/external/angular-1.0.3/angular-resource.min.js b/src/main/webapp/external/angular-1.0.3/angular-resource.min.js deleted file mode 100644 index 816107d..0000000 --- a/src/main/webapp/external/angular-1.0.3/angular-resource.min.js +++ /dev/null @@ -1,10 +0,0 @@ -/* - AngularJS v1.0.3 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(A,e,w){'use strict';e.module("ngResource",["ng"]).factory("$resource",["$http","$parse",function(x,y){function k(a,f){return encodeURIComponent(a).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(f?null:/%20/g,"+")}function t(a,f){this.template=a+="#";this.defaults=f||{};var b=this.urlParams={};l(a.split(/\W/),function(d){d&&a.match(RegExp("[^\\\\]:"+d+"\\W"))&&(b[d]=!0)});this.template=a.replace(/\\:/g,":")}function u(a,f,b){function d(b,c){var a= -{},c=i({},f,c);l(c,function(o,c){var d;o.charAt&&o.charAt(0)=="@"?(d=o.substr(1),d=y(d)(b)):d=o;a[c]=d});return a}function h(a){v(a||{},this)}var e=new t(a),b=i({},z,b);l(b,function(g,c){var k=g.method=="POST"||g.method=="PUT"||g.method=="PATCH";h[c]=function(a,b,c,f){var s={},j,m=p,q=null;switch(arguments.length){case 4:q=f,m=c;case 3:case 2:if(r(b)){if(r(a)){m=a;q=b;break}m=b;q=c}else{s=a;j=b;m=c;break}case 1:r(a)?m=a:k?j=a:s=a;break;case 0:break;default:throw"Expected between 0-4 arguments [params, data, success, error], got "+ -arguments.length+" arguments.";}var n=this instanceof h?this:g.isArray?[]:new h(j);x({method:g.method,url:e.url(i({},d(j,g.params||{}),s)),data:j}).then(function(a){var b=a.data;if(b)g.isArray?(n.length=0,l(b,function(a){n.push(new h(a))})):v(b,n);(m||p)(n,a.headers)},q);return n};h.bind=function(c){return u(a,i({},f,c),b)};h.prototype["$"+c]=function(a,b,f){var g=d(this),e=p,j;switch(arguments.length){case 3:g=a;e=b;j=f;break;case 2:case 1:r(a)?(e=a,j=b):(g=a,e=b||p);case 0:break;default:throw"Expected between 1-3 arguments [params, success, error], got "+ -arguments.length+" arguments.";}h[c].call(this,g,k?this:w,e,j)}});return h}var z={get:{method:"GET"},save:{method:"POST"},query:{method:"GET",isArray:!0},remove:{method:"DELETE"},"delete":{method:"DELETE"}},p=e.noop,l=e.forEach,i=e.extend,v=e.copy,r=e.isFunction;t.prototype={url:function(a){var f=this,b=this.template,d,h,a=a||{};l(this.urlParams,function(g,c){d=a.hasOwnProperty(c)?a[c]:f.defaults[c];e.isDefined(d)&&d!==null?(h=k(d,!0).replace(/%26/gi,"&").replace(/%3D/gi,"=").replace(/%2B/gi,"+"), -b=b.replace(RegExp(":"+c+"(\\W)","g"),h+"$1")):b=b.replace(RegExp("/?:"+c+"(\\W)","g"),"$1")});var b=b.replace(/\/?#$/,""),i=[];l(a,function(a,b){f.urlParams[b]||i.push(k(b)+"="+k(a))});i.sort();b=b.replace(/\/*$/,"");return b+(i.length?"?"+i.join("&"):"")}};return u}])})(window,window.angular); diff --git a/src/main/webapp/external/angular-1.0.3/angular.js b/src/main/webapp/external/angular-1.0.3/angular.js deleted file mode 100644 index 9e40471..0000000 --- a/src/main/webapp/external/angular-1.0.3/angular.js +++ /dev/null @@ -1,14531 +0,0 @@ -/** - * @license AngularJS v1.0.3 - * (c) 2010-2012 Google, Inc. http://angularjs.org - * License: MIT - */ -(function(window, document, undefined) { -'use strict'; - -//////////////////////////////////// - -/** - * @ngdoc function - * @name angular.lowercase - * @function - * - * @description Converts the specified string to lowercase. - * @param {string} string String to be converted to lowercase. - * @returns {string} Lowercased string. - */ -var lowercase = function(string){return isString(string) ? string.toLowerCase() : string;}; - - -/** - * @ngdoc function - * @name angular.uppercase - * @function - * - * @description Converts the specified string to uppercase. - * @param {string} string String to be converted to uppercase. - * @returns {string} Uppercased string. - */ -var uppercase = function(string){return isString(string) ? string.toUpperCase() : string;}; - - -var manualLowercase = function(s) { - return isString(s) - ? s.replace(/[A-Z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) | 32);}) - : s; -}; -var manualUppercase = function(s) { - return isString(s) - ? s.replace(/[a-z]/g, function(ch) {return fromCharCode(ch.charCodeAt(0) & ~32);}) - : s; -}; - - -// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish -// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods -// with correct but slower alternatives. -if ('i' !== 'I'.toLowerCase()) { - lowercase = manualLowercase; - uppercase = manualUppercase; -} - -function fromCharCode(code) {return String.fromCharCode(code);} - - -var Error = window.Error, - /** holds major version number for IE or NaN for real browsers */ - msie = int((/msie (\d+)/.exec(lowercase(navigator.userAgent)) || [])[1]), - jqLite, // delay binding since jQuery could be loaded after us. - jQuery, // delay binding - slice = [].slice, - push = [].push, - toString = Object.prototype.toString, - - /** @name angular */ - angular = window.angular || (window.angular = {}), - angularModule, - nodeName_, - uid = ['0', '0', '0']; - -/** - * @ngdoc function - * @name angular.forEach - * @function - * - * @description - * Invokes the `iterator` function once for each item in `obj` collection, which can be either an - * object or an array. The `iterator` function is invoked with `iterator(value, key)`, where `value` - * is the value of an object property or an array element and `key` is the object property key or - * array element index. Specifying a `context` for the function is optional. - * - * Note: this function was previously known as `angular.foreach`. - * - <pre> - var values = {name: 'misko', gender: 'male'}; - var log = []; - angular.forEach(values, function(value, key){ - this.push(key + ': ' + value); - }, log); - expect(log).toEqual(['name: misko', 'gender:male']); - </pre> - * - * @param {Object|Array} obj Object to iterate over. - * @param {Function} iterator Iterator function. - * @param {Object=} context Object to become context (`this`) for the iterator function. - * @returns {Object|Array} Reference to `obj`. - */ -function forEach(obj, iterator, context) { - var key; - if (obj) { - if (isFunction(obj)){ - for (key in obj) { - if (key != 'prototype' && key != 'length' && key != 'name' && obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key); - } - } - } else if (obj.forEach && obj.forEach !== forEach) { - obj.forEach(iterator, context); - } else if (isObject(obj) && isNumber(obj.length)) { - for (key = 0; key < obj.length; key++) - iterator.call(context, obj[key], key); - } else { - for (key in obj) { - if (obj.hasOwnProperty(key)) { - iterator.call(context, obj[key], key); - } - } - } - } - return obj; -} - -function sortedKeys(obj) { - var keys = []; - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - keys.push(key); - } - } - return keys.sort(); -} - -function forEachSorted(obj, iterator, context) { - var keys = sortedKeys(obj); - for ( var i = 0; i < keys.length; i++) { - iterator.call(context, obj[keys[i]], keys[i]); - } - return keys; -} - - -/** - * when using forEach the params are value, key, but it is often useful to have key, value. - * @param {function(string, *)} iteratorFn - * @returns {function(*, string)} - */ -function reverseParams(iteratorFn) { - return function(value, key) { iteratorFn(key, value) }; -} - -/** - * A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric - * characters such as '012ABC'. The reason why we are not using simply a number counter is that - * the number string gets longer over time, and it can also overflow, where as the the nextId - * will grow much slower, it is a string, and it will never overflow. - * - * @returns an unique alpha-numeric string - */ -function nextUid() { - var index = uid.length; - var digit; - - while(index) { - index--; - digit = uid[index].charCodeAt(0); - if (digit == 57 /*'9'*/) { - uid[index] = 'A'; - return uid.join(''); - } - if (digit == 90 /*'Z'*/) { - uid[index] = '0'; - } else { - uid[index] = String.fromCharCode(digit + 1); - return uid.join(''); - } - } - uid.unshift('0'); - return uid.join(''); -} - -/** - * @ngdoc function - * @name angular.extend - * @function - * - * @description - * Extends the destination object `dst` by copying all of the properties from the `src` object(s) - * to `dst`. You can specify multiple `src` objects. - * - * @param {Object} dst Destination object. - * @param {...Object} src Source object(s). - */ -function extend(dst) { - forEach(arguments, function(obj){ - if (obj !== dst) { - forEach(obj, function(value, key){ - dst[key] = value; - }); - } - }); - return dst; -} - -function int(str) { - return parseInt(str, 10); -} - - -function inherit(parent, extra) { - return extend(new (extend(function() {}, {prototype:parent}))(), extra); -} - - -/** - * @ngdoc function - * @name angular.noop - * @function - * - * @description - * A function that performs no operations. This function can be useful when writing code in the - * functional style. - <pre> - function foo(callback) { - var result = calculateResult(); - (callback || angular.noop)(result); - } - </pre> - */ -function noop() {} -noop.$inject = []; - - -/** - * @ngdoc function - * @name angular.identity - * @function - * - * @description - * A function that returns its first argument. This function is useful when writing code in the - * functional style. - * - <pre> - function transformer(transformationFn, value) { - return (transformationFn || identity)(value); - }; - </pre> - */ -function identity($) {return $;} -identity.$inject = []; - - -function valueFn(value) {return function() {return value;};} - -/** - * @ngdoc function - * @name angular.isUndefined - * @function - * - * @description - * Determines if a reference is undefined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is undefined. - */ -function isUndefined(value){return typeof value == 'undefined';} - - -/** - * @ngdoc function - * @name angular.isDefined - * @function - * - * @description - * Determines if a reference is defined. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is defined. - */ -function isDefined(value){return typeof value != 'undefined';} - - -/** - * @ngdoc function - * @name angular.isObject - * @function - * - * @description - * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not - * considered to be objects. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Object` but not `null`. - */ -function isObject(value){return value != null && typeof value == 'object';} - - -/** - * @ngdoc function - * @name angular.isString - * @function - * - * @description - * Determines if a reference is a `String`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `String`. - */ -function isString(value){return typeof value == 'string';} - - -/** - * @ngdoc function - * @name angular.isNumber - * @function - * - * @description - * Determines if a reference is a `Number`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Number`. - */ -function isNumber(value){return typeof value == 'number';} - - -/** - * @ngdoc function - * @name angular.isDate - * @function - * - * @description - * Determines if a value is a date. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Date`. - */ -function isDate(value){ - return toString.apply(value) == '[object Date]'; -} - - -/** - * @ngdoc function - * @name angular.isArray - * @function - * - * @description - * Determines if a reference is an `Array`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is an `Array`. - */ -function isArray(value) { - return toString.apply(value) == '[object Array]'; -} - - -/** - * @ngdoc function - * @name angular.isFunction - * @function - * - * @description - * Determines if a reference is a `Function`. - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a `Function`. - */ -function isFunction(value){return typeof value == 'function';} - - -/** - * Checks if `obj` is a window object. - * - * @private - * @param {*} obj Object to check - * @returns {boolean} True if `obj` is a window obj. - */ -function isWindow(obj) { - return obj && obj.document && obj.location && obj.alert && obj.setInterval; -} - - -function isScope(obj) { - return obj && obj.$evalAsync && obj.$watch; -} - - -function isFile(obj) { - return toString.apply(obj) === '[object File]'; -} - - -function isBoolean(value) { - return typeof value == 'boolean'; -} - - -function trim(value) { - return isString(value) ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; -} - -/** - * @ngdoc function - * @name angular.isElement - * @function - * - * @description - * Determines if a reference is a DOM element (or wrapped jQuery element). - * - * @param {*} value Reference to check. - * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element). - */ -function isElement(node) { - return node && - (node.nodeName // we are a direct element - || (node.bind && node.find)); // we have a bind and find method part of jQuery API -} - -/** - * @param str 'key1,key2,...' - * @returns {object} in the form of {key1:true, key2:true, ...} - */ -function makeMap(str){ - var obj = {}, items = str.split(","), i; - for ( i = 0; i < items.length; i++ ) - obj[ items[i] ] = true; - return obj; -} - - -if (msie < 9) { - nodeName_ = function(element) { - element = element.nodeName ? element : element[0]; - return (element.scopeName && element.scopeName != 'HTML') - ? uppercase(element.scopeName + ':' + element.nodeName) : element.nodeName; - }; -} else { - nodeName_ = function(element) { - return element.nodeName ? element.nodeName : element[0].nodeName; - }; -} - - -function map(obj, iterator, context) { - var results = []; - forEach(obj, function(value, index, list) { - results.push(iterator.call(context, value, index, list)); - }); - return results; -} - - -/** - * @description - * Determines the number of elements in an array, the number of properties an object has, or - * the length of a string. - * - * Note: This function is used to augment the Object type in Angular expressions. See - * {@link angular.Object} for more information about Angular arrays. - * - * @param {Object|Array|string} obj Object, array, or string to inspect. - * @param {boolean} [ownPropsOnly=false] Count only "own" properties in an object - * @returns {number} The size of `obj` or `0` if `obj` is neither an object nor an array. - */ -function size(obj, ownPropsOnly) { - var size = 0, key; - - if (isArray(obj) || isString(obj)) { - return obj.length; - } else if (isObject(obj)){ - for (key in obj) - if (!ownPropsOnly || obj.hasOwnProperty(key)) - size++; - } - - return size; -} - - -function includes(array, obj) { - return indexOf(array, obj) != -1; -} - -function indexOf(array, obj) { - if (array.indexOf) return array.indexOf(obj); - - for ( var i = 0; i < array.length; i++) { - if (obj === array[i]) return i; - } - return -1; -} - -function arrayRemove(array, value) { - var index = indexOf(array, value); - if (index >=0) - array.splice(index, 1); - return value; -} - -function isLeafNode (node) { - if (node) { - switch (node.nodeName) { - case "OPTION": - case "PRE": - case "TITLE": - return true; - } - } - return false; -} - -/** - * @ngdoc function - * @name angular.copy - * @function - * - * @description - * Creates a deep copy of `source`, which should be an object or an array. - * - * * If no destination is supplied, a copy of the object or array is created. - * * If a destination is provided, all of its elements (for array) or properties (for objects) - * are deleted and then all elements/properties from the source are copied to it. - * * If `source` is not an object or array, `source` is returned. - * - * Note: this function is used to augment the Object type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * - * @param {*} source The source that will be used to make a copy. - * Can be any type, including primitives, `null`, and `undefined`. - * @param {(Object|Array)=} destination Destination into which the source is copied. If - * provided, must be of the same type as `source`. - * @returns {*} The copy or updated `destination`, if `destination` was specified. - */ -function copy(source, destination){ - if (isWindow(source) || isScope(source)) throw Error("Can't copy Window or Scope"); - if (!destination) { - destination = source; - if (source) { - if (isArray(source)) { - destination = copy(source, []); - } else if (isDate(source)) { - destination = new Date(source.getTime()); - } else if (isObject(source)) { - destination = copy(source, {}); - } - } - } else { - if (source === destination) throw Error("Can't copy equivalent objects or arrays"); - if (isArray(source)) { - while(destination.length) { - destination.pop(); - } - for ( var i = 0; i < source.length; i++) { - destination.push(copy(source[i])); - } - } else { - forEach(destination, function(value, key){ - delete destination[key]; - }); - for ( var key in source) { - destination[key] = copy(source[key]); - } - } - } - return destination; -} - -/** - * Create a shallow copy of an object - */ -function shallowCopy(src, dst) { - dst = dst || {}; - - for(var key in src) { - if (src.hasOwnProperty(key) && key.substr(0, 2) !== '$$') { - dst[key] = src[key]; - } - } - - return dst; -} - - -/** - * @ngdoc function - * @name angular.equals - * @function - * - * @description - * Determines if two objects or two values are equivalent. Supports value types, arrays and - * objects. - * - * Two objects or values are considered equivalent if at least one of the following is true: - * - * * Both objects or values pass `===` comparison. - * * Both objects or values are of the same type and all of their properties pass `===` comparison. - * * Both values are NaN. (In JavasScript, NaN == NaN => false. But we consider two NaN as equal) - * - * During a property comparision, properties of `function` type and properties with names - * that begin with `$` are ignored. - * - * Scope and DOMWindow objects are being compared only be identify (`===`). - * - * @param {*} o1 Object or value to compare. - * @param {*} o2 Object or value to compare. - * @returns {boolean} True if arguments are equal. - */ -function equals(o1, o2) { - if (o1 === o2) return true; - if (o1 === null || o2 === null) return false; - if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN - var t1 = typeof o1, t2 = typeof o2, length, key, keySet; - if (t1 == t2) { - if (t1 == 'object') { - if (isArray(o1)) { - if ((length = o1.length) == o2.length) { - for(key=0; key<length; key++) { - if (!equals(o1[key], o2[key])) return false; - } - return true; - } - } else if (isDate(o1)) { - return isDate(o2) && o1.getTime() == o2.getTime(); - } else { - if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2)) return false; - keySet = {}; - for(key in o1) { - if (key.charAt(0) !== '$' && !isFunction(o1[key]) && !equals(o1[key], o2[key])) { - return false; - } - keySet[key] = true; - } - for(key in o2) { - if (!keySet[key] && key.charAt(0) !== '$' && !isFunction(o2[key])) return false; - } - return true; - } - } - } - return false; -} - - -function concat(array1, array2, index) { - return array1.concat(slice.call(array2, index)); -} - -function sliceArgs(args, startIndex) { - return slice.call(args, startIndex || 0); -} - - -/** - * @ngdoc function - * @name angular.bind - * @function - * - * @description - * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for - * `fn`). You can supply optional `args` that are are prebound to the function. This feature is also - * known as [function currying](http://en.wikipedia.org/wiki/Currying). - * - * @param {Object} self Context which `fn` should be evaluated in. - * @param {function()} fn Function to be bound. - * @param {...*} args Optional arguments to be prebound to the `fn` function call. - * @returns {function()} Function that wraps the `fn` with all the specified bindings. - */ -function bind(self, fn) { - var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : []; - if (isFunction(fn) && !(fn instanceof RegExp)) { - return curryArgs.length - ? function() { - return arguments.length - ? fn.apply(self, curryArgs.concat(slice.call(arguments, 0))) - : fn.apply(self, curryArgs); - } - : function() { - return arguments.length - ? fn.apply(self, arguments) - : fn.call(self); - }; - } else { - // in IE, native methods are not functions so they cannot be bound (note: they don't need to be) - return fn; - } -} - - -function toJsonReplacer(key, value) { - var val = value; - - if (/^\$+/.test(key)) { - val = undefined; - } else if (isWindow(value)) { - val = '$WINDOW'; - } else if (value && document === value) { - val = '$DOCUMENT'; - } else if (isScope(value)) { - val = '$SCOPE'; - } - - return val; -} - - -/** - * @ngdoc function - * @name angular.toJson - * @function - * - * @description - * Serializes input into a JSON-formatted string. - * - * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON. - * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace. - * @returns {string} Jsonified string representing `obj`. - */ -function toJson(obj, pretty) { - return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null); -} - - -/** - * @ngdoc function - * @name angular.fromJson - * @function - * - * @description - * Deserializes a JSON string. - * - * @param {string} json JSON string to deserialize. - * @returns {Object|Array|Date|string|number} Deserialized thingy. - */ -function fromJson(json) { - return isString(json) - ? JSON.parse(json) - : json; -} - - -function toBoolean(value) { - if (value && value.length !== 0) { - var v = lowercase("" + value); - value = !(v == 'f' || v == '0' || v == 'false' || v == 'no' || v == 'n' || v == '[]'); - } else { - value = false; - } - return value; -} - -/** - * @returns {string} Returns the string representation of the element. - */ -function startingTag(element) { - element = jqLite(element).clone(); - try { - // turns out IE does not let you set .html() on elements which - // are not allowed to have children. So we just ignore it. - element.html(''); - } catch(e) {} - return jqLite('<div>').append(element).html(). - match(/^(<[^>]+>)/)[1]. - replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); }); -} - - -///////////////////////////////////////////////// - -/** - * Parses an escaped url query string into key-value pairs. - * @returns Object.<(string|boolean)> - */ -function parseKeyValue(/**string*/keyValue) { - var obj = {}, key_value, key; - forEach((keyValue || "").split('&'), function(keyValue){ - if (keyValue) { - key_value = keyValue.split('='); - key = decodeURIComponent(key_value[0]); - obj[key] = isDefined(key_value[1]) ? decodeURIComponent(key_value[1]) : true; - } - }); - return obj; -} - -function toKeyValue(obj) { - var parts = []; - forEach(obj, function(value, key) { - parts.push(encodeUriQuery(key, true) + (value === true ? '' : '=' + encodeUriQuery(value, true))); - }); - return parts.length ? parts.join('&') : ''; -} - - -/** - * We need our custom method because encodeURIComponent is too agressive and doesn't follow - * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path - * segments: - * segment = *pchar - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * pct-encoded = "%" HEXDIG HEXDIG - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriSegment(val) { - return encodeUriQuery(val, true). - replace(/%26/gi, '&'). - replace(/%3D/gi, '='). - replace(/%2B/gi, '+'); -} - - -/** - * This method is intended for encoding *key* or *value* parts of query component. We need a custom - * method becuase encodeURIComponent is too agressive and encodes stuff that doesn't have to be - * encoded per http://tools.ietf.org/html/rfc3986: - * query = *( pchar / "/" / "?" ) - * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" - * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" - * pct-encoded = "%" HEXDIG HEXDIG - * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" - * / "*" / "+" / "," / ";" / "=" - */ -function encodeUriQuery(val, pctEncodeSpaces) { - return encodeURIComponent(val). - replace(/%40/gi, '@'). - replace(/%3A/gi, ':'). - replace(/%24/g, '$'). - replace(/%2C/gi, ','). - replace((pctEncodeSpaces ? null : /%20/g), '+'); -} - - -/** - * @ngdoc directive - * @name ng.directive:ngApp - * - * @element ANY - * @param {angular.Module} ngApp an optional application - * {@link angular.module module} name to load. - * - * @description - * - * Use this directive to auto-bootstrap on application. Only - * one directive can be used per HTML document. The directive - * designates the root of the application and is typically placed - * ot the root of the page. - * - * In the example below if the `ngApp` directive would not be placed - * on the `html` element then the document would not be compiled - * and the `{{ 1+2 }}` would not be resolved to `3`. - * - * `ngApp` is the easiest way to bootstrap an application. - * - <doc:example> - <doc:source> - I can add: 1 + 2 = {{ 1+2 }} - </doc:source> - </doc:example> - * - */ -function angularInit(element, bootstrap) { - var elements = [element], - appElement, - module, - names = ['ng:app', 'ng-app', 'x-ng-app', 'data-ng-app'], - NG_APP_CLASS_REGEXP = /\sng[:\-]app(:\s*([\w\d_]+);?)?\s/; - - function append(element) { - element && elements.push(element); - } - - forEach(names, function(name) { - names[name] = true; - append(document.getElementById(name)); - name = name.replace(':', '\\:'); - if (element.querySelectorAll) { - forEach(element.querySelectorAll('.' + name), append); - forEach(element.querySelectorAll('.' + name + '\\:'), append); - forEach(element.querySelectorAll('[' + name + ']'), append); - } - }); - - forEach(elements, function(element) { - if (!appElement) { - var className = ' ' + element.className + ' '; - var match = NG_APP_CLASS_REGEXP.exec(className); - if (match) { - appElement = element; - module = (match[2] || '').replace(/\s+/g, ','); - } else { - forEach(element.attributes, function(attr) { - if (!appElement && names[attr.name]) { - appElement = element; - module = attr.value; - } - }); - } - } - }); - if (appElement) { - bootstrap(appElement, module ? [module] : []); - } -} - -/** - * @ngdoc function - * @name angular.bootstrap - * @description - * Use this function to manually start up angular application. - * - * See: {@link guide/bootstrap Bootstrap} - * - * @param {Element} element DOM element which is the root of angular application. - * @param {Array<String|Function>=} modules an array of module declarations. See: {@link angular.module modules} - * @returns {AUTO.$injector} Returns the newly created injector for this app. - */ -function bootstrap(element, modules) { - element = jqLite(element); - modules = modules || []; - modules.unshift(['$provide', function($provide) { - $provide.value('$rootElement', element); - }]); - modules.unshift('ng'); - var injector = createInjector(modules); - injector.invoke( - ['$rootScope', '$rootElement', '$compile', '$injector', function(scope, element, compile, injector){ - scope.$apply(function() { - element.data('$injector', injector); - compile(element)(scope); - }); - }] - ); - return injector; -} - -var SNAKE_CASE_REGEXP = /[A-Z]/g; -function snake_case(name, separator){ - separator = separator || '_'; - return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) { - return (pos ? separator : '') + letter.toLowerCase(); - }); -} - -function bindJQuery() { - // bind to jQuery if present; - jQuery = window.jQuery; - // reset to jQuery or default to us. - if (jQuery) { - jqLite = jQuery; - extend(jQuery.fn, { - scope: JQLitePrototype.scope, - controller: JQLitePrototype.controller, - injector: JQLitePrototype.injector, - inheritedData: JQLitePrototype.inheritedData - }); - JQLitePatchJQueryRemove('remove', true); - JQLitePatchJQueryRemove('empty'); - JQLitePatchJQueryRemove('html'); - } else { - jqLite = JQLite; - } - angular.element = jqLite; -} - -/** - * throw error of the argument is falsy. - */ -function assertArg(arg, name, reason) { - if (!arg) { - throw new Error("Argument '" + (name || '?') + "' is " + (reason || "required")); - } - return arg; -} - -function assertArgFn(arg, name, acceptArrayAnnotation) { - if (acceptArrayAnnotation && isArray(arg)) { - arg = arg[arg.length - 1]; - } - - assertArg(isFunction(arg), name, 'not a function, got ' + - (arg && typeof arg == 'object' ? arg.constructor.name || 'Object' : typeof arg)); - return arg; -} - -/** - * @ngdoc interface - * @name angular.Module - * @description - * - * Interface for configuring angular {@link angular.module modules}. - */ - -function setupModuleLoader(window) { - - function ensure(obj, name, factory) { - return obj[name] || (obj[name] = factory()); - } - - return ensure(ensure(window, 'angular', Object), 'module', function() { - /** @type {Object.<string, angular.Module>} */ - var modules = {}; - - /** - * @ngdoc function - * @name angular.module - * @description - * - * The `angular.module` is a global place for creating and registering Angular modules. All - * modules (angular core or 3rd party) that should be available to an application must be - * registered using this mechanism. - * - * - * # Module - * - * A module is a collocation of services, directives, filters, and configuration information. Module - * is used to configure the {@link AUTO.$injector $injector}. - * - * <pre> - * // Create a new module - * var myModule = angular.module('myModule', []); - * - * // register a new service - * myModule.value('appName', 'MyCoolApp'); - * - * // configure existing services inside initialization blocks. - * myModule.config(function($locationProvider) { - * // Configure existing providers - * $locationProvider.hashPrefix('!'); - * }); - * </pre> - * - * Then you can create an injector and load your modules like this: - * - * <pre> - * var injector = angular.injector(['ng', 'MyModule']) - * </pre> - * - * However it's more likely that you'll just use - * {@link ng.directive:ngApp ngApp} or - * {@link angular.bootstrap} to simplify this process for you. - * - * @param {!string} name The name of the module to create or retrieve. - * @param {Array.<string>=} requires If specified then new module is being created. If unspecified then the - * the module is being retrieved for further configuration. - * @param {Function} configFn Optional configuration function for the module. Same as - * {@link angular.Module#config Module#config()}. - * @returns {module} new module with the {@link angular.Module} api. - */ - return function module(name, requires, configFn) { - if (requires && modules.hasOwnProperty(name)) { - modules[name] = null; - } - return ensure(modules, name, function() { - if (!requires) { - throw Error('No module: ' + name); - } - - /** @type {!Array.<Array.<*>>} */ - var invokeQueue = []; - - /** @type {!Array.<Function>} */ - var runBlocks = []; - - var config = invokeLater('$injector', 'invoke'); - - /** @type {angular.Module} */ - var moduleInstance = { - // Private state - _invokeQueue: invokeQueue, - _runBlocks: runBlocks, - - /** - * @ngdoc property - * @name angular.Module#requires - * @propertyOf angular.Module - * @returns {Array.<string>} List of module names which must be loaded before this module. - * @description - * Holds the list of modules which the injector will load before the current module is loaded. - */ - requires: requires, - - /** - * @ngdoc property - * @name angular.Module#name - * @propertyOf angular.Module - * @returns {string} Name of the module. - * @description - */ - name: name, - - - /** - * @ngdoc method - * @name angular.Module#provider - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerType Construction function for creating new instance of the service. - * @description - * See {@link AUTO.$provide#provider $provide.provider()}. - */ - provider: invokeLater('$provide', 'provider'), - - /** - * @ngdoc method - * @name angular.Module#factory - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} providerFunction Function for creating new instance of the service. - * @description - * See {@link AUTO.$provide#factory $provide.factory()}. - */ - factory: invokeLater('$provide', 'factory'), - - /** - * @ngdoc method - * @name angular.Module#service - * @methodOf angular.Module - * @param {string} name service name - * @param {Function} constructor A constructor function that will be instantiated. - * @description - * See {@link AUTO.$provide#service $provide.service()}. - */ - service: invokeLater('$provide', 'service'), - - /** - * @ngdoc method - * @name angular.Module#value - * @methodOf angular.Module - * @param {string} name service name - * @param {*} object Service instance object. - * @description - * See {@link AUTO.$provide#value $provide.value()}. - */ - value: invokeLater('$provide', 'value'), - - /** - * @ngdoc method - * @name angular.Module#constant - * @methodOf angular.Module - * @param {string} name constant name - * @param {*} object Constant value. - * @description - * Because the constant are fixed, they get applied before other provide methods. - * See {@link AUTO.$provide#constant $provide.constant()}. - */ - constant: invokeLater('$provide', 'constant', 'unshift'), - - /** - * @ngdoc method - * @name angular.Module#filter - * @methodOf angular.Module - * @param {string} name Filter name. - * @param {Function} filterFactory Factory function for creating new instance of filter. - * @description - * See {@link ng.$filterProvider#register $filterProvider.register()}. - */ - filter: invokeLater('$filterProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#controller - * @methodOf angular.Module - * @param {string} name Controller name. - * @param {Function} constructor Controller constructor function. - * @description - * See {@link ng.$controllerProvider#register $controllerProvider.register()}. - */ - controller: invokeLater('$controllerProvider', 'register'), - - /** - * @ngdoc method - * @name angular.Module#directive - * @methodOf angular.Module - * @param {string} name directive name - * @param {Function} directiveFactory Factory function for creating new instance of - * directives. - * @description - * See {@link ng.$compileProvider#directive $compileProvider.directive()}. - */ - directive: invokeLater('$compileProvider', 'directive'), - - /** - * @ngdoc method - * @name angular.Module#config - * @methodOf angular.Module - * @param {Function} configFn Execute this function on module load. Useful for service - * configuration. - * @description - * Use this method to register work which needs to be performed on module loading. - */ - config: config, - - /** - * @ngdoc method - * @name angular.Module#run - * @methodOf angular.Module - * @param {Function} initializationFn Execute this function after injector creation. - * Useful for application initialization. - * @description - * Use this method to register work which should be performed when the injector is done - * loading all modules. - */ - run: function(block) { - runBlocks.push(block); - return this; - } - }; - - if (configFn) { - config(configFn); - } - - return moduleInstance; - - /** - * @param {string} provider - * @param {string} method - * @param {String=} insertMethod - * @returns {angular.Module} - */ - function invokeLater(provider, method, insertMethod) { - return function() { - invokeQueue[insertMethod || 'push']([provider, method, arguments]); - return moduleInstance; - } - } - }); - }; - }); - -} - -/** - * @ngdoc property - * @name angular.version - * @description - * An object that contains information about the current AngularJS version. This object has the - * following properties: - * - * - `full` – `{string}` – Full version string, such as "0.9.18". - * - `major` – `{number}` – Major version number, such as "0". - * - `minor` – `{number}` – Minor version number, such as "9". - * - `dot` – `{number}` – Dot version number, such as "18". - * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". - */ -var version = { - full: '1.0.3', // all of these placeholder strings will be replaced by rake's - major: 1, // compile task - minor: 0, - dot: 3, - codeName: 'bouncy-thunder' -}; - - -function publishExternalAPI(angular){ - extend(angular, { - 'bootstrap': bootstrap, - 'copy': copy, - 'extend': extend, - 'equals': equals, - 'element': jqLite, - 'forEach': forEach, - 'injector': createInjector, - 'noop':noop, - 'bind':bind, - 'toJson': toJson, - 'fromJson': fromJson, - 'identity':identity, - 'isUndefined': isUndefined, - 'isDefined': isDefined, - 'isString': isString, - 'isFunction': isFunction, - 'isObject': isObject, - 'isNumber': isNumber, - 'isElement': isElement, - 'isArray': isArray, - 'version': version, - 'isDate': isDate, - 'lowercase': lowercase, - 'uppercase': uppercase, - 'callbacks': {counter: 0} - }); - - angularModule = setupModuleLoader(window); - try { - angularModule('ngLocale'); - } catch (e) { - angularModule('ngLocale', []).provider('$locale', $LocaleProvider); - } - - angularModule('ng', ['ngLocale'], ['$provide', - function ngModule($provide) { - $provide.provider('$compile', $CompileProvider). - directive({ - a: htmlAnchorDirective, - input: inputDirective, - textarea: inputDirective, - form: formDirective, - script: scriptDirective, - select: selectDirective, - style: styleDirective, - option: optionDirective, - ngBind: ngBindDirective, - ngBindHtmlUnsafe: ngBindHtmlUnsafeDirective, - ngBindTemplate: ngBindTemplateDirective, - ngClass: ngClassDirective, - ngClassEven: ngClassEvenDirective, - ngClassOdd: ngClassOddDirective, - ngCsp: ngCspDirective, - ngCloak: ngCloakDirective, - ngController: ngControllerDirective, - ngForm: ngFormDirective, - ngHide: ngHideDirective, - ngInclude: ngIncludeDirective, - ngInit: ngInitDirective, - ngNonBindable: ngNonBindableDirective, - ngPluralize: ngPluralizeDirective, - ngRepeat: ngRepeatDirective, - ngShow: ngShowDirective, - ngSubmit: ngSubmitDirective, - ngStyle: ngStyleDirective, - ngSwitch: ngSwitchDirective, - ngSwitchWhen: ngSwitchWhenDirective, - ngSwitchDefault: ngSwitchDefaultDirective, - ngOptions: ngOptionsDirective, - ngView: ngViewDirective, - ngTransclude: ngTranscludeDirective, - ngModel: ngModelDirective, - ngList: ngListDirective, - ngChange: ngChangeDirective, - required: requiredDirective, - ngRequired: requiredDirective, - ngValue: ngValueDirective - }). - directive(ngAttributeAliasDirectives). - directive(ngEventDirectives); - $provide.provider({ - $anchorScroll: $AnchorScrollProvider, - $browser: $BrowserProvider, - $cacheFactory: $CacheFactoryProvider, - $controller: $ControllerProvider, - $document: $DocumentProvider, - $exceptionHandler: $ExceptionHandlerProvider, - $filter: $FilterProvider, - $interpolate: $InterpolateProvider, - $http: $HttpProvider, - $httpBackend: $HttpBackendProvider, - $location: $LocationProvider, - $log: $LogProvider, - $parse: $ParseProvider, - $route: $RouteProvider, - $routeParams: $RouteParamsProvider, - $rootScope: $RootScopeProvider, - $q: $QProvider, - $sniffer: $SnifferProvider, - $templateCache: $TemplateCacheProvider, - $timeout: $TimeoutProvider, - $window: $WindowProvider - }); - } - ]); -} - -////////////////////////////////// -//JQLite -////////////////////////////////// - -/** - * @ngdoc function - * @name angular.element - * @function - * - * @description - * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element. - * `angular.element` can be either an alias for [jQuery](http://api.jquery.com/jQuery/) function, if - * jQuery is available, or a function that wraps the element or string in Angular's jQuery lite - * implementation (commonly referred to as jqLite). - * - * Real jQuery always takes precedence over jqLite, provided it was loaded before `DOMContentLoaded` - * event fired. - * - * jqLite is a tiny, API-compatible subset of jQuery that allows - * Angular to manipulate the DOM. jqLite implements only the most commonly needed functionality - * within a very small footprint, so only a subset of the jQuery API - methods, arguments and - * invocation styles - are supported. - * - * Note: All element references in Angular are always wrapped with jQuery or jqLite; they are never - * raw DOM references. - * - * ## Angular's jQuery lite provides the following methods: - * - * - [addClass()](http://api.jquery.com/addClass/) - * - [after()](http://api.jquery.com/after/) - * - [append()](http://api.jquery.com/append/) - * - [attr()](http://api.jquery.com/attr/) - * - [bind()](http://api.jquery.com/bind/) - * - [children()](http://api.jquery.com/children/) - * - [clone()](http://api.jquery.com/clone/) - * - [contents()](http://api.jquery.com/contents/) - * - [css()](http://api.jquery.com/css/) - * - [data()](http://api.jquery.com/data/) - * - [eq()](http://api.jquery.com/eq/) - * - [find()](http://api.jquery.com/find/) - Limited to lookups by tag name. - * - [hasClass()](http://api.jquery.com/hasClass/) - * - [html()](http://api.jquery.com/html/) - * - [next()](http://api.jquery.com/next/) - * - [parent()](http://api.jquery.com/parent/) - * - [prepend()](http://api.jquery.com/prepend/) - * - [prop()](http://api.jquery.com/prop/) - * - [ready()](http://api.jquery.com/ready/) - * - [remove()](http://api.jquery.com/remove/) - * - [removeAttr()](http://api.jquery.com/removeAttr/) - * - [removeClass()](http://api.jquery.com/removeClass/) - * - [removeData()](http://api.jquery.com/removeData/) - * - [replaceWith()](http://api.jquery.com/replaceWith/) - * - [text()](http://api.jquery.com/text/) - * - [toggleClass()](http://api.jquery.com/toggleClass/) - * - [triggerHandler()](http://api.jquery.com/triggerHandler/) - Doesn't pass native event objects to handlers. - * - [unbind()](http://api.jquery.com/unbind/) - * - [val()](http://api.jquery.com/val/) - * - [wrap()](http://api.jquery.com/wrap/) - * - * ## In addtion to the above, Angular privides an additional method to both jQuery and jQuery lite: - * - * - `controller(name)` - retrieves the controller of the current element or its parent. By default - * retrieves controller associated with the `ngController` directive. If `name` is provided as - * camelCase directive name, then the controller for this directive will be retrieved (e.g. - * `'ngModel'`). - * - `injector()` - retrieves the injector of the current element or its parent. - * - `scope()` - retrieves the {@link api/ng.$rootScope.Scope scope} of the current - * element or its parent. - * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top - * parent element is reached. - * - * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery. - * @returns {Object} jQuery object. - */ - -var jqCache = JQLite.cache = {}, - jqName = JQLite.expando = 'ng-' + new Date().getTime(), - jqId = 1, - addEventListenerFn = (window.document.addEventListener - ? function(element, type, fn) {element.addEventListener(type, fn, false);} - : function(element, type, fn) {element.attachEvent('on' + type, fn);}), - removeEventListenerFn = (window.document.removeEventListener - ? function(element, type, fn) {element.removeEventListener(type, fn, false); } - : function(element, type, fn) {element.detachEvent('on' + type, fn); }); - -function jqNextId() { return ++jqId; } - - -var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g; -var MOZ_HACK_REGEXP = /^moz([A-Z])/; - -/** - * Converts snake_case to camelCase. - * Also there is special case for Moz prefix starting with upper case letter. - * @param name Name to normalize - */ -function camelCase(name) { - return name. - replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) { - return offset ? letter.toUpperCase() : letter; - }). - replace(MOZ_HACK_REGEXP, 'Moz$1'); -} - -///////////////////////////////////////////// -// jQuery mutation patch -// -// In conjunction with bindJQuery intercepts all jQuery's DOM destruction apis and fires a -// $destroy event on all DOM nodes being removed. -// -///////////////////////////////////////////// - -function JQLitePatchJQueryRemove(name, dispatchThis) { - var originalJqFn = jQuery.fn[name]; - originalJqFn = originalJqFn.$original || originalJqFn; - removePatch.$original = originalJqFn; - jQuery.fn[name] = removePatch; - - function removePatch() { - var list = [this], - fireEvent = dispatchThis, - set, setIndex, setLength, - element, childIndex, childLength, children, - fns, events; - - while(list.length) { - set = list.shift(); - for(setIndex = 0, setLength = set.length; setIndex < setLength; setIndex++) { - element = jqLite(set[setIndex]); - if (fireEvent) { - element.triggerHandler('$destroy'); - } else { - fireEvent = !fireEvent; - } - for(childIndex = 0, childLength = (children = element.children()).length; - childIndex < childLength; - childIndex++) { - list.push(jQuery(children[childIndex])); - } - } - } - return originalJqFn.apply(this, arguments); - } -} - -///////////////////////////////////////////// -function JQLite(element) { - if (element instanceof JQLite) { - return element; - } - if (!(this instanceof JQLite)) { - if (isString(element) && element.charAt(0) != '<') { - throw Error('selectors not implemented'); - } - return new JQLite(element); - } - - if (isString(element)) { - var div = document.createElement('div'); - // Read about the NoScope elements here: - // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx - div.innerHTML = '<div> </div>' + element; // IE insanity to make NoScope elements work! - div.removeChild(div.firstChild); // remove the superfluous div - JQLiteAddNodes(this, div.childNodes); - this.remove(); // detach the elements from the temporary DOM div. - } else { - JQLiteAddNodes(this, element); - } -} - -function JQLiteClone(element) { - return element.cloneNode(true); -} - -function JQLiteDealoc(element){ - JQLiteRemoveData(element); - for ( var i = 0, children = element.childNodes || []; i < children.length; i++) { - JQLiteDealoc(children[i]); - } -} - -function JQLiteUnbind(element, type, fn) { - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); - - if (!handle) return; //no listeners registered - - if (isUndefined(type)) { - forEach(events, function(eventHandler, type) { - removeEventListenerFn(element, type, eventHandler); - delete events[type]; - }); - } else { - if (isUndefined(fn)) { - removeEventListenerFn(element, type, events[type]); - delete events[type]; - } else { - arrayRemove(events[type], fn); - } - } -} - -function JQLiteRemoveData(element) { - var expandoId = element[jqName], - expandoStore = jqCache[expandoId]; - - if (expandoStore) { - if (expandoStore.handle) { - expandoStore.events.$destroy && expandoStore.handle({}, '$destroy'); - JQLiteUnbind(element); - } - delete jqCache[expandoId]; - element[jqName] = undefined; // ie does not allow deletion of attributes on elements. - } -} - -function JQLiteExpandoStore(element, key, value) { - var expandoId = element[jqName], - expandoStore = jqCache[expandoId || -1]; - - if (isDefined(value)) { - if (!expandoStore) { - element[jqName] = expandoId = jqNextId(); - expandoStore = jqCache[expandoId] = {}; - } - expandoStore[key] = value; - } else { - return expandoStore && expandoStore[key]; - } -} - -function JQLiteData(element, key, value) { - var data = JQLiteExpandoStore(element, 'data'), - isSetter = isDefined(value), - keyDefined = !isSetter && isDefined(key), - isSimpleGetter = keyDefined && !isObject(key); - - if (!data && !isSimpleGetter) { - JQLiteExpandoStore(element, 'data', data = {}); - } - - if (isSetter) { - data[key] = value; - } else { - if (keyDefined) { - if (isSimpleGetter) { - // don't create data in this case. - return data && data[key]; - } else { - extend(data, key); - } - } else { - return data; - } - } -} - -function JQLiteHasClass(element, selector) { - return ((" " + element.className + " ").replace(/[\n\t]/g, " "). - indexOf( " " + selector + " " ) > -1); -} - -function JQLiteRemoveClass(element, cssClasses) { - if (cssClasses) { - forEach(cssClasses.split(' '), function(cssClass) { - element.className = trim( - (" " + element.className + " ") - .replace(/[\n\t]/g, " ") - .replace(" " + trim(cssClass) + " ", " ") - ); - }); - } -} - -function JQLiteAddClass(element, cssClasses) { - if (cssClasses) { - forEach(cssClasses.split(' '), function(cssClass) { - if (!JQLiteHasClass(element, cssClass)) { - element.className = trim(element.className + ' ' + trim(cssClass)); - } - }); - } -} - -function JQLiteAddNodes(root, elements) { - if (elements) { - elements = (!elements.nodeName && isDefined(elements.length) && !isWindow(elements)) - ? elements - : [ elements ]; - for(var i=0; i < elements.length; i++) { - root.push(elements[i]); - } - } -} - -function JQLiteController(element, name) { - return JQLiteInheritedData(element, '$' + (name || 'ngController' ) + 'Controller'); -} - -function JQLiteInheritedData(element, name, value) { - element = jqLite(element); - - // if element is the document object work with the html element instead - // this makes $(document).scope() possible - if(element[0].nodeType == 9) { - element = element.find('html'); - } - - while (element.length) { - if (value = element.data(name)) return value; - element = element.parent(); - } -} - -////////////////////////////////////////// -// Functions which are declared directly. -////////////////////////////////////////// -var JQLitePrototype = JQLite.prototype = { - ready: function(fn) { - var fired = false; - - function trigger() { - if (fired) return; - fired = true; - fn(); - } - - this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9 - // we can not use jqLite since we are not done loading and jQuery could be loaded later. - JQLite(window).bind('load', trigger); // fallback to window.onload for others - }, - toString: function() { - var value = []; - forEach(this, function(e){ value.push('' + e);}); - return '[' + value.join(', ') + ']'; - }, - - eq: function(index) { - return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]); - }, - - length: 0, - push: push, - sort: [].sort, - splice: [].splice -}; - -////////////////////////////////////////// -// Functions iterating getter/setters. -// these functions return self on setter and -// value on get. -////////////////////////////////////////// -var BOOLEAN_ATTR = {}; -forEach('multiple,selected,checked,disabled,readOnly,required'.split(','), function(value) { - BOOLEAN_ATTR[lowercase(value)] = value; -}); -var BOOLEAN_ELEMENTS = {}; -forEach('input,select,option,textarea,button,form'.split(','), function(value) { - BOOLEAN_ELEMENTS[uppercase(value)] = true; -}); - -function getBooleanAttrName(element, name) { - // check dom last since we will most likely fail on name - var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()]; - - // booleanAttr is here twice to minimize DOM access - return booleanAttr && BOOLEAN_ELEMENTS[element.nodeName] && booleanAttr; -} - -forEach({ - data: JQLiteData, - inheritedData: JQLiteInheritedData, - - scope: function(element) { - return JQLiteInheritedData(element, '$scope'); - }, - - controller: JQLiteController , - - injector: function(element) { - return JQLiteInheritedData(element, '$injector'); - }, - - removeAttr: function(element,name) { - element.removeAttribute(name); - }, - - hasClass: JQLiteHasClass, - - css: function(element, name, value) { - name = camelCase(name); - - if (isDefined(value)) { - element.style[name] = value; - } else { - var val; - - if (msie <= 8) { - // this is some IE specific weirdness that jQuery 1.6.4 does not sure why - val = element.currentStyle && element.currentStyle[name]; - if (val === '') val = 'auto'; - } - - val = val || element.style[name]; - - if (msie <= 8) { - // jquery weirdness :-/ - val = (val === '') ? undefined : val; - } - - return val; - } - }, - - attr: function(element, name, value){ - var lowercasedName = lowercase(name); - if (BOOLEAN_ATTR[lowercasedName]) { - if (isDefined(value)) { - if (!!value) { - element[name] = true; - element.setAttribute(name, lowercasedName); - } else { - element[name] = false; - element.removeAttribute(lowercasedName); - } - } else { - return (element[name] || - (element.attributes.getNamedItem(name)|| noop).specified) - ? lowercasedName - : undefined; - } - } else if (isDefined(value)) { - element.setAttribute(name, value); - } else if (element.getAttribute) { - // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code - // some elements (e.g. Document) don't have get attribute, so return undefined - var ret = element.getAttribute(name, 2); - // normalize non-existing attributes to undefined (as jQuery) - return ret === null ? undefined : ret; - } - }, - - prop: function(element, name, value) { - if (isDefined(value)) { - element[name] = value; - } else { - return element[name]; - } - }, - - text: extend((msie < 9) - ? function(element, value) { - if (element.nodeType == 1 /** Element */) { - if (isUndefined(value)) - return element.innerText; - element.innerText = value; - } else { - if (isUndefined(value)) - return element.nodeValue; - element.nodeValue = value; - } - } - : function(element, value) { - if (isUndefined(value)) { - return element.textContent; - } - element.textContent = value; - }, {$dv:''}), - - val: function(element, value) { - if (isUndefined(value)) { - return element.value; - } - element.value = value; - }, - - html: function(element, value) { - if (isUndefined(value)) { - return element.innerHTML; - } - for (var i = 0, childNodes = element.childNodes; i < childNodes.length; i++) { - JQLiteDealoc(childNodes[i]); - } - element.innerHTML = value; - } -}, function(fn, name){ - /** - * Properties: writes return selection, reads return first value - */ - JQLite.prototype[name] = function(arg1, arg2) { - var i, key; - - // JQLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it - // in a way that survives minification. - if (((fn.length == 2 && (fn !== JQLiteHasClass && fn !== JQLiteController)) ? arg1 : arg2) === undefined) { - if (isObject(arg1)) { - - // we are a write, but the object properties are the key/values - for(i=0; i < this.length; i++) { - if (fn === JQLiteData) { - // data() takes the whole object in jQuery - fn(this[i], arg1); - } else { - for (key in arg1) { - fn(this[i], key, arg1[key]); - } - } - } - // return self for chaining - return this; - } else { - // we are a read, so read the first child. - if (this.length) - return fn(this[0], arg1, arg2); - } - } else { - // we are a write, so apply to all children - for(i=0; i < this.length; i++) { - fn(this[i], arg1, arg2); - } - // return self for chaining - return this; - } - return fn.$dv; - }; -}); - -function createEventHandler(element, events) { - var eventHandler = function (event, type) { - if (!event.preventDefault) { - event.preventDefault = function() { - event.returnValue = false; //ie - }; - } - - if (!event.stopPropagation) { - event.stopPropagation = function() { - event.cancelBubble = true; //ie - }; - } - - if (!event.target) { - event.target = event.srcElement || document; - } - - if (isUndefined(event.defaultPrevented)) { - var prevent = event.preventDefault; - event.preventDefault = function() { - event.defaultPrevented = true; - prevent.call(event); - }; - event.defaultPrevented = false; - } - - event.isDefaultPrevented = function() { - return event.defaultPrevented; - }; - - forEach(events[type || event.type], function(fn) { - fn.call(element, event); - }); - - // Remove monkey-patched methods (IE), - // as they would cause memory leaks in IE8. - if (msie <= 8) { - // IE7/8 does not allow to delete property on native object - event.preventDefault = null; - event.stopPropagation = null; - event.isDefaultPrevented = null; - } else { - // It shouldn't affect normal browsers (native methods are defined on prototype). - delete event.preventDefault; - delete event.stopPropagation; - delete event.isDefaultPrevented; - } - }; - eventHandler.elem = element; - return eventHandler; -} - -////////////////////////////////////////// -// Functions iterating traversal. -// These functions chain results into a single -// selector. -////////////////////////////////////////// -forEach({ - removeData: JQLiteRemoveData, - - dealoc: JQLiteDealoc, - - bind: function bindFn(element, type, fn){ - var events = JQLiteExpandoStore(element, 'events'), - handle = JQLiteExpandoStore(element, 'handle'); - - if (!events) JQLiteExpandoStore(element, 'events', events = {}); - if (!handle) JQLiteExpandoStore(element, 'handle', handle = createEventHandler(element, events)); - - forEach(type.split(' '), function(type){ - var eventFns = events[type]; - - if (!eventFns) { - if (type == 'mouseenter' || type == 'mouseleave') { - var counter = 0; - - events.mouseenter = []; - events.mouseleave = []; - - bindFn(element, 'mouseover', function(event) { - counter++; - if (counter == 1) { - handle(event, 'mouseenter'); - } - }); - bindFn(element, 'mouseout', function(event) { - counter --; - if (counter == 0) { - handle(event, 'mouseleave'); - } - }); - } else { - addEventListenerFn(element, type, handle); - events[type] = []; - } - eventFns = events[type] - } - eventFns.push(fn); - }); - }, - - unbind: JQLiteUnbind, - - replaceWith: function(element, replaceNode) { - var index, parent = element.parentNode; - JQLiteDealoc(element); - forEach(new JQLite(replaceNode), function(node){ - if (index) { - parent.insertBefore(node, index.nextSibling); - } else { - parent.replaceChild(node, element); - } - index = node; - }); - }, - - children: function(element) { - var children = []; - forEach(element.childNodes, function(element){ - if (element.nodeName != '#text') - children.push(element); - }); - return children; - }, - - contents: function(element) { - return element.childNodes; - }, - - append: function(element, node) { - forEach(new JQLite(node), function(child){ - if (element.nodeType === 1) - element.appendChild(child); - }); - }, - - prepend: function(element, node) { - if (element.nodeType === 1) { - var index = element.firstChild; - forEach(new JQLite(node), function(child){ - if (index) { - element.insertBefore(child, index); - } else { - element.appendChild(child); - index = child; - } - }); - } - }, - - wrap: function(element, wrapNode) { - wrapNode = jqLite(wrapNode)[0]; - var parent = element.parentNode; - if (parent) { - parent.replaceChild(wrapNode, element); - } - wrapNode.appendChild(element); - }, - - remove: function(element) { - JQLiteDealoc(element); - var parent = element.parentNode; - if (parent) parent.removeChild(element); - }, - - after: function(element, newElement) { - var index = element, parent = element.parentNode; - forEach(new JQLite(newElement), function(node){ - parent.insertBefore(node, index.nextSibling); - index = node; - }); - }, - - addClass: JQLiteAddClass, - removeClass: JQLiteRemoveClass, - - toggleClass: function(element, selector, condition) { - if (isUndefined(condition)) { - condition = !JQLiteHasClass(element, selector); - } - (condition ? JQLiteAddClass : JQLiteRemoveClass)(element, selector); - }, - - parent: function(element) { - var parent = element.parentNode; - return parent && parent.nodeType !== 11 ? parent : null; - }, - - next: function(element) { - return element.nextSibling; - }, - - find: function(element, selector) { - return element.getElementsByTagName(selector); - }, - - clone: JQLiteClone, - - triggerHandler: function(element, eventName) { - var eventFns = (JQLiteExpandoStore(element, 'events') || {})[eventName]; - - forEach(eventFns, function(fn) { - fn.call(element, null); - }); - } -}, function(fn, name){ - /** - * chaining functions - */ - JQLite.prototype[name] = function(arg1, arg2) { - var value; - for(var i=0; i < this.length; i++) { - if (value == undefined) { - value = fn(this[i], arg1, arg2); - if (value !== undefined) { - // any function which returns a value needs to be wrapped - value = jqLite(value); - } - } else { - JQLiteAddNodes(value, fn(this[i], arg1, arg2)); - } - } - return value == undefined ? this : value; - }; -}); - -/** - * Computes a hash of an 'obj'. - * Hash of a: - * string is string - * number is number as string - * object is either result of calling $$hashKey function on the object or uniquely generated id, - * that is also assigned to the $$hashKey property of the object. - * - * @param obj - * @returns {string} hash string such that the same input will have the same hash string. - * The resulting string key is in 'type:hashKey' format. - */ -function hashKey(obj) { - var objType = typeof obj, - key; - - if (objType == 'object' && obj !== null) { - if (typeof (key = obj.$$hashKey) == 'function') { - // must invoke on object to keep the right this - key = obj.$$hashKey(); - } else if (key === undefined) { - key = obj.$$hashKey = nextUid(); - } - } else { - key = obj; - } - - return objType + ':' + key; -} - -/** - * HashMap which can use objects as keys - */ -function HashMap(array){ - forEach(array, this.put, this); -} -HashMap.prototype = { - /** - * Store key value pair - * @param key key to store can be any type - * @param value value to store can be any type - */ - put: function(key, value) { - this[hashKey(key)] = value; - }, - - /** - * @param key - * @returns the value for the key - */ - get: function(key) { - return this[hashKey(key)]; - }, - - /** - * Remove the key/value pair - * @param key - */ - remove: function(key) { - var value = this[key = hashKey(key)]; - delete this[key]; - return value; - } -}; - -/** - * A map where multiple values can be added to the same key such that they form a queue. - * @returns {HashQueueMap} - */ -function HashQueueMap() {} -HashQueueMap.prototype = { - /** - * Same as array push, but using an array as the value for the hash - */ - push: function(key, value) { - var array = this[key = hashKey(key)]; - if (!array) { - this[key] = [value]; - } else { - array.push(value); - } - }, - - /** - * Same as array shift, but using an array as the value for the hash - */ - shift: function(key) { - var array = this[key = hashKey(key)]; - if (array) { - if (array.length == 1) { - delete this[key]; - return array[0]; - } else { - return array.shift(); - } - } - }, - - /** - * return the first item without deleting it - */ - peek: function(key) { - var array = this[hashKey(key)]; - if (array) { - return array[0]; - } - } -}; - -/** - * @ngdoc function - * @name angular.injector - * @function - * - * @description - * Creates an injector function that can be used for retrieving services as well as for - * dependency injection (see {@link guide/di dependency injection}). - * - - * @param {Array.<string|Function>} modules A list of module functions or their aliases. See - * {@link angular.module}. The `ng` module must be explicitly added. - * @returns {function()} Injector function. See {@link AUTO.$injector $injector}. - * - * @example - * Typical usage - * <pre> - * // create an injector - * var $injector = angular.injector(['ng']); - * - * // use the injector to kick off your application - * // use the type inference to auto inject arguments, or use implicit injection - * $injector.invoke(function($rootScope, $compile, $document){ - * $compile($document)($rootScope); - * $rootScope.$digest(); - * }); - * </pre> - */ - - -/** - * @ngdoc overview - * @name AUTO - * @description - * - * Implicit module which gets automatically added to each {@link AUTO.$injector $injector}. - */ - -var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m; -var FN_ARG_SPLIT = /,/; -var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/; -var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg; -function annotate(fn) { - var $inject, - fnText, - argDecl, - last; - - if (typeof fn == 'function') { - if (!($inject = fn.$inject)) { - $inject = []; - fnText = fn.toString().replace(STRIP_COMMENTS, ''); - argDecl = fnText.match(FN_ARGS); - forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg){ - arg.replace(FN_ARG, function(all, underscore, name){ - $inject.push(name); - }); - }); - fn.$inject = $inject; - } - } else if (isArray(fn)) { - last = fn.length - 1; - assertArgFn(fn[last], 'fn') - $inject = fn.slice(0, last); - } else { - assertArgFn(fn, 'fn', true); - } - return $inject; -} - -/////////////////////////////////////// - -/** - * @ngdoc object - * @name AUTO.$injector - * @function - * - * @description - * - * `$injector` is used to retrieve object instances as defined by - * {@link AUTO.$provide provider}, instantiate types, invoke methods, - * and load modules. - * - * The following always holds true: - * - * <pre> - * var $injector = angular.injector(); - * expect($injector.get('$injector')).toBe($injector); - * expect($injector.invoke(function($injector){ - * return $injector; - * }).toBe($injector); - * </pre> - * - * # Injection Function Annotation - * - * JavaScript does not have annotations, and annotations are needed for dependency injection. The - * following ways are all valid way of annotating function with injection arguments and are equivalent. - * - * <pre> - * // inferred (only works if code not minified/obfuscated) - * $inject.invoke(function(serviceA){}); - * - * // annotated - * function explicit(serviceA) {}; - * explicit.$inject = ['serviceA']; - * $inject.invoke(explicit); - * - * // inline - * $inject.invoke(['serviceA', function(serviceA){}]); - * </pre> - * - * ## Inference - * - * In JavaScript calling `toString()` on a function returns the function definition. The definition can then be - * parsed and the function arguments can be extracted. *NOTE:* This does not work with minification, and obfuscation - * tools since these tools change the argument names. - * - * ## `$inject` Annotation - * By adding a `$inject` property onto a function the injection parameters can be specified. - * - * ## Inline - * As an array of injection names, where the last item in the array is the function to call. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#get - * @methodOf AUTO.$injector - * - * @description - * Return an instance of the service. - * - * @param {string} name The name of the instance to retrieve. - * @return {*} The instance. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#invoke - * @methodOf AUTO.$injector - * - * @description - * Invoke the method and supply the method arguments from the `$injector`. - * - * @param {!function} fn The function to invoke. The function arguments come form the function annotation. - * @param {Object=} self The `this` for the invoked method. - * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before - * the `$injector` is consulted. - * @returns {*} the value returned by the invoked `fn` function. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#instantiate - * @methodOf AUTO.$injector - * @description - * Create a new instance of JS type. The method takes a constructor function invokes the new operator and supplies - * all of the arguments to the constructor function as specified by the constructor annotation. - * - * @param {function} Type Annotated constructor function. - * @param {Object=} locals Optional object. If preset then any argument names are read from this object first, before - * the `$injector` is consulted. - * @returns {Object} new instance of `Type`. - */ - -/** - * @ngdoc method - * @name AUTO.$injector#annotate - * @methodOf AUTO.$injector - * - * @description - * Returns an array of service names which the function is requesting for injection. This API is used by the injector - * to determine which services need to be injected into the function when the function is invoked. There are three - * ways in which the function can be annotated with the needed dependencies. - * - * # Argument names - * - * The simplest form is to extract the dependencies from the arguments of the function. This is done by converting - * the function into a string using `toString()` method and extracting the argument names. - * <pre> - * // Given - * function MyController($scope, $route) { - * // ... - * } - * - * // Then - * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); - * </pre> - * - * This method does not work with code minfication / obfuscation. For this reason the following annotation strategies - * are supported. - * - * # The `$injector` property - * - * If a function has an `$inject` property and its value is an array of strings, then the strings represent names of - * services to be injected into the function. - * <pre> - * // Given - * var MyController = function(obfuscatedScope, obfuscatedRoute) { - * // ... - * } - * // Define function dependencies - * MyController.$inject = ['$scope', '$route']; - * - * // Then - * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']); - * </pre> - * - * # The array notation - * - * It is often desirable to inline Injected functions and that's when setting the `$inject` property is very - * inconvenient. In these situations using the array notation to specify the dependencies in a way that survives - * minification is a better choice: - * - * <pre> - * // We wish to write this (not minification / obfuscation safe) - * injector.invoke(function($compile, $rootScope) { - * // ... - * }); - * - * // We are forced to write break inlining - * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) { - * // ... - * }; - * tmpFn.$inject = ['$compile', '$rootScope']; - * injector.invoke(tempFn); - * - * // To better support inline function the inline annotation is supported - * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) { - * // ... - * }]); - * - * // Therefore - * expect(injector.annotate( - * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}]) - * ).toEqual(['$compile', '$rootScope']); - * </pre> - * - * @param {function|Array.<string|Function>} fn Function for which dependent service names need to be retrieved as described - * above. - * - * @returns {Array.<string>} The names of the services which the function requires. - */ - - - - -/** - * @ngdoc object - * @name AUTO.$provide - * - * @description - * - * Use `$provide` to register new providers with the `$injector`. The providers are the factories for the instance. - * The providers share the same name as the instance they create with the `Provider` suffixed to them. - * - * A provider is an object with a `$get()` method. The injector calls the `$get` method to create a new instance of - * a service. The Provider can have additional methods which would allow for configuration of the provider. - * - * <pre> - * function GreetProvider() { - * var salutation = 'Hello'; - * - * this.salutation = function(text) { - * salutation = text; - * }; - * - * this.$get = function() { - * return function (name) { - * return salutation + ' ' + name + '!'; - * }; - * }; - * } - * - * describe('Greeter', function(){ - * - * beforeEach(module(function($provide) { - * $provide.provider('greet', GreetProvider); - * }); - * - * it('should greet', inject(function(greet) { - * expect(greet('angular')).toEqual('Hello angular!'); - * })); - * - * it('should allow configuration of salutation', function() { - * module(function(greetProvider) { - * greetProvider.salutation('Ahoj'); - * }); - * inject(function(greet) { - * expect(greet('angular')).toEqual('Ahoj angular!'); - * }); - * )}; - * - * }); - * </pre> - */ - -/** - * @ngdoc method - * @name AUTO.$provide#provider - * @methodOf AUTO.$provide - * @description - * - * Register a provider for a service. The providers can be retrieved and can have additional configuration methods. - * - * @param {string} name The name of the instance. NOTE: the provider will be available under `name + 'Provider'` key. - * @param {(Object|function())} provider If the provider is: - * - * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using - * {@link AUTO.$injector#invoke $injector.invoke()} when an instance needs to be created. - * - `Constructor`: a new instance of the provider will be created using - * {@link AUTO.$injector#instantiate $injector.instantiate()}, then treated as `object`. - * - * @returns {Object} registered provider instance - */ - -/** - * @ngdoc method - * @name AUTO.$provide#factory - * @methodOf AUTO.$provide - * @description - * - * A short hand for configuring services if only `$get` method is required. - * - * @param {string} name The name of the instance. - * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand for - * `$provide.provider(name, {$get: $getFn})`. - * @returns {Object} registered provider instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#service - * @methodOf AUTO.$provide - * @description - * - * A short hand for registering service of given class. - * - * @param {string} name The name of the instance. - * @param {Function} constructor A class (constructor function) that will be instantiated. - * @returns {Object} registered provider instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#value - * @methodOf AUTO.$provide - * @description - * - * A short hand for configuring services if the `$get` method is a constant. - * - * @param {string} name The name of the instance. - * @param {*} value The value. - * @returns {Object} registered provider instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#constant - * @methodOf AUTO.$provide - * @description - * - * A constant value, but unlike {@link AUTO.$provide#value value} it can be injected - * into configuration function (other modules) and it is not interceptable by - * {@link AUTO.$provide#decorator decorator}. - * - * @param {string} name The name of the constant. - * @param {*} value The constant value. - * @returns {Object} registered instance - */ - - -/** - * @ngdoc method - * @name AUTO.$provide#decorator - * @methodOf AUTO.$provide - * @description - * - * Decoration of service, allows the decorator to intercept the service instance creation. The - * returned instance may be the original instance, or a new instance which delegates to the - * original instance. - * - * @param {string} name The name of the service to decorate. - * @param {function()} decorator This function will be invoked when the service needs to be - * instanciated. The function is called using the {@link AUTO.$injector#invoke - * injector.invoke} method and is therefore fully injectable. Local injection arguments: - * - * * `$delegate` - The original service instance, which can be monkey patched, configured, - * decorated or delegated to. - */ - - -function createInjector(modulesToLoad) { - var INSTANTIATING = {}, - providerSuffix = 'Provider', - path = [], - loadedModules = new HashMap(), - providerCache = { - $provide: { - provider: supportObject(provider), - factory: supportObject(factory), - service: supportObject(service), - value: supportObject(value), - constant: supportObject(constant), - decorator: decorator - } - }, - providerInjector = createInternalInjector(providerCache, function() { - throw Error("Unknown provider: " + path.join(' <- ')); - }), - instanceCache = {}, - instanceInjector = (instanceCache.$injector = - createInternalInjector(instanceCache, function(servicename) { - var provider = providerInjector.get(servicename + providerSuffix); - return instanceInjector.invoke(provider.$get, provider); - })); - - - forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); }); - - return instanceInjector; - - //////////////////////////////////// - // $provider - //////////////////////////////////// - - function supportObject(delegate) { - return function(key, value) { - if (isObject(key)) { - forEach(key, reverseParams(delegate)); - } else { - return delegate(key, value); - } - } - } - - function provider(name, provider_) { - if (isFunction(provider_)) { - provider_ = providerInjector.instantiate(provider_); - } - if (!provider_.$get) { - throw Error('Provider ' + name + ' must define $get factory method.'); - } - return providerCache[name + providerSuffix] = provider_; - } - - function factory(name, factoryFn) { return provider(name, { $get: factoryFn }); } - - function service(name, constructor) { - return factory(name, ['$injector', function($injector) { - return $injector.instantiate(constructor); - }]); - } - - function value(name, value) { return factory(name, valueFn(value)); } - - function constant(name, value) { - providerCache[name] = value; - instanceCache[name] = value; - } - - function decorator(serviceName, decorFn) { - var origProvider = providerInjector.get(serviceName + providerSuffix), - orig$get = origProvider.$get; - - origProvider.$get = function() { - var origInstance = instanceInjector.invoke(orig$get, origProvider); - return instanceInjector.invoke(decorFn, null, {$delegate: origInstance}); - }; - } - - //////////////////////////////////// - // Module Loading - //////////////////////////////////// - function loadModules(modulesToLoad){ - var runBlocks = []; - forEach(modulesToLoad, function(module) { - if (loadedModules.get(module)) return; - loadedModules.put(module, true); - if (isString(module)) { - var moduleFn = angularModule(module); - runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks); - - try { - for(var invokeQueue = moduleFn._invokeQueue, i = 0, ii = invokeQueue.length; i < ii; i++) { - var invokeArgs = invokeQueue[i], - provider = invokeArgs[0] == '$injector' - ? providerInjector - : providerInjector.get(invokeArgs[0]); - - provider[invokeArgs[1]].apply(provider, invokeArgs[2]); - } - } catch (e) { - if (e.message) e.message += ' from ' + module; - throw e; - } - } else if (isFunction(module)) { - try { - runBlocks.push(providerInjector.invoke(module)); - } catch (e) { - if (e.message) e.message += ' from ' + module; - throw e; - } - } else if (isArray(module)) { - try { - runBlocks.push(providerInjector.invoke(module)); - } catch (e) { - if (e.message) e.message += ' from ' + String(module[module.length - 1]); - throw e; - } - } else { - assertArgFn(module, 'module'); - } - }); - return runBlocks; - } - - //////////////////////////////////// - // internal Injector - //////////////////////////////////// - - function createInternalInjector(cache, factory) { - - function getService(serviceName) { - if (typeof serviceName !== 'string') { - throw Error('Service name expected'); - } - if (cache.hasOwnProperty(serviceName)) { - if (cache[serviceName] === INSTANTIATING) { - throw Error('Circular dependency: ' + path.join(' <- ')); - } - return cache[serviceName]; - } else { - try { - path.unshift(serviceName); - cache[serviceName] = INSTANTIATING; - return cache[serviceName] = factory(serviceName); - } finally { - path.shift(); - } - } - } - - function invoke(fn, self, locals){ - var args = [], - $inject = annotate(fn), - length, i, - key; - - for(i = 0, length = $inject.length; i < length; i++) { - key = $inject[i]; - args.push( - locals && locals.hasOwnProperty(key) - ? locals[key] - : getService(key, path) - ); - } - if (!fn.$inject) { - // this means that we must be an array. - fn = fn[length]; - } - - - // Performance optimization: http://jsperf.com/apply-vs-call-vs-invoke - switch (self ? -1 : args.length) { - case 0: return fn(); - case 1: return fn(args[0]); - case 2: return fn(args[0], args[1]); - case 3: return fn(args[0], args[1], args[2]); - case 4: return fn(args[0], args[1], args[2], args[3]); - case 5: return fn(args[0], args[1], args[2], args[3], args[4]); - case 6: return fn(args[0], args[1], args[2], args[3], args[4], args[5]); - case 7: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6]); - case 8: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]); - case 9: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]); - case 10: return fn(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]); - default: return fn.apply(self, args); - } - } - - function instantiate(Type, locals) { - var Constructor = function() {}, - instance, returnedValue; - - Constructor.prototype = (isArray(Type) ? Type[Type.length - 1] : Type).prototype; - instance = new Constructor(); - returnedValue = invoke(Type, instance, locals); - - return isObject(returnedValue) ? returnedValue : instance; - } - - return { - invoke: invoke, - instantiate: instantiate, - get: getService, - annotate: annotate - }; - } -} -/** - * @ngdoc function - * @name ng.$anchorScroll - * @requires $window - * @requires $location - * @requires $rootScope - * - * @description - * When called, it checks current value of `$location.hash()` and scroll to related element, - * according to rules specified in - * {@link http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document Html5 spec}. - * - * It also watches the `$location.hash()` and scroll whenever it changes to match any anchor. - * This can be disabled by calling `$anchorScrollProvider.disableAutoScrolling()`. - */ -function $AnchorScrollProvider() { - - var autoScrollingEnabled = true; - - this.disableAutoScrolling = function() { - autoScrollingEnabled = false; - }; - - this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) { - var document = $window.document; - - // helper function to get first anchor from a NodeList - // can't use filter.filter, as it accepts only instances of Array - // and IE can't convert NodeList to an array using [].slice - // TODO(vojta): use filter if we change it to accept lists as well - function getFirstAnchor(list) { - var result = null; - forEach(list, function(element) { - if (!result && lowercase(element.nodeName) === 'a') result = element; - }); - return result; - } - - function scroll() { - var hash = $location.hash(), elm; - - // empty hash, scroll to the top of the page - if (!hash) $window.scrollTo(0, 0); - - // element with given id - else if ((elm = document.getElementById(hash))) elm.scrollIntoView(); - - // first anchor with given name :-D - else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) elm.scrollIntoView(); - - // no element and hash == 'top', scroll to the top of the page - else if (hash === 'top') $window.scrollTo(0, 0); - } - - // does not scroll when user clicks on anchor link that is currently on - // (no url change, no $locaiton.hash() change), browser native does scroll - if (autoScrollingEnabled) { - $rootScope.$watch(function autoScrollWatch() {return $location.hash();}, - function autoScrollWatchAction() { - $rootScope.$evalAsync(scroll); - }); - } - - return scroll; - }]; -} - -/** - * ! This is a private undocumented service ! - * - * @name ng.$browser - * @requires $log - * @description - * This object has two goals: - * - * - hide all the global state in the browser caused by the window object - * - abstract away all the browser specific features and inconsistencies - * - * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser` - * service, which can be used for convenient testing of the application without the interaction with - * the real browser apis. - */ -/** - * @param {object} window The global window object. - * @param {object} document jQuery wrapped document. - * @param {function()} XHR XMLHttpRequest constructor. - * @param {object} $log console.log or an object with the same interface. - * @param {object} $sniffer $sniffer service - */ -function Browser(window, document, $log, $sniffer) { - var self = this, - rawDocument = document[0], - location = window.location, - history = window.history, - setTimeout = window.setTimeout, - clearTimeout = window.clearTimeout, - pendingDeferIds = {}; - - self.isMock = false; - - var outstandingRequestCount = 0; - var outstandingRequestCallbacks = []; - - // TODO(vojta): remove this temporary api - self.$$completeOutstandingRequest = completeOutstandingRequest; - self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; }; - - /** - * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks` - * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed. - */ - function completeOutstandingRequest(fn) { - try { - fn.apply(null, sliceArgs(arguments, 1)); - } finally { - outstandingRequestCount--; - if (outstandingRequestCount === 0) { - while(outstandingRequestCallbacks.length) { - try { - outstandingRequestCallbacks.pop()(); - } catch (e) { - $log.error(e); - } - } - } - } - } - - /** - * @private - * Note: this method is used only by scenario runner - * TODO(vojta): prefix this method with $$ ? - * @param {function()} callback Function that will be called when no outstanding request - */ - self.notifyWhenNoOutstandingRequests = function(callback) { - // force browser to execute all pollFns - this is needed so that cookies and other pollers fire - // at some deterministic time in respect to the test runner's actions. Leaving things up to the - // regular poller would result in flaky tests. - forEach(pollFns, function(pollFn){ pollFn(); }); - - if (outstandingRequestCount === 0) { - callback(); - } else { - outstandingRequestCallbacks.push(callback); - } - }; - - ////////////////////////////////////////////////////////////// - // Poll Watcher API - ////////////////////////////////////////////////////////////// - var pollFns = [], - pollTimeout; - - /** - * @name ng.$browser#addPollFn - * @methodOf ng.$browser - * - * @param {function()} fn Poll function to add - * - * @description - * Adds a function to the list of functions that poller periodically executes, - * and starts polling if not started yet. - * - * @returns {function()} the added function - */ - self.addPollFn = function(fn) { - if (isUndefined(pollTimeout)) startPoller(100, setTimeout); - pollFns.push(fn); - return fn; - }; - - /** - * @param {number} interval How often should browser call poll functions (ms) - * @param {function()} setTimeout Reference to a real or fake `setTimeout` function. - * - * @description - * Configures the poller to run in the specified intervals, using the specified - * setTimeout fn and kicks it off. - */ - function startPoller(interval, setTimeout) { - (function check() { - forEach(pollFns, function(pollFn){ pollFn(); }); - pollTimeout = setTimeout(check, interval); - })(); - } - - ////////////////////////////////////////////////////////////// - // URL API - ////////////////////////////////////////////////////////////// - - var lastBrowserUrl = location.href, - baseElement = document.find('base'); - - /** - * @name ng.$browser#url - * @methodOf ng.$browser - * - * @description - * GETTER: - * Without any argument, this method just returns current value of location.href. - * - * SETTER: - * With at least one argument, this method sets url to new value. - * If html5 history api supported, pushState/replaceState is used, otherwise - * location.href/location.replace is used. - * Returns its own instance to allow chaining - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to change url. - * - * @param {string} url New url (when used as setter) - * @param {boolean=} replace Should new url replace current history record ? - */ - self.url = function(url, replace) { - // setter - if (url) { - if (lastBrowserUrl == url) return; - lastBrowserUrl = url; - if ($sniffer.history) { - if (replace) history.replaceState(null, '', url); - else { - history.pushState(null, '', url); - // Crazy Opera Bug: http://my.opera.com/community/forums/topic.dml?id=1185462 - baseElement.attr('href', baseElement.attr('href')); - } - } else { - if (replace) location.replace(url); - else location.href = url; - } - return self; - // getter - } else { - // the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172 - return location.href.replace(/%27/g,"'"); - } - }; - - var urlChangeListeners = [], - urlChangeInit = false; - - function fireUrlChange() { - if (lastBrowserUrl == self.url()) return; - - lastBrowserUrl = self.url(); - forEach(urlChangeListeners, function(listener) { - listener(self.url()); - }); - } - - /** - * @name ng.$browser#onUrlChange - * @methodOf ng.$browser - * @TODO(vojta): refactor to use node's syntax for events - * - * @description - * Register callback function that will be called, when url changes. - * - * It's only called when the url is changed by outside of angular: - * - user types different url into address bar - * - user clicks on history (forward/back) button - * - user clicks on a link - * - * It's not called when url is changed by $browser.url() method - * - * The listener gets called with new url as parameter. - * - * NOTE: this api is intended for use only by the $location service. Please use the - * {@link ng.$location $location service} to monitor url changes in angular apps. - * - * @param {function(string)} listener Listener function to be called when url changes. - * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous. - */ - self.onUrlChange = function(callback) { - if (!urlChangeInit) { - // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera) - // don't fire popstate when user change the address bar and don't fire hashchange when url - // changed by push/replaceState - - // html5 history api - popstate event - if ($sniffer.history) jqLite(window).bind('popstate', fireUrlChange); - // hashchange event - if ($sniffer.hashchange) jqLite(window).bind('hashchange', fireUrlChange); - // polling - else self.addPollFn(fireUrlChange); - - urlChangeInit = true; - } - - urlChangeListeners.push(callback); - return callback; - }; - - ////////////////////////////////////////////////////////////// - // Misc API - ////////////////////////////////////////////////////////////// - - /** - * Returns current <base href> - * (always relative - without domain) - * - * @returns {string=} - */ - self.baseHref = function() { - var href = baseElement.attr('href'); - return href ? href.replace(/^https?\:\/\/[^\/]*/, '') : href; - }; - - ////////////////////////////////////////////////////////////// - // Cookies API - ////////////////////////////////////////////////////////////// - var lastCookies = {}; - var lastCookieString = ''; - var cookiePath = self.baseHref(); - - /** - * @name ng.$browser#cookies - * @methodOf ng.$browser - * - * @param {string=} name Cookie name - * @param {string=} value Cokkie value - * - * @description - * The cookies method provides a 'private' low level access to browser cookies. - * It is not meant to be used directly, use the $cookie service instead. - * - * The return values vary depending on the arguments that the method was called with as follows: - * <ul> - * <li>cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify it</li> - * <li>cookies(name, value) -> set name to value, if value is undefined delete the cookie</li> - * <li>cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that way)</li> - * </ul> - * - * @returns {Object} Hash of all cookies (if called without any parameter) - */ - self.cookies = function(name, value) { - var cookieLength, cookieArray, cookie, i, index; - - if (name) { - if (value === undefined) { - rawDocument.cookie = escape(name) + "=;path=" + cookiePath + ";expires=Thu, 01 Jan 1970 00:00:00 GMT"; - } else { - if (isString(value)) { - cookieLength = (rawDocument.cookie = escape(name) + '=' + escape(value) + ';path=' + cookiePath).length + 1; - if (cookieLength > 4096) { - $log.warn("Cookie '"+ name +"' possibly not set or overflowed because it was too large ("+ - cookieLength + " > 4096 bytes)!"); - } - if (lastCookies.length > 20) { - $log.warn("Cookie '"+ name +"' possibly not set or overflowed because too many cookies " + - "were already set (" + lastCookies.length + " > 20 )"); - } - } - } - } else { - if (rawDocument.cookie !== lastCookieString) { - lastCookieString = rawDocument.cookie; - cookieArray = lastCookieString.split("; "); - lastCookies = {}; - - for (i = 0; i < cookieArray.length; i++) { - cookie = cookieArray[i]; - index = cookie.indexOf('='); - if (index > 0) { //ignore nameless cookies - lastCookies[unescape(cookie.substring(0, index))] = unescape(cookie.substring(index + 1)); - } - } - } - return lastCookies; - } - }; - - - /** - * @name ng.$browser#defer - * @methodOf ng.$browser - * @param {function()} fn A function, who's execution should be defered. - * @param {number=} [delay=0] of milliseconds to defer the function execution. - * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`. - * - * @description - * Executes a fn asynchroniously via `setTimeout(fn, delay)`. - * - * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using - * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed - * via `$browser.defer.flush()`. - * - */ - self.defer = function(fn, delay) { - var timeoutId; - outstandingRequestCount++; - timeoutId = setTimeout(function() { - delete pendingDeferIds[timeoutId]; - completeOutstandingRequest(fn); - }, delay || 0); - pendingDeferIds[timeoutId] = true; - return timeoutId; - }; - - - /** - * @name ng.$browser#defer.cancel - * @methodOf ng.$browser.defer - * - * @description - * Cancels a defered task identified with `deferId`. - * - * @param {*} deferId Token returned by the `$browser.defer` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfuly canceled. - */ - self.defer.cancel = function(deferId) { - if (pendingDeferIds[deferId]) { - delete pendingDeferIds[deferId]; - clearTimeout(deferId); - completeOutstandingRequest(noop); - return true; - } - return false; - }; - -} - -function $BrowserProvider(){ - this.$get = ['$window', '$log', '$sniffer', '$document', - function( $window, $log, $sniffer, $document){ - return new Browser($window, $document, $log, $sniffer); - }]; -} -/** - * @ngdoc object - * @name ng.$cacheFactory - * - * @description - * Factory that constructs cache objects. - * - * - * @param {string} cacheId Name or id of the newly created cache. - * @param {object=} options Options object that specifies the cache behavior. Properties: - * - * - `{number=}` `capacity` — turns the cache into LRU cache. - * - * @returns {object} Newly created cache object with the following set of methods: - * - * - `{object}` `info()` — Returns id, size, and options of cache. - * - `{void}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache. - * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss. - * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache. - * - `{void}` `removeAll()` — Removes all cached values. - * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory. - * - */ -function $CacheFactoryProvider() { - - this.$get = function() { - var caches = {}; - - function cacheFactory(cacheId, options) { - if (cacheId in caches) { - throw Error('cacheId ' + cacheId + ' taken'); - } - - var size = 0, - stats = extend({}, options, {id: cacheId}), - data = {}, - capacity = (options && options.capacity) || Number.MAX_VALUE, - lruHash = {}, - freshEnd = null, - staleEnd = null; - - return caches[cacheId] = { - - put: function(key, value) { - var lruEntry = lruHash[key] || (lruHash[key] = {key: key}); - - refresh(lruEntry); - - if (isUndefined(value)) return; - if (!(key in data)) size++; - data[key] = value; - - if (size > capacity) { - this.remove(staleEnd.key); - } - }, - - - get: function(key) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - refresh(lruEntry); - - return data[key]; - }, - - - remove: function(key) { - var lruEntry = lruHash[key]; - - if (!lruEntry) return; - - if (lruEntry == freshEnd) freshEnd = lruEntry.p; - if (lruEntry == staleEnd) staleEnd = lruEntry.n; - link(lruEntry.n,lruEntry.p); - - delete lruHash[key]; - delete data[key]; - size--; - }, - - - removeAll: function() { - data = {}; - size = 0; - lruHash = {}; - freshEnd = staleEnd = null; - }, - - - destroy: function() { - data = null; - stats = null; - lruHash = null; - delete caches[cacheId]; - }, - - - info: function() { - return extend({}, stats, {size: size}); - } - }; - - - /** - * makes the `entry` the freshEnd of the LRU linked list - */ - function refresh(entry) { - if (entry != freshEnd) { - if (!staleEnd) { - staleEnd = entry; - } else if (staleEnd == entry) { - staleEnd = entry.n; - } - - link(entry.n, entry.p); - link(entry, freshEnd); - freshEnd = entry; - freshEnd.n = null; - } - } - - - /** - * bidirectionally links two entries of the LRU linked list - */ - function link(nextEntry, prevEntry) { - if (nextEntry != prevEntry) { - if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify - if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify - } - } - } - - - cacheFactory.info = function() { - var info = {}; - forEach(caches, function(cache, cacheId) { - info[cacheId] = cache.info(); - }); - return info; - }; - - - cacheFactory.get = function(cacheId) { - return caches[cacheId]; - }; - - - return cacheFactory; - }; -} - -/** - * @ngdoc object - * @name ng.$templateCache - * - * @description - * Cache used for storing html templates. - * - * See {@link ng.$cacheFactory $cacheFactory}. - * - */ -function $TemplateCacheProvider() { - this.$get = ['$cacheFactory', function($cacheFactory) { - return $cacheFactory('templates'); - }]; -} - -/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE! - * - * DOM-related variables: - * - * - "node" - DOM Node - * - "element" - DOM Element or Node - * - "$node" or "$element" - jqLite-wrapped node or element - * - * - * Compiler related stuff: - * - * - "linkFn" - linking fn of a single directive - * - "nodeLinkFn" - function that aggregates all linking fns for a particular node - * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node - * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList) - */ - - -var NON_ASSIGNABLE_MODEL_EXPRESSION = 'Non-assignable model expression: '; - - -/** - * @ngdoc function - * @name ng.$compile - * @function - * - * @description - * Compiles a piece of HTML string or DOM into a template and produces a template function, which - * can then be used to link {@link ng.$rootScope.Scope scope} and the template together. - * - * The compilation is a process of walking the DOM tree and trying to match DOM elements to - * {@link ng.$compileProvider#directive directives}. For each match it - * executes corresponding template function and collects the - * instance functions into a single template function which is then returned. - * - * The template function can then be used once to produce the view or as it is the case with - * {@link ng.directive:ngRepeat repeater} many-times, in which - * case each call results in a view that is a DOM clone of the original template. - * - <doc:example module="compile"> - <doc:source> - <script> - // declare a new module, and inject the $compileProvider - angular.module('compile', [], function($compileProvider) { - // configure new 'compile' directive by passing a directive - // factory function. The factory function injects the '$compile' - $compileProvider.directive('compile', function($compile) { - // directive factory creates a link function - return function(scope, element, attrs) { - scope.$watch( - function(scope) { - // watch the 'compile' expression for changes - return scope.$eval(attrs.compile); - }, - function(value) { - // when the 'compile' expression changes - // assign it into the current DOM - element.html(value); - - // compile the new DOM and link it to the current - // scope. - // NOTE: we only compile .childNodes so that - // we don't get into infinite loop compiling ourselves - $compile(element.contents())(scope); - } - ); - }; - }) - }); - - function Ctrl($scope) { - $scope.name = 'Angular'; - $scope.html = 'Hello {{name}}'; - } - </script> - <div ng-controller="Ctrl"> - <input ng-model="name"> <br> - <textarea ng-model="html"></textarea> <br> - <div compile="html"></div> - </div> - </doc:source> - <doc:scenario> - it('should auto compile', function() { - expect(element('div[compile]').text()).toBe('Hello Angular'); - input('html').enter('{{name}}!'); - expect(element('div[compile]').text()).toBe('Angular!'); - }); - </doc:scenario> - </doc:example> - - * - * - * @param {string|DOMElement} element Element or HTML string to compile into a template function. - * @param {function(angular.Scope[, cloneAttachFn]} transclude function available to directives. - * @param {number} maxPriority only apply directives lower then given priority (Only effects the - * root element(s), not their children) - * @returns {function(scope[, cloneAttachFn])} a link function which is used to bind template - * (a DOM element/tree) to a scope. Where: - * - * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to. - * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the - * `template` and call the `cloneAttachFn` function allowing the caller to attach the - * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is - * called as: <br> `cloneAttachFn(clonedElement, scope)` where: - * - * * `clonedElement` - is a clone of the original `element` passed into the compiler. - * * `scope` - is the current scope with which the linking function is working with. - * - * Calling the linking function returns the element of the template. It is either the original element - * passed in, or the clone of the element if the `cloneAttachFn` is provided. - * - * After linking the view is not updated until after a call to $digest which typically is done by - * Angular automatically. - * - * If you need access to the bound view, there are two ways to do it: - * - * - If you are not asking the linking function to clone the template, create the DOM element(s) - * before you send them to the compiler and keep this reference around. - * <pre> - * var element = $compile('<p>{{total}}</p>')(scope); - * </pre> - * - * - if on the other hand, you need the element to be cloned, the view reference from the original - * example would not point to the clone, but rather to the original template that was cloned. In - * this case, you can access the clone via the cloneAttachFn: - * <pre> - * var templateHTML = angular.element('<p>{{total}}</p>'), - * scope = ....; - * - * var clonedElement = $compile(templateHTML)(scope, function(clonedElement, scope) { - * //attach the clone to DOM document at the right place - * }); - * - * //now we have reference to the cloned DOM via `clone` - * </pre> - * - * - * For information on how the compiler works, see the - * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide. - */ - - -/** - * @ngdoc service - * @name ng.$compileProvider - * @function - * - * @description - */ -$CompileProvider.$inject = ['$provide']; -function $CompileProvider($provide) { - var hasDirectives = {}, - Suffix = 'Directive', - COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, - CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, - MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: '; - - - /** - * @ngdoc function - * @name ng.$compileProvider#directive - * @methodOf ng.$compileProvider - * @function - * - * @description - * Register a new directives with the compiler. - * - * @param {string} name Name of the directive in camel-case. (ie <code>ngBind</code> which will match as - * <code>ng-bind</code>). - * @param {function} directiveFactory An injectable directive factroy function. See {@link guide/directive} for more - * info. - * @returns {ng.$compileProvider} Self for chaining. - */ - this.directive = function registerDirective(name, directiveFactory) { - if (isString(name)) { - assertArg(directiveFactory, 'directive'); - if (!hasDirectives.hasOwnProperty(name)) { - hasDirectives[name] = []; - $provide.factory(name + Suffix, ['$injector', '$exceptionHandler', - function($injector, $exceptionHandler) { - var directives = []; - forEach(hasDirectives[name], function(directiveFactory) { - try { - var directive = $injector.invoke(directiveFactory); - if (isFunction(directive)) { - directive = { compile: valueFn(directive) }; - } else if (!directive.compile && directive.link) { - directive.compile = valueFn(directive.link); - } - directive.priority = directive.priority || 0; - directive.name = directive.name || name; - directive.require = directive.require || (directive.controller && directive.name); - directive.restrict = directive.restrict || 'A'; - directives.push(directive); - } catch (e) { - $exceptionHandler(e); - } - }); - return directives; - }]); - } - hasDirectives[name].push(directiveFactory); - } else { - forEach(name, reverseParams(registerDirective)); - } - return this; - }; - - - this.$get = [ - '$injector', '$interpolate', '$exceptionHandler', '$http', '$templateCache', '$parse', - '$controller', '$rootScope', - function($injector, $interpolate, $exceptionHandler, $http, $templateCache, $parse, - $controller, $rootScope) { - - var Attributes = function(element, attr) { - this.$$element = element; - this.$attr = attr || {}; - }; - - Attributes.prototype = { - $normalize: directiveNormalize, - - - /** - * Set a normalized attribute on the element in a way such that all directives - * can share the attribute. This function properly handles boolean attributes. - * @param {string} key Normalized key. (ie ngAttribute) - * @param {string|boolean} value The value to set. If `null` attribute will be deleted. - * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute. - * Defaults to true. - * @param {string=} attrName Optional none normalized name. Defaults to key. - */ - $set: function(key, value, writeAttr, attrName) { - var booleanKey = getBooleanAttrName(this.$$element[0], key), - $$observers = this.$$observers; - - if (booleanKey) { - this.$$element.prop(key, value); - attrName = booleanKey; - } - - this[key] = value; - - // translate normalized key to actual key - if (attrName) { - this.$attr[key] = attrName; - } else { - attrName = this.$attr[key]; - if (!attrName) { - this.$attr[key] = attrName = snake_case(key, '-'); - } - } - - if (writeAttr !== false) { - if (value === null || value === undefined) { - this.$$element.removeAttr(attrName); - } else { - this.$$element.attr(attrName, value); - } - } - - // fire observers - $$observers && forEach($$observers[key], function(fn) { - try { - fn(value); - } catch (e) { - $exceptionHandler(e); - } - }); - }, - - - /** - * Observe an interpolated attribute. - * The observer will never be called, if given attribute is not interpolated. - * - * @param {string} key Normalized key. (ie ngAttribute) . - * @param {function(*)} fn Function that will be called whenever the attribute value changes. - * @returns {function(*)} the `fn` Function passed in. - */ - $observe: function(key, fn) { - var attrs = this, - $$observers = (attrs.$$observers || (attrs.$$observers = {})), - listeners = ($$observers[key] || ($$observers[key] = [])); - - listeners.push(fn); - $rootScope.$evalAsync(function() { - if (!listeners.$$inter) { - // no one registered attribute interpolation function, so lets call it manually - fn(attrs[key]); - } - }); - return fn; - } - }; - - var startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(), - denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}') - ? identity - : function denormalizeTemplate(template) { - return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol); - }; - - - return compile; - - //================================ - - function compile($compileNodes, transcludeFn, maxPriority) { - if (!($compileNodes instanceof jqLite)) { - // jquery always rewraps, where as we need to preserve the original selector so that we can modify it. - $compileNodes = jqLite($compileNodes); - } - // We can not compile top level text elements since text nodes can be merged and we will - // not be able to attach scope data to them, so we will wrap them in <span> - forEach($compileNodes, function(node, index){ - if (node.nodeType == 3 /* text node */) { - $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0]; - } - }); - var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority); - return function publicLinkFn(scope, cloneConnectFn){ - assertArg(scope, 'scope'); - // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart - // and sometimes changes the structure of the DOM. - var $linkNode = cloneConnectFn - ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! - : $compileNodes; - $linkNode.data('$scope', scope); - safeAddClass($linkNode, 'ng-scope'); - if (cloneConnectFn) cloneConnectFn($linkNode, scope); - if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode); - return $linkNode; - }; - } - - function wrongMode(localName, mode) { - throw Error("Unsupported '" + mode + "' for '" + localName + "'."); - } - - function safeAddClass($element, className) { - try { - $element.addClass(className); - } catch(e) { - // ignore, since it means that we are trying to set class on - // SVG element, where class name is read-only. - } - } - - /** - * Compile function matches each node in nodeList against the directives. Once all directives - * for a particular node are collected their compile functions are executed. The compile - * functions return values - the linking functions - are combined into a composite linking - * function, which is the a linking function for the node. - * - * @param {NodeList} nodeList an array of nodes to compile - * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the - * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then the - * rootElement must be set the jqLite collection of the compile root. This is - * needed so that the jqLite collection items can be replaced with widgets. - * @param {number=} max directive priority - * @returns {?function} A composite linking function of all of the matched directives or null. - */ - function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority) { - var linkFns = [], - nodeLinkFn, childLinkFn, directives, attrs, linkFnFound; - - for(var i = 0; i < nodeList.length; i++) { - attrs = new Attributes(); - - // we must always refer to nodeList[i] since the nodes can be replaced underneath us. - directives = collectDirectives(nodeList[i], [], attrs, maxPriority); - - nodeLinkFn = (directives.length) - ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement) - : null; - - childLinkFn = (nodeLinkFn && nodeLinkFn.terminal || !nodeList[i].childNodes.length) - ? null - : compileNodes(nodeList[i].childNodes, - nodeLinkFn ? nodeLinkFn.transclude : transcludeFn); - - linkFns.push(nodeLinkFn); - linkFns.push(childLinkFn); - linkFnFound = (linkFnFound || nodeLinkFn || childLinkFn); - } - - // return a linking function if we have found anything, null otherwise - return linkFnFound ? compositeLinkFn : null; - - function compositeLinkFn(scope, nodeList, $rootElement, boundTranscludeFn) { - var nodeLinkFn, childLinkFn, node, childScope, childTranscludeFn; - - for(var i = 0, n = 0, ii = linkFns.length; i < ii; n++) { - node = nodeList[n]; - nodeLinkFn = linkFns[i++]; - childLinkFn = linkFns[i++]; - - if (nodeLinkFn) { - if (nodeLinkFn.scope) { - childScope = scope.$new(isObject(nodeLinkFn.scope)); - jqLite(node).data('$scope', childScope); - } else { - childScope = scope; - } - childTranscludeFn = nodeLinkFn.transclude; - if (childTranscludeFn || (!boundTranscludeFn && transcludeFn)) { - nodeLinkFn(childLinkFn, childScope, node, $rootElement, - (function(transcludeFn) { - return function(cloneFn) { - var transcludeScope = scope.$new(); - - return transcludeFn(transcludeScope, cloneFn). - bind('$destroy', bind(transcludeScope, transcludeScope.$destroy)); - }; - })(childTranscludeFn || transcludeFn) - ); - } else { - nodeLinkFn(childLinkFn, childScope, node, undefined, boundTranscludeFn); - } - } else if (childLinkFn) { - childLinkFn(scope, node.childNodes, undefined, boundTranscludeFn); - } - } - } - } - - - /** - * Looks for directives on the given node and adds them to the directive collection which is - * sorted. - * - * @param node Node to search. - * @param directives An array to which the directives are added to. This array is sorted before - * the function returns. - * @param attrs The shared attrs object which is used to populate the normalized attributes. - * @param {number=} maxPriority Max directive priority. - */ - function collectDirectives(node, directives, attrs, maxPriority) { - var nodeType = node.nodeType, - attrsMap = attrs.$attr, - match, - className; - - switch(nodeType) { - case 1: /* Element */ - // use the node name: <directive> - addDirective(directives, - directiveNormalize(nodeName_(node).toLowerCase()), 'E', maxPriority); - - // iterate over the attributes - for (var attr, name, nName, value, nAttrs = node.attributes, - j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) { - attr = nAttrs[j]; - if (attr.specified) { - name = attr.name; - nName = directiveNormalize(name.toLowerCase()); - attrsMap[nName] = name; - attrs[nName] = value = trim((msie && name == 'href') - ? decodeURIComponent(node.getAttribute(name, 2)) - : attr.value); - if (getBooleanAttrName(node, nName)) { - attrs[nName] = true; // presence means true - } - addAttrInterpolateDirective(node, directives, value, nName); - addDirective(directives, nName, 'A', maxPriority); - } - } - - // use class as directive - className = node.className; - if (isString(className) && className !== '') { - while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) { - nName = directiveNormalize(match[2]); - if (addDirective(directives, nName, 'C', maxPriority)) { - attrs[nName] = trim(match[3]); - } - className = className.substr(match.index + match[0].length); - } - } - break; - case 3: /* Text Node */ - addTextInterpolateDirective(directives, node.nodeValue); - break; - case 8: /* Comment */ - try { - match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue); - if (match) { - nName = directiveNormalize(match[1]); - if (addDirective(directives, nName, 'M', maxPriority)) { - attrs[nName] = trim(match[2]); - } - } - } catch (e) { - // turns out that under some circumstances IE9 throws errors when one attempts to read comment's node value. - // Just ignore it and continue. (Can't seem to reproduce in test case.) - } - break; - } - - directives.sort(byPriority); - return directives; - } - - - /** - * Once the directives have been collected their compile functions is executed. This method - * is responsible for inlining directive templates as well as terminating the application - * of the directives if the terminal directive has been reached.. - * - * @param {Array} directives Array of collected directives to execute their compile function. - * this needs to be pre-sorted by priority order. - * @param {Node} compileNode The raw DOM node to apply the compile functions to - * @param {Object} templateAttrs The shared attribute function - * @param {function(angular.Scope[, cloneAttachFn]} transcludeFn A linking function, where the - * scope argument is auto-generated to the new child of the transcluded parent scope. - * @param {DOMElement} $rootElement If we are working on the root of the compile tree then this - * argument has the root jqLite array so that we can replace widgets on it. - * @returns linkFn - */ - function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn, $rootElement) { - var terminalPriority = -Number.MAX_VALUE, - preLinkFns = [], - postLinkFns = [], - newScopeDirective = null, - newIsolateScopeDirective = null, - templateDirective = null, - $compileNode = templateAttrs.$$element = jqLite(compileNode), - directive, - directiveName, - $template, - transcludeDirective, - childTranscludeFn = transcludeFn, - controllerDirectives, - linkFn, - directiveValue; - - // executes all directives on the current element - for(var i = 0, ii = directives.length; i < ii; i++) { - directive = directives[i]; - $template = undefined; - - if (terminalPriority > directive.priority) { - break; // prevent further processing of directives - } - - if (directiveValue = directive.scope) { - assertNoDuplicate('isolated scope', newIsolateScopeDirective, directive, $compileNode); - if (isObject(directiveValue)) { - safeAddClass($compileNode, 'ng-isolate-scope'); - newIsolateScopeDirective = directive; - } - safeAddClass($compileNode, 'ng-scope'); - newScopeDirective = newScopeDirective || directive; - } - - directiveName = directive.name; - - if (directiveValue = directive.controller) { - controllerDirectives = controllerDirectives || {}; - assertNoDuplicate("'" + directiveName + "' controller", - controllerDirectives[directiveName], directive, $compileNode); - controllerDirectives[directiveName] = directive; - } - - if (directiveValue = directive.transclude) { - assertNoDuplicate('transclusion', transcludeDirective, directive, $compileNode); - transcludeDirective = directive; - terminalPriority = directive.priority; - if (directiveValue == 'element') { - $template = jqLite(compileNode); - $compileNode = templateAttrs.$$element = - jqLite('<!-- ' + directiveName + ': ' + templateAttrs[directiveName] + ' -->'); - compileNode = $compileNode[0]; - replaceWith($rootElement, jqLite($template[0]), compileNode); - childTranscludeFn = compile($template, transcludeFn, terminalPriority); - } else { - $template = jqLite(JQLiteClone(compileNode)).contents(); - $compileNode.html(''); // clear contents - childTranscludeFn = compile($template, transcludeFn); - } - } - - if ((directiveValue = directive.template)) { - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - directiveValue = denormalizeTemplate(directiveValue); - - if (directive.replace) { - $template = jqLite('<div>' + - trim(directiveValue) + - '</div>').contents(); - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== 1) { - throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue); - } - - replaceWith($rootElement, $compileNode, compileNode); - - var newTemplateAttrs = {$attr: {}}; - - // combine directives from the original node and from the template: - // - take the array of directives for this element - // - split it into two parts, those that were already applied and those that weren't - // - collect directives from the template, add them to the second group and sort them - // - append the second group with new directives to the first group - directives = directives.concat( - collectDirectives( - compileNode, - directives.splice(i + 1, directives.length - (i + 1)), - newTemplateAttrs - ) - ); - mergeTemplateAttributes(templateAttrs, newTemplateAttrs); - - ii = directives.length; - } else { - $compileNode.html(directiveValue); - } - } - - if (directive.templateUrl) { - assertNoDuplicate('template', templateDirective, directive, $compileNode); - templateDirective = directive; - nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), - nodeLinkFn, $compileNode, templateAttrs, $rootElement, directive.replace, - childTranscludeFn); - ii = directives.length; - } else if (directive.compile) { - try { - linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn); - if (isFunction(linkFn)) { - addLinkFns(null, linkFn); - } else if (linkFn) { - addLinkFns(linkFn.pre, linkFn.post); - } - } catch (e) { - $exceptionHandler(e, startingTag($compileNode)); - } - } - - if (directive.terminal) { - nodeLinkFn.terminal = true; - terminalPriority = Math.max(terminalPriority, directive.priority); - } - - } - - nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope; - nodeLinkFn.transclude = transcludeDirective && childTranscludeFn; - - // might be normal or delayed nodeLinkFn depending on if templateUrl is present - return nodeLinkFn; - - //////////////////// - - function addLinkFns(pre, post) { - if (pre) { - pre.require = directive.require; - preLinkFns.push(pre); - } - if (post) { - post.require = directive.require; - postLinkFns.push(post); - } - } - - - function getControllers(require, $element) { - var value, retrievalMethod = 'data', optional = false; - if (isString(require)) { - while((value = require.charAt(0)) == '^' || value == '?') { - require = require.substr(1); - if (value == '^') { - retrievalMethod = 'inheritedData'; - } - optional = optional || value == '?'; - } - value = $element[retrievalMethod]('$' + require + 'Controller'); - if (!value && !optional) { - throw Error("No controller: " + require); - } - return value; - } else if (isArray(require)) { - value = []; - forEach(require, function(require) { - value.push(getControllers(require, $element)); - }); - } - return value; - } - - - function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) { - var attrs, $element, i, ii, linkFn, controller; - - if (compileNode === linkNode) { - attrs = templateAttrs; - } else { - attrs = shallowCopy(templateAttrs, new Attributes(jqLite(linkNode), templateAttrs.$attr)); - } - $element = attrs.$$element; - - if (newIsolateScopeDirective) { - var LOCAL_REGEXP = /^\s*([@=&])\s*(\w*)\s*$/; - - var parentScope = scope.$parent || scope; - - forEach(newIsolateScopeDirective.scope, function(definiton, scopeName) { - var match = definiton.match(LOCAL_REGEXP) || [], - attrName = match[2]|| scopeName, - mode = match[1], // @, =, or & - lastValue, - parentGet, parentSet; - - switch (mode) { - - case '@': { - attrs.$observe(attrName, function(value) { - scope[scopeName] = value; - }); - attrs.$$observers[attrName].$$scope = parentScope; - break; - } - - case '=': { - parentGet = $parse(attrs[attrName]); - parentSet = parentGet.assign || function() { - // reset the change, or we will throw this exception on every $digest - lastValue = scope[scopeName] = parentGet(parentScope); - throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + attrs[attrName] + - ' (directive: ' + newIsolateScopeDirective.name + ')'); - }; - lastValue = scope[scopeName] = parentGet(parentScope); - scope.$watch(function parentValueWatch() { - var parentValue = parentGet(parentScope); - - if (parentValue !== scope[scopeName]) { - // we are out of sync and need to copy - if (parentValue !== lastValue) { - // parent changed and it has precedence - lastValue = scope[scopeName] = parentValue; - } else { - // if the parent can be assigned then do so - parentSet(parentScope, parentValue = lastValue = scope[scopeName]); - } - } - return parentValue; - }); - break; - } - - case '&': { - parentGet = $parse(attrs[attrName]); - scope[scopeName] = function(locals) { - return parentGet(parentScope, locals); - } - break; - } - - default: { - throw Error('Invalid isolate scope definition for directive ' + - newIsolateScopeDirective.name + ': ' + definiton); - } - } - }); - } - - if (controllerDirectives) { - forEach(controllerDirectives, function(directive) { - var locals = { - $scope: scope, - $element: $element, - $attrs: attrs, - $transclude: boundTranscludeFn - }; - - controller = directive.controller; - if (controller == '@') { - controller = attrs[directive.name]; - } - - $element.data( - '$' + directive.name + 'Controller', - $controller(controller, locals)); - }); - } - - // PRELINKING - for(i = 0, ii = preLinkFns.length; i < ii; i++) { - try { - linkFn = preLinkFns[i]; - linkFn(scope, $element, attrs, - linkFn.require && getControllers(linkFn.require, $element)); - } catch (e) { - $exceptionHandler(e, startingTag($element)); - } - } - - // RECURSION - childLinkFn && childLinkFn(scope, linkNode.childNodes, undefined, boundTranscludeFn); - - // POSTLINKING - for(i = 0, ii = postLinkFns.length; i < ii; i++) { - try { - linkFn = postLinkFns[i]; - linkFn(scope, $element, attrs, - linkFn.require && getControllers(linkFn.require, $element)); - } catch (e) { - $exceptionHandler(e, startingTag($element)); - } - } - } - } - - - /** - * looks up the directive and decorates it with exception handling and proper parameters. We - * call this the boundDirective. - * - * @param {string} name name of the directive to look up. - * @param {string} location The directive must be found in specific format. - * String containing any of theses characters: - * - * * `E`: element name - * * `A': attribute - * * `C`: class - * * `M`: comment - * @returns true if directive was added. - */ - function addDirective(tDirectives, name, location, maxPriority) { - var match = false; - if (hasDirectives.hasOwnProperty(name)) { - for(var directive, directives = $injector.get(name + Suffix), - i = 0, ii = directives.length; i<ii; i++) { - try { - directive = directives[i]; - if ( (maxPriority === undefined || maxPriority > directive.priority) && - directive.restrict.indexOf(location) != -1) { - tDirectives.push(directive); - match = true; - } - } catch(e) { $exceptionHandler(e); } - } - } - return match; - } - - - /** - * When the element is replaced with HTML template then the new attributes - * on the template need to be merged with the existing attributes in the DOM. - * The desired effect is to have both of the attributes present. - * - * @param {object} dst destination attributes (original DOM) - * @param {object} src source attributes (from the directive template) - */ - function mergeTemplateAttributes(dst, src) { - var srcAttr = src.$attr, - dstAttr = dst.$attr, - $element = dst.$$element; - - // reapply the old attributes to the new element - forEach(dst, function(value, key) { - if (key.charAt(0) != '$') { - if (src[key]) { - value += (key === 'style' ? ';' : ' ') + src[key]; - } - dst.$set(key, value, true, srcAttr[key]); - } - }); - - // copy the new attributes on the old attrs object - forEach(src, function(value, key) { - if (key == 'class') { - safeAddClass($element, value); - dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value; - } else if (key == 'style') { - $element.attr('style', $element.attr('style') + ';' + value); - } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) { - dst[key] = value; - dstAttr[key] = srcAttr[key]; - } - }); - } - - - function compileTemplateUrl(directives, beforeTemplateNodeLinkFn, $compileNode, tAttrs, - $rootElement, replace, childTranscludeFn) { - var linkQueue = [], - afterTemplateNodeLinkFn, - afterTemplateChildLinkFn, - beforeTemplateCompileNode = $compileNode[0], - origAsyncDirective = directives.shift(), - // The fact that we have to copy and patch the directive seems wrong! - derivedSyncDirective = extend({}, origAsyncDirective, { - controller: null, templateUrl: null, transclude: null, scope: null - }); - - $compileNode.html(''); - - $http.get(origAsyncDirective.templateUrl, {cache: $templateCache}). - success(function(content) { - var compileNode, tempTemplateAttrs, $template; - - content = denormalizeTemplate(content); - - if (replace) { - $template = jqLite('<div>' + trim(content) + '</div>').contents(); - compileNode = $template[0]; - - if ($template.length != 1 || compileNode.nodeType !== 1) { - throw new Error(MULTI_ROOT_TEMPLATE_ERROR + content); - } - - tempTemplateAttrs = {$attr: {}}; - replaceWith($rootElement, $compileNode, compileNode); - collectDirectives(compileNode, directives, tempTemplateAttrs); - mergeTemplateAttributes(tAttrs, tempTemplateAttrs); - } else { - compileNode = beforeTemplateCompileNode; - $compileNode.html(content); - } - - directives.unshift(derivedSyncDirective); - afterTemplateNodeLinkFn = applyDirectivesToNode(directives, $compileNode, tAttrs, childTranscludeFn); - afterTemplateChildLinkFn = compileNodes($compileNode.contents(), childTranscludeFn); - - - while(linkQueue.length) { - var controller = linkQueue.pop(), - linkRootElement = linkQueue.pop(), - beforeTemplateLinkNode = linkQueue.pop(), - scope = linkQueue.pop(), - linkNode = compileNode; - - if (beforeTemplateLinkNode !== beforeTemplateCompileNode) { - // it was cloned therefore we have to clone as well. - linkNode = JQLiteClone(compileNode); - replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); - } - - afterTemplateNodeLinkFn(function() { - beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement, controller); - }, scope, linkNode, $rootElement, controller); - } - linkQueue = null; - }). - error(function(response, code, headers, config) { - throw Error('Failed to load template: ' + config.url); - }); - - return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, controller) { - if (linkQueue) { - linkQueue.push(scope); - linkQueue.push(node); - linkQueue.push(rootElement); - linkQueue.push(controller); - } else { - afterTemplateNodeLinkFn(function() { - beforeTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, controller); - }, scope, node, rootElement, controller); - } - }; - } - - - /** - * Sorting function for bound directives. - */ - function byPriority(a, b) { - return b.priority - a.priority; - } - - - function assertNoDuplicate(what, previousDirective, directive, element) { - if (previousDirective) { - throw Error('Multiple directives [' + previousDirective.name + ', ' + - directive.name + '] asking for ' + what + ' on: ' + startingTag(element)); - } - } - - - function addTextInterpolateDirective(directives, text) { - var interpolateFn = $interpolate(text, true); - if (interpolateFn) { - directives.push({ - priority: 0, - compile: valueFn(function textInterpolateLinkFn(scope, node) { - var parent = node.parent(), - bindings = parent.data('$binding') || []; - bindings.push(interpolateFn); - safeAddClass(parent.data('$binding', bindings), 'ng-binding'); - scope.$watch(interpolateFn, function interpolateFnWatchAction(value) { - node[0].nodeValue = value; - }); - }) - }); - } - } - - - function addAttrInterpolateDirective(node, directives, value, name) { - var interpolateFn = $interpolate(value, true); - - - // no interpolation found -> ignore - if (!interpolateFn) return; - - directives.push({ - priority: 100, - compile: valueFn(function attrInterpolateLinkFn(scope, element, attr) { - var $$observers = (attr.$$observers || (attr.$$observers = {})); - - if (name === 'class') { - // we need to interpolate classes again, in the case the element was replaced - // and therefore the two class attrs got merged - we want to interpolate the result - interpolateFn = $interpolate(attr[name], true); - } - - attr[name] = undefined; - ($$observers[name] || ($$observers[name] = [])).$$inter = true; - (attr.$$observers && attr.$$observers[name].$$scope || scope). - $watch(interpolateFn, function interpolateFnWatchAction(value) { - attr.$set(name, value); - }); - }) - }); - } - - - /** - * This is a special jqLite.replaceWith, which can replace items which - * have no parents, provided that the containing jqLite collection is provided. - * - * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes - * in the root of the tree. - * @param {JqLite} $element The jqLite element which we are going to replace. We keep the shell, - * but replace its DOM node reference. - * @param {Node} newNode The new DOM node. - */ - function replaceWith($rootElement, $element, newNode) { - var oldNode = $element[0], - parent = oldNode.parentNode, - i, ii; - - if ($rootElement) { - for(i = 0, ii = $rootElement.length; i < ii; i++) { - if ($rootElement[i] == oldNode) { - $rootElement[i] = newNode; - break; - } - } - } - - if (parent) { - parent.replaceChild(newNode, oldNode); - } - - newNode[jqLite.expando] = oldNode[jqLite.expando]; - $element[0] = newNode; - } - }]; -} - -var PREFIX_REGEXP = /^(x[\:\-_]|data[\:\-_])/i; -/** - * Converts all accepted directives format into proper directive name. - * All of these will become 'myDirective': - * my:DiRective - * my-directive - * x-my-directive - * data-my:directive - * - * Also there is special case for Moz prefix starting with upper case letter. - * @param name Name to normalize - */ -function directiveNormalize(name) { - return camelCase(name.replace(PREFIX_REGEXP, '')); -} - -/** - * @ngdoc object - * @name ng.$compile.directive.Attributes - * @description - * - * A shared object between directive compile / linking functions which contains normalized DOM element - * attributes. The the values reflect current binding state `{{ }}`. The normalization is needed - * since all of these are treated as equivalent in Angular: - * - * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a"> - */ - -/** - * @ngdoc property - * @name ng.$compile.directive.Attributes#$attr - * @propertyOf ng.$compile.directive.Attributes - * @returns {object} A map of DOM element attribute names to the normalized name. This is - * needed to do reverse lookup from normalized name back to actual name. - */ - - -/** - * @ngdoc function - * @name ng.$compile.directive.Attributes#$set - * @methodOf ng.$compile.directive.Attributes - * @function - * - * @description - * Set DOM element attribute value. - * - * - * @param {string} name Normalized element attribute name of the property to modify. The name is - * revers translated using the {@link ng.$compile.directive.Attributes#$attr $attr} - * property to the original name. - * @param {string} value Value to set the attribute to. - */ - - - -/** - * Closure compiler type information - */ - -function nodesetLinkingFn( - /* angular.Scope */ scope, - /* NodeList */ nodeList, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -){} - -function directiveLinkingFn( - /* nodesetLinkingFn */ nodesetLinkingFn, - /* angular.Scope */ scope, - /* Node */ node, - /* Element */ rootElement, - /* function(Function) */ boundTranscludeFn -){} - -/** - * @ngdoc object - * @name ng.$controllerProvider - * @description - * The {@link ng.$controller $controller service} is used by Angular to create new - * controllers. - * - * This provider allows controller registration via the - * {@link ng.$controllerProvider#register register} method. - */ -function $ControllerProvider() { - var controllers = {}; - - - /** - * @ngdoc function - * @name ng.$controllerProvider#register - * @methodOf ng.$controllerProvider - * @param {string} name Controller name - * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI - * annotations in the array notation). - */ - this.register = function(name, constructor) { - if (isObject(name)) { - extend(controllers, name) - } else { - controllers[name] = constructor; - } - }; - - - this.$get = ['$injector', '$window', function($injector, $window) { - - /** - * @ngdoc function - * @name ng.$controller - * @requires $injector - * - * @param {Function|string} constructor If called with a function then it's considered to be the - * controller constructor function. Otherwise it's considered to be a string which is used - * to retrieve the controller constructor using the following steps: - * - * * check if a controller with given name is registered via `$controllerProvider` - * * check if evaluating the string on the current scope returns a constructor - * * check `window[constructor]` on the global `window` object - * - * @param {Object} locals Injection locals for Controller. - * @return {Object} Instance of given controller. - * - * @description - * `$controller` service is responsible for instantiating controllers. - * - * It's just simple call to {@link AUTO.$injector $injector}, but extracted into - * a service, so that one can override this service with {@link https://gist.github.com/1649788 - * BC version}. - */ - return function(constructor, locals) { - if(isString(constructor)) { - var name = constructor; - constructor = controllers.hasOwnProperty(name) - ? controllers[name] - : getter(locals.$scope, name, true) || getter($window, name, true); - - assertArgFn(constructor, name, true); - } - - return $injector.instantiate(constructor, locals); - }; - }]; -} - -/** - * @ngdoc object - * @name ng.$document - * @requires $window - * - * @description - * A {@link angular.element jQuery (lite)}-wrapped reference to the browser's `window.document` - * element. - */ -function $DocumentProvider(){ - this.$get = ['$window', function(window){ - return jqLite(window.document); - }]; -} - -/** - * @ngdoc function - * @name ng.$exceptionHandler - * @requires $log - * - * @description - * Any uncaught exception in angular expressions is delegated to this service. - * The default implementation simply delegates to `$log.error` which logs it into - * the browser console. - * - * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by - * {@link ngMock.$exceptionHandler mock $exceptionHandler} - * - * @param {Error} exception Exception associated with the error. - * @param {string=} cause optional information about the context in which - * the error was thrown. - */ -function $ExceptionHandlerProvider() { - this.$get = ['$log', function($log){ - return function(exception, cause) { - $log.error.apply($log, arguments); - }; - }]; -} - -/** - * @ngdoc object - * @name ng.$interpolateProvider - * @function - * - * @description - * - * Used for configuring the interpolation markup. Defaults to `{{` and `}}`. - */ -function $InterpolateProvider() { - var startSymbol = '{{'; - var endSymbol = '}}'; - - /** - * @ngdoc method - * @name ng.$interpolateProvider#startSymbol - * @methodOf ng.$interpolateProvider - * @description - * Symbol to denote start of expression in the interpolated string. Defaults to `{{`. - * - * @param {string=} value new value to set the starting symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.startSymbol = function(value){ - if (value) { - startSymbol = value; - return this; - } else { - return startSymbol; - } - }; - - /** - * @ngdoc method - * @name ng.$interpolateProvider#endSymbol - * @methodOf ng.$interpolateProvider - * @description - * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. - * - * @param {string=} value new value to set the ending symbol to. - * @returns {string|self} Returns the symbol when used as getter and self if used as setter. - */ - this.endSymbol = function(value){ - if (value) { - endSymbol = value; - return this; - } else { - return endSymbol; - } - }; - - - this.$get = ['$parse', function($parse) { - var startSymbolLength = startSymbol.length, - endSymbolLength = endSymbol.length; - - /** - * @ngdoc function - * @name ng.$interpolate - * @function - * - * @requires $parse - * - * @description - * - * Compiles a string with markup into an interpolation function. This service is used by the - * HTML {@link ng.$compile $compile} service for data binding. See - * {@link ng.$interpolateProvider $interpolateProvider} for configuring the - * interpolation markup. - * - * - <pre> - var $interpolate = ...; // injected - var exp = $interpolate('Hello {{name}}!'); - expect(exp({name:'Angular'}).toEqual('Hello Angular!'); - </pre> - * - * - * @param {string} text The text with markup to interpolate. - * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have - * embedded expression in order to return an interpolation function. Strings with no - * embedded expression will return null for the interpolation function. - * @returns {function(context)} an interpolation function which is used to compute the interpolated - * string. The function has these parameters: - * - * * `context`: an object against which any expressions embedded in the strings are evaluated - * against. - * - */ - function $interpolate(text, mustHaveExpression) { - var startIndex, - endIndex, - index = 0, - parts = [], - length = text.length, - hasInterpolation = false, - fn, - exp, - concat = []; - - while(index < length) { - if ( ((startIndex = text.indexOf(startSymbol, index)) != -1) && - ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1) ) { - (index != startIndex) && parts.push(text.substring(index, startIndex)); - parts.push(fn = $parse(exp = text.substring(startIndex + startSymbolLength, endIndex))); - fn.exp = exp; - index = endIndex + endSymbolLength; - hasInterpolation = true; - } else { - // we did not find anything, so we have to add the remainder to the parts array - (index != length) && parts.push(text.substring(index)); - index = length; - } - } - - if (!(length = parts.length)) { - // we added, nothing, must have been an empty string. - parts.push(''); - length = 1; - } - - if (!mustHaveExpression || hasInterpolation) { - concat.length = length; - fn = function(context) { - for(var i = 0, ii = length, part; i<ii; i++) { - if (typeof (part = parts[i]) == 'function') { - part = part(context); - if (part == null || part == undefined) { - part = ''; - } else if (typeof part != 'string') { - part = toJson(part); - } - } - concat[i] = part; - } - return concat.join(''); - }; - fn.exp = text; - fn.parts = parts; - return fn; - } - } - - - /** - * @ngdoc method - * @name ng.$interpolate#startSymbol - * @methodOf ng.$interpolate - * @description - * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`. - * - * Use {@link ng.$interpolateProvider#startSymbol $interpolateProvider#startSymbol} to change - * the symbol. - * - * @returns {string} start symbol. - */ - $interpolate.startSymbol = function() { - return startSymbol; - } - - - /** - * @ngdoc method - * @name ng.$interpolate#endSymbol - * @methodOf ng.$interpolate - * @description - * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`. - * - * Use {@link ng.$interpolateProvider#endSymbol $interpolateProvider#endSymbol} to change - * the symbol. - * - * @returns {string} start symbol. - */ - $interpolate.endSymbol = function() { - return endSymbol; - } - - return $interpolate; - }]; -} - -var URL_MATCH = /^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/, - PATH_MATCH = /^([^\?#]*)?(\?([^#]*))?(#(.*))?$/, - HASH_MATCH = PATH_MATCH, - DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21}; - - -/** - * Encode path using encodeUriSegment, ignoring forward slashes - * - * @param {string} path Path to encode - * @returns {string} - */ -function encodePath(path) { - var segments = path.split('/'), - i = segments.length; - - while (i--) { - segments[i] = encodeUriSegment(segments[i]); - } - - return segments.join('/'); -} - -function stripHash(url) { - return url.split('#')[0]; -} - - -function matchUrl(url, obj) { - var match = URL_MATCH.exec(url); - - match = { - protocol: match[1], - host: match[3], - port: int(match[5]) || DEFAULT_PORTS[match[1]] || null, - path: match[6] || '/', - search: match[8], - hash: match[10] - }; - - if (obj) { - obj.$$protocol = match.protocol; - obj.$$host = match.host; - obj.$$port = match.port; - } - - return match; -} - - -function composeProtocolHostPort(protocol, host, port) { - return protocol + '://' + host + (port == DEFAULT_PORTS[protocol] ? '' : ':' + port); -} - - -function pathPrefixFromBase(basePath) { - return basePath.substr(0, basePath.lastIndexOf('/')); -} - - -function convertToHtml5Url(url, basePath, hashPrefix) { - var match = matchUrl(url); - - // already html5 url - if (decodeURIComponent(match.path) != basePath || isUndefined(match.hash) || - match.hash.indexOf(hashPrefix) !== 0) { - return url; - // convert hashbang url -> html5 url - } else { - return composeProtocolHostPort(match.protocol, match.host, match.port) + - pathPrefixFromBase(basePath) + match.hash.substr(hashPrefix.length); - } -} - - -function convertToHashbangUrl(url, basePath, hashPrefix) { - var match = matchUrl(url); - - // already hashbang url - if (decodeURIComponent(match.path) == basePath) { - return url; - // convert html5 url -> hashbang url - } else { - var search = match.search && '?' + match.search || '', - hash = match.hash && '#' + match.hash || '', - pathPrefix = pathPrefixFromBase(basePath), - path = match.path.substr(pathPrefix.length); - - if (match.path.indexOf(pathPrefix) !== 0) { - throw Error('Invalid url "' + url + '", missing path prefix "' + pathPrefix + '" !'); - } - - return composeProtocolHostPort(match.protocol, match.host, match.port) + basePath + - '#' + hashPrefix + path + search + hash; - } -} - - -/** - * LocationUrl represents an url - * This object is exposed as $location service when HTML5 mode is enabled and supported - * - * @constructor - * @param {string} url HTML5 url - * @param {string} pathPrefix - */ -function LocationUrl(url, pathPrefix, appBaseUrl) { - pathPrefix = pathPrefix || ''; - - /** - * Parse given html5 (regular) url string into properties - * @param {string} newAbsoluteUrl HTML5 url - * @private - */ - this.$$parse = function(newAbsoluteUrl) { - var match = matchUrl(newAbsoluteUrl, this); - - if (match.path.indexOf(pathPrefix) !== 0) { - throw Error('Invalid url "' + newAbsoluteUrl + '", missing path prefix "' + pathPrefix + '" !'); - } - - this.$$path = decodeURIComponent(match.path.substr(pathPrefix.length)); - this.$$search = parseKeyValue(match.search); - this.$$hash = match.hash && decodeURIComponent(match.hash) || ''; - - this.$$compose(); - }; - - /** - * Compose url and update `absUrl` property - * @private - */ - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) + - pathPrefix + this.$$url; - }; - - - this.$$rewriteAppUrl = function(absoluteLinkUrl) { - if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) { - return absoluteLinkUrl; - } - } - - - this.$$parse(url); -} - - -/** - * LocationHashbangUrl represents url - * This object is exposed as $location service when html5 history api is disabled or not supported - * - * @constructor - * @param {string} url Legacy url - * @param {string} hashPrefix Prefix for hash part (containing path and search) - */ -function LocationHashbangUrl(url, hashPrefix, appBaseUrl) { - var basePath; - - /** - * Parse given hashbang url into properties - * @param {string} url Hashbang url - * @private - */ - this.$$parse = function(url) { - var match = matchUrl(url, this); - - - if (match.hash && match.hash.indexOf(hashPrefix) !== 0) { - throw Error('Invalid url "' + url + '", missing hash prefix "' + hashPrefix + '" !'); - } - - basePath = match.path + (match.search ? '?' + match.search : ''); - match = HASH_MATCH.exec((match.hash || '').substr(hashPrefix.length)); - if (match[1]) { - this.$$path = (match[1].charAt(0) == '/' ? '' : '/') + decodeURIComponent(match[1]); - } else { - this.$$path = ''; - } - - this.$$search = parseKeyValue(match[3]); - this.$$hash = match[5] && decodeURIComponent(match[5]) || ''; - - this.$$compose(); - }; - - /** - * Compose hashbang url and update `absUrl` property - * @private - */ - this.$$compose = function() { - var search = toKeyValue(this.$$search), - hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : ''; - - this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash; - this.$$absUrl = composeProtocolHostPort(this.$$protocol, this.$$host, this.$$port) + - basePath + (this.$$url ? '#' + hashPrefix + this.$$url : ''); - }; - - this.$$rewriteAppUrl = function(absoluteLinkUrl) { - if(absoluteLinkUrl.indexOf(appBaseUrl) == 0) { - return absoluteLinkUrl; - } - } - - - this.$$parse(url); -} - - -LocationUrl.prototype = { - - /** - * Has any change been replacing ? - * @private - */ - $$replace: false, - - /** - * @ngdoc method - * @name ng.$location#absUrl - * @methodOf ng.$location - * - * @description - * This method is getter only. - * - * Return full url representation with all segments encoded according to rules specified in - * {@link http://www.ietf.org/rfc/rfc3986.txt RFC 3986}. - * - * @return {string} full url - */ - absUrl: locationGetter('$$absUrl'), - - /** - * @ngdoc method - * @name ng.$location#url - * @methodOf ng.$location - * - * @description - * This method is getter / setter. - * - * Return url (e.g. `/path?a=b#hash`) when called without any parameter. - * - * Change path, search and hash, when called with parameter and return `$location`. - * - * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`) - * @return {string} url - */ - url: function(url, replace) { - if (isUndefined(url)) - return this.$$url; - - var match = PATH_MATCH.exec(url); - if (match[1]) this.path(decodeURIComponent(match[1])); - if (match[2] || match[1]) this.search(match[3] || ''); - this.hash(match[5] || '', replace); - - return this; - }, - - /** - * @ngdoc method - * @name ng.$location#protocol - * @methodOf ng.$location - * - * @description - * This method is getter only. - * - * Return protocol of current url. - * - * @return {string} protocol of current url - */ - protocol: locationGetter('$$protocol'), - - /** - * @ngdoc method - * @name ng.$location#host - * @methodOf ng.$location - * - * @description - * This method is getter only. - * - * Return host of current url. - * - * @return {string} host of current url. - */ - host: locationGetter('$$host'), - - /** - * @ngdoc method - * @name ng.$location#port - * @methodOf ng.$location - * - * @description - * This method is getter only. - * - * Return port of current url. - * - * @return {Number} port - */ - port: locationGetter('$$port'), - - /** - * @ngdoc method - * @name ng.$location#path - * @methodOf ng.$location - * - * @description - * This method is getter / setter. - * - * Return path of current url when called without any parameter. - * - * Change path when called with parameter and return `$location`. - * - * Note: Path should always begin with forward slash (/), this method will add the forward slash - * if it is missing. - * - * @param {string=} path New path - * @return {string} path - */ - path: locationGetterSetter('$$path', function(path) { - return path.charAt(0) == '/' ? path : '/' + path; - }), - - /** - * @ngdoc method - * @name ng.$location#search - * @methodOf ng.$location - * - * @description - * This method is getter / setter. - * - * Return search part (as object) of current url when called without any parameter. - * - * Change search part when called with parameter and return `$location`. - * - * @param {string|object<string,string>=} search New search params - string or hash object - * @param {string=} paramValue If `search` is a string, then `paramValue` will override only a - * single search parameter. If the value is `null`, the parameter will be deleted. - * - * @return {string} search - */ - search: function(search, paramValue) { - if (isUndefined(search)) - return this.$$search; - - if (isDefined(paramValue)) { - if (paramValue === null) { - delete this.$$search[search]; - } else { - this.$$search[search] = paramValue; - } - } else { - this.$$search = isString(search) ? parseKeyValue(search) : search; - } - - this.$$compose(); - return this; - }, - - /** - * @ngdoc method - * @name ng.$location#hash - * @methodOf ng.$location - * - * @description - * This method is getter / setter. - * - * Return hash fragment when called without any parameter. - * - * Change hash fragment when called with parameter and return `$location`. - * - * @param {string=} hash New hash fragment - * @return {string} hash - */ - hash: locationGetterSetter('$$hash', identity), - - /** - * @ngdoc method - * @name ng.$location#replace - * @methodOf ng.$location - * - * @description - * If called, all changes to $location during current `$digest` will be replacing current history - * record, instead of adding new one. - */ - replace: function() { - this.$$replace = true; - return this; - } -}; - -LocationHashbangUrl.prototype = inherit(LocationUrl.prototype); - -function LocationHashbangInHtml5Url(url, hashPrefix, appBaseUrl, baseExtra) { - LocationHashbangUrl.apply(this, arguments); - - - this.$$rewriteAppUrl = function(absoluteLinkUrl) { - if (absoluteLinkUrl.indexOf(appBaseUrl) == 0) { - return appBaseUrl + baseExtra + '#' + hashPrefix + absoluteLinkUrl.substr(appBaseUrl.length); - } - } -} - -LocationHashbangInHtml5Url.prototype = inherit(LocationHashbangUrl.prototype); - -function locationGetter(property) { - return function() { - return this[property]; - }; -} - - -function locationGetterSetter(property, preprocess) { - return function(value) { - if (isUndefined(value)) - return this[property]; - - this[property] = preprocess(value); - this.$$compose(); - - return this; - }; -} - - -/** - * @ngdoc object - * @name ng.$location - * - * @requires $browser - * @requires $sniffer - * @requires $rootElement - * - * @description - * The $location service parses the URL in the browser address bar (based on the - * {@link https://developer.mozilla.org/en/window.location window.location}) and makes the URL - * available to your application. Changes to the URL in the address bar are reflected into - * $location service and changes to $location are reflected into the browser address bar. - * - * **The $location service:** - * - * - Exposes the current URL in the browser address bar, so you can - * - Watch and observe the URL. - * - Change the URL. - * - Synchronizes the URL with the browser when the user - * - Changes the address bar. - * - Clicks the back or forward button (or clicks a History link). - * - Clicks on a link. - * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash). - * - * For more information see {@link guide/dev_guide.services.$location Developer Guide: Angular - * Services: Using $location} - */ - -/** - * @ngdoc object - * @name ng.$locationProvider - * @description - * Use the `$locationProvider` to configure how the application deep linking paths are stored. - */ -function $LocationProvider(){ - var hashPrefix = '', - html5Mode = false; - - /** - * @ngdoc property - * @name ng.$locationProvider#hashPrefix - * @methodOf ng.$locationProvider - * @description - * @param {string=} prefix Prefix for hash part (containing path and search) - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.hashPrefix = function(prefix) { - if (isDefined(prefix)) { - hashPrefix = prefix; - return this; - } else { - return hashPrefix; - } - }; - - /** - * @ngdoc property - * @name ng.$locationProvider#html5Mode - * @methodOf ng.$locationProvider - * @description - * @param {string=} mode Use HTML5 strategy if available. - * @returns {*} current value if used as getter or itself (chaining) if used as setter - */ - this.html5Mode = function(mode) { - if (isDefined(mode)) { - html5Mode = mode; - return this; - } else { - return html5Mode; - } - }; - - this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', - function( $rootScope, $browser, $sniffer, $rootElement) { - var $location, - basePath, - pathPrefix, - initUrl = $browser.url(), - initUrlParts = matchUrl(initUrl), - appBaseUrl; - - if (html5Mode) { - basePath = $browser.baseHref() || '/'; - pathPrefix = pathPrefixFromBase(basePath); - appBaseUrl = - composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) + - pathPrefix + '/'; - - if ($sniffer.history) { - $location = new LocationUrl( - convertToHtml5Url(initUrl, basePath, hashPrefix), - pathPrefix, appBaseUrl); - } else { - $location = new LocationHashbangInHtml5Url( - convertToHashbangUrl(initUrl, basePath, hashPrefix), - hashPrefix, appBaseUrl, basePath.substr(pathPrefix.length + 1)); - } - } else { - appBaseUrl = - composeProtocolHostPort(initUrlParts.protocol, initUrlParts.host, initUrlParts.port) + - (initUrlParts.path || '') + - (initUrlParts.search ? ('?' + initUrlParts.search) : '') + - '#' + hashPrefix + '/'; - - $location = new LocationHashbangUrl(initUrl, hashPrefix, appBaseUrl); - } - - $rootElement.bind('click', function(event) { - // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser) - // currently we open nice url link and redirect then - - if (event.ctrlKey || event.metaKey || event.which == 2) return; - - var elm = jqLite(event.target); - - // traverse the DOM up to find first A tag - while (lowercase(elm[0].nodeName) !== 'a') { - // ignore rewriting if no A tag (reached root element, or no parent - removed from document) - if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return; - } - - var absHref = elm.prop('href'), - rewrittenUrl = $location.$$rewriteAppUrl(absHref); - - if (absHref && !elm.attr('target') && rewrittenUrl) { - // update location manually - $location.$$parse(rewrittenUrl); - $rootScope.$apply(); - event.preventDefault(); - // hack to work around FF6 bug 684208 when scenario runner clicks on links - window.angular['ff-684208-preventDefault'] = true; - } - }); - - - // rewrite hashbang url <> html5 url - if ($location.absUrl() != initUrl) { - $browser.url($location.absUrl(), true); - } - - // update $location when $browser url changes - $browser.onUrlChange(function(newUrl) { - if ($location.absUrl() != newUrl) { - $rootScope.$evalAsync(function() { - var oldUrl = $location.absUrl(); - - $location.$$parse(newUrl); - afterLocationChange(oldUrl); - }); - if (!$rootScope.$$phase) $rootScope.$digest(); - } - }); - - // update browser - var changeCounter = 0; - $rootScope.$watch(function $locationWatch() { - var oldUrl = $browser.url(); - var currentReplace = $location.$$replace; - - if (!changeCounter || oldUrl != $location.absUrl()) { - changeCounter++; - $rootScope.$evalAsync(function() { - if ($rootScope.$broadcast('$locationChangeStart', $location.absUrl(), oldUrl). - defaultPrevented) { - $location.$$parse(oldUrl); - } else { - $browser.url($location.absUrl(), currentReplace); - afterLocationChange(oldUrl); - } - }); - } - $location.$$replace = false; - - return changeCounter; - }); - - return $location; - - function afterLocationChange(oldUrl) { - $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl); - } -}]; -} - -/** - * @ngdoc object - * @name ng.$log - * @requires $window - * - * @description - * Simple service for logging. Default implementation writes the message - * into the browser's console (if present). - * - * The main purpose of this service is to simplify debugging and troubleshooting. - * - * @example - <example> - <file name="script.js"> - function LogCtrl($scope, $log) { - $scope.$log = $log; - $scope.message = 'Hello World!'; - } - </file> - <file name="index.html"> - <div ng-controller="LogCtrl"> - <p>Reload this page with open console, enter text and hit the log button...</p> - Message: - <input type="text" ng-model="message"/> - <button ng-click="$log.log(message)">log</button> - <button ng-click="$log.warn(message)">warn</button> - <button ng-click="$log.info(message)">info</button> - <button ng-click="$log.error(message)">error</button> - </div> - </file> - </example> - */ - -function $LogProvider(){ - this.$get = ['$window', function($window){ - return { - /** - * @ngdoc method - * @name ng.$log#log - * @methodOf ng.$log - * - * @description - * Write a log message - */ - log: consoleLog('log'), - - /** - * @ngdoc method - * @name ng.$log#warn - * @methodOf ng.$log - * - * @description - * Write a warning message - */ - warn: consoleLog('warn'), - - /** - * @ngdoc method - * @name ng.$log#info - * @methodOf ng.$log - * - * @description - * Write an information message - */ - info: consoleLog('info'), - - /** - * @ngdoc method - * @name ng.$log#error - * @methodOf ng.$log - * - * @description - * Write an error message - */ - error: consoleLog('error') - }; - - function formatError(arg) { - if (arg instanceof Error) { - if (arg.stack) { - arg = (arg.message && arg.stack.indexOf(arg.message) === -1) - ? 'Error: ' + arg.message + '\n' + arg.stack - : arg.stack; - } else if (arg.sourceURL) { - arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line; - } - } - return arg; - } - - function consoleLog(type) { - var console = $window.console || {}, - logFn = console[type] || console.log || noop; - - if (logFn.apply) { - return function() { - var args = []; - forEach(arguments, function(arg) { - args.push(formatError(arg)); - }); - return logFn.apply(console, args); - }; - } - - // we are IE which either doesn't have window.console => this is noop and we do nothing, - // or we are IE where console.log doesn't have apply so we log at least first 2 args - return function(arg1, arg2) { - logFn(arg1, arg2); - } - } - }]; -} - -var OPERATORS = { - 'null':function(){return null;}, - 'true':function(){return true;}, - 'false':function(){return false;}, - undefined:noop, - '+':function(self, locals, a,b){ - a=a(self, locals); b=b(self, locals); - if (isDefined(a)) { - if (isDefined(b)) { - return a + b; - } - return a; - } - return isDefined(b)?b:undefined;}, - '-':function(self, locals, a,b){a=a(self, locals); b=b(self, locals); return (isDefined(a)?a:0)-(isDefined(b)?b:0);}, - '*':function(self, locals, a,b){return a(self, locals)*b(self, locals);}, - '/':function(self, locals, a,b){return a(self, locals)/b(self, locals);}, - '%':function(self, locals, a,b){return a(self, locals)%b(self, locals);}, - '^':function(self, locals, a,b){return a(self, locals)^b(self, locals);}, - '=':noop, - '==':function(self, locals, a,b){return a(self, locals)==b(self, locals);}, - '!=':function(self, locals, a,b){return a(self, locals)!=b(self, locals);}, - '<':function(self, locals, a,b){return a(self, locals)<b(self, locals);}, - '>':function(self, locals, a,b){return a(self, locals)>b(self, locals);}, - '<=':function(self, locals, a,b){return a(self, locals)<=b(self, locals);}, - '>=':function(self, locals, a,b){return a(self, locals)>=b(self, locals);}, - '&&':function(self, locals, a,b){return a(self, locals)&&b(self, locals);}, - '||':function(self, locals, a,b){return a(self, locals)||b(self, locals);}, - '&':function(self, locals, a,b){return a(self, locals)&b(self, locals);}, -// '|':function(self, locals, a,b){return a|b;}, - '|':function(self, locals, a,b){return b(self, locals)(self, locals, a(self, locals));}, - '!':function(self, locals, a){return !a(self, locals);} -}; -var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'}; - -function lex(text, csp){ - var tokens = [], - token, - index = 0, - json = [], - ch, - lastCh = ':'; // can start regexp - - while (index < text.length) { - ch = text.charAt(index); - if (is('"\'')) { - readString(ch); - } else if (isNumber(ch) || is('.') && isNumber(peek())) { - readNumber(); - } else if (isIdent(ch)) { - readIdent(); - // identifiers can only be if the preceding char was a { or , - if (was('{,') && json[0]=='{' && - (token=tokens[tokens.length-1])) { - token.json = token.text.indexOf('.') == -1; - } - } else if (is('(){}[].,;:')) { - tokens.push({ - index:index, - text:ch, - json:(was(':[,') && is('{[')) || is('}]:,') - }); - if (is('{[')) json.unshift(ch); - if (is('}]')) json.shift(); - index++; - } else if (isWhitespace(ch)) { - index++; - continue; - } else { - var ch2 = ch + peek(), - fn = OPERATORS[ch], - fn2 = OPERATORS[ch2]; - if (fn2) { - tokens.push({index:index, text:ch2, fn:fn2}); - index += 2; - } else if (fn) { - tokens.push({index:index, text:ch, fn:fn, json: was('[,:') && is('+-')}); - index += 1; - } else { - throwError("Unexpected next character ", index, index+1); - } - } - lastCh = ch; - } - return tokens; - - function is(chars) { - return chars.indexOf(ch) != -1; - } - - function was(chars) { - return chars.indexOf(lastCh) != -1; - } - - function peek() { - return index + 1 < text.length ? text.charAt(index + 1) : false; - } - function isNumber(ch) { - return '0' <= ch && ch <= '9'; - } - function isWhitespace(ch) { - return ch == ' ' || ch == '\r' || ch == '\t' || - ch == '\n' || ch == '\v' || ch == '\u00A0'; // IE treats non-breaking space as \u00A0 - } - function isIdent(ch) { - return 'a' <= ch && ch <= 'z' || - 'A' <= ch && ch <= 'Z' || - '_' == ch || ch == '$'; - } - function isExpOperator(ch) { - return ch == '-' || ch == '+' || isNumber(ch); - } - - function throwError(error, start, end) { - end = end || index; - throw Error("Lexer Error: " + error + " at column" + - (isDefined(start) - ? "s " + start + "-" + index + " [" + text.substring(start, end) + "]" - : " " + end) + - " in expression [" + text + "]."); - } - - function readNumber() { - var number = ""; - var start = index; - while (index < text.length) { - var ch = lowercase(text.charAt(index)); - if (ch == '.' || isNumber(ch)) { - number += ch; - } else { - var peekCh = peek(); - if (ch == 'e' && isExpOperator(peekCh)) { - number += ch; - } else if (isExpOperator(ch) && - peekCh && isNumber(peekCh) && - number.charAt(number.length - 1) == 'e') { - number += ch; - } else if (isExpOperator(ch) && - (!peekCh || !isNumber(peekCh)) && - number.charAt(number.length - 1) == 'e') { - throwError('Invalid exponent'); - } else { - break; - } - } - index++; - } - number = 1 * number; - tokens.push({index:start, text:number, json:true, - fn:function() {return number;}}); - } - function readIdent() { - var ident = "", - start = index, - lastDot, peekIndex, methodName; - - while (index < text.length) { - var ch = text.charAt(index); - if (ch == '.' || isIdent(ch) || isNumber(ch)) { - if (ch == '.') lastDot = index; - ident += ch; - } else { - break; - } - index++; - } - - //check if this is not a method invocation and if it is back out to last dot - if (lastDot) { - peekIndex = index; - while(peekIndex < text.length) { - var ch = text.charAt(peekIndex); - if (ch == '(') { - methodName = ident.substr(lastDot - start + 1); - ident = ident.substr(0, lastDot - start); - index = peekIndex; - break; - } - if(isWhitespace(ch)) { - peekIndex++; - } else { - break; - } - } - } - - - var token = { - index:start, - text:ident - }; - - if (OPERATORS.hasOwnProperty(ident)) { - token.fn = token.json = OPERATORS[ident]; - } else { - var getter = getterFn(ident, csp); - token.fn = extend(function(self, locals) { - return (getter(self, locals)); - }, { - assign: function(self, value) { - return setter(self, ident, value); - } - }); - } - - tokens.push(token); - - if (methodName) { - tokens.push({ - index:lastDot, - text: '.', - json: false - }); - tokens.push({ - index: lastDot + 1, - text: methodName, - json: false - }); - } - } - - function readString(quote) { - var start = index; - index++; - var string = ""; - var rawString = quote; - var escape = false; - while (index < text.length) { - var ch = text.charAt(index); - rawString += ch; - if (escape) { - if (ch == 'u') { - var hex = text.substring(index + 1, index + 5); - if (!hex.match(/[\da-f]{4}/i)) - throwError( "Invalid unicode escape [\\u" + hex + "]"); - index += 4; - string += String.fromCharCode(parseInt(hex, 16)); - } else { - var rep = ESCAPE[ch]; - if (rep) { - string += rep; - } else { - string += ch; - } - } - escape = false; - } else if (ch == '\\') { - escape = true; - } else if (ch == quote) { - index++; - tokens.push({ - index:start, - text:rawString, - string:string, - json:true, - fn:function() { return string; } - }); - return; - } else { - string += ch; - } - index++; - } - throwError("Unterminated quote", start); - } -} - -///////////////////////////////////////// - -function parser(text, json, $filter, csp){ - var ZERO = valueFn(0), - value, - tokens = lex(text, csp), - assignment = _assignment, - functionCall = _functionCall, - fieldAccess = _fieldAccess, - objectIndex = _objectIndex, - filterChain = _filterChain; - - if(json){ - // The extra level of aliasing is here, just in case the lexer misses something, so that - // we prevent any accidental execution in JSON. - assignment = logicalOR; - functionCall = - fieldAccess = - objectIndex = - filterChain = - function() { throwError("is not valid json", {text:text, index:0}); }; - value = primary(); - } else { - value = statements(); - } - if (tokens.length !== 0) { - throwError("is an unexpected token", tokens[0]); - } - return value; - - /////////////////////////////////// - function throwError(msg, token) { - throw Error("Syntax Error: Token '" + token.text + - "' " + msg + " at column " + - (token.index + 1) + " of the expression [" + - text + "] starting at [" + text.substring(token.index) + "]."); - } - - function peekToken() { - if (tokens.length === 0) - throw Error("Unexpected end of expression: " + text); - return tokens[0]; - } - - function peek(e1, e2, e3, e4) { - if (tokens.length > 0) { - var token = tokens[0]; - var t = token.text; - if (t==e1 || t==e2 || t==e3 || t==e4 || - (!e1 && !e2 && !e3 && !e4)) { - return token; - } - } - return false; - } - - function expect(e1, e2, e3, e4){ - var token = peek(e1, e2, e3, e4); - if (token) { - if (json && !token.json) { - throwError("is not valid json", token); - } - tokens.shift(); - return token; - } - return false; - } - - function consume(e1){ - if (!expect(e1)) { - throwError("is unexpected, expecting [" + e1 + "]", peek()); - } - } - - function unaryFn(fn, right) { - return function(self, locals) { - return fn(self, locals, right); - }; - } - - function binaryFn(left, fn, right) { - return function(self, locals) { - return fn(self, locals, left, right); - }; - } - - function statements() { - var statements = []; - while(true) { - if (tokens.length > 0 && !peek('}', ')', ';', ']')) - statements.push(filterChain()); - if (!expect(';')) { - // optimize for the common case where there is only one statement. - // TODO(size): maybe we should not support multiple statements? - return statements.length == 1 - ? statements[0] - : function(self, locals){ - var value; - for ( var i = 0; i < statements.length; i++) { - var statement = statements[i]; - if (statement) - value = statement(self, locals); - } - return value; - }; - } - } - } - - function _filterChain() { - var left = expression(); - var token; - while(true) { - if ((token = expect('|'))) { - left = binaryFn(left, token.fn, filter()); - } else { - return left; - } - } - } - - function filter() { - var token = expect(); - var fn = $filter(token.text); - var argsFn = []; - while(true) { - if ((token = expect(':'))) { - argsFn.push(expression()); - } else { - var fnInvoke = function(self, locals, input){ - var args = [input]; - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](self, locals)); - } - return fn.apply(self, args); - }; - return function() { - return fnInvoke; - }; - } - } - } - - function expression() { - return assignment(); - } - - function _assignment() { - var left = logicalOR(); - var right; - var token; - if ((token = expect('='))) { - if (!left.assign) { - throwError("implies assignment but [" + - text.substring(0, token.index) + "] can not be assigned to", token); - } - right = logicalOR(); - return function(self, locals){ - return left.assign(self, right(self, locals), locals); - }; - } else { - return left; - } - } - - function logicalOR() { - var left = logicalAND(); - var token; - while(true) { - if ((token = expect('||'))) { - left = binaryFn(left, token.fn, logicalAND()); - } else { - return left; - } - } - } - - function logicalAND() { - var left = equality(); - var token; - if ((token = expect('&&'))) { - left = binaryFn(left, token.fn, logicalAND()); - } - return left; - } - - function equality() { - var left = relational(); - var token; - if ((token = expect('==','!='))) { - left = binaryFn(left, token.fn, equality()); - } - return left; - } - - function relational() { - var left = additive(); - var token; - if ((token = expect('<', '>', '<=', '>='))) { - left = binaryFn(left, token.fn, relational()); - } - return left; - } - - function additive() { - var left = multiplicative(); - var token; - while ((token = expect('+','-'))) { - left = binaryFn(left, token.fn, multiplicative()); - } - return left; - } - - function multiplicative() { - var left = unary(); - var token; - while ((token = expect('*','/','%'))) { - left = binaryFn(left, token.fn, unary()); - } - return left; - } - - function unary() { - var token; - if (expect('+')) { - return primary(); - } else if ((token = expect('-'))) { - return binaryFn(ZERO, token.fn, unary()); - } else if ((token = expect('!'))) { - return unaryFn(token.fn, unary()); - } else { - return primary(); - } - } - - - function primary() { - var primary; - if (expect('(')) { - primary = filterChain(); - consume(')'); - } else if (expect('[')) { - primary = arrayDeclaration(); - } else if (expect('{')) { - primary = object(); - } else { - var token = expect(); - primary = token.fn; - if (!primary) { - throwError("not a primary expression", token); - } - } - - var next, context; - while ((next = expect('(', '[', '.'))) { - if (next.text === '(') { - primary = functionCall(primary, context); - context = null; - } else if (next.text === '[') { - context = primary; - primary = objectIndex(primary); - } else if (next.text === '.') { - context = primary; - primary = fieldAccess(primary); - } else { - throwError("IMPOSSIBLE"); - } - } - return primary; - } - - function _fieldAccess(object) { - var field = expect().text; - var getter = getterFn(field, csp); - return extend( - function(self, locals) { - return getter(object(self, locals), locals); - }, - { - assign:function(self, value, locals) { - return setter(object(self, locals), field, value); - } - } - ); - } - - function _objectIndex(obj) { - var indexFn = expression(); - consume(']'); - return extend( - function(self, locals){ - var o = obj(self, locals), - i = indexFn(self, locals), - v, p; - - if (!o) return undefined; - v = o[i]; - if (v && v.then) { - p = v; - if (!('$$v' in v)) { - p.$$v = undefined; - p.then(function(val) { p.$$v = val; }); - } - v = v.$$v; - } - return v; - }, { - assign:function(self, value, locals){ - return obj(self, locals)[indexFn(self, locals)] = value; - } - }); - } - - function _functionCall(fn, contextGetter) { - var argsFn = []; - if (peekToken().text != ')') { - do { - argsFn.push(expression()); - } while (expect(',')); - } - consume(')'); - return function(self, locals){ - var args = [], - context = contextGetter ? contextGetter(self, locals) : self; - - for ( var i = 0; i < argsFn.length; i++) { - args.push(argsFn[i](self, locals)); - } - var fnPtr = fn(self, locals) || noop; - // IE stupidity! - return fnPtr.apply - ? fnPtr.apply(context, args) - : fnPtr(args[0], args[1], args[2], args[3], args[4]); - }; - } - - // This is used with json array declaration - function arrayDeclaration () { - var elementFns = []; - if (peekToken().text != ']') { - do { - elementFns.push(expression()); - } while (expect(',')); - } - consume(']'); - return function(self, locals){ - var array = []; - for ( var i = 0; i < elementFns.length; i++) { - array.push(elementFns[i](self, locals)); - } - return array; - }; - } - - function object () { - var keyValues = []; - if (peekToken().text != '}') { - do { - var token = expect(), - key = token.string || token.text; - consume(":"); - var value = expression(); - keyValues.push({key:key, value:value}); - } while (expect(',')); - } - consume('}'); - return function(self, locals){ - var object = {}; - for ( var i = 0; i < keyValues.length; i++) { - var keyValue = keyValues[i]; - var value = keyValue.value(self, locals); - object[keyValue.key] = value; - } - return object; - }; - } -} - -////////////////////////////////////////////////// -// Parser helper functions -////////////////////////////////////////////////// - -function setter(obj, path, setValue) { - var element = path.split('.'); - for (var i = 0; element.length > 1; i++) { - var key = element.shift(); - var propertyObj = obj[key]; - if (!propertyObj) { - propertyObj = {}; - obj[key] = propertyObj; - } - obj = propertyObj; - } - obj[element.shift()] = setValue; - return setValue; -} - -/** - * Return the value accesible from the object by path. Any undefined traversals are ignored - * @param {Object} obj starting object - * @param {string} path path to traverse - * @param {boolean=true} bindFnToScope - * @returns value as accesbile by path - */ -//TODO(misko): this function needs to be removed -function getter(obj, path, bindFnToScope) { - if (!path) return obj; - var keys = path.split('.'); - var key; - var lastInstance = obj; - var len = keys.length; - - for (var i = 0; i < len; i++) { - key = keys[i]; - if (obj) { - obj = (lastInstance = obj)[key]; - } - } - if (!bindFnToScope && isFunction(obj)) { - return bind(lastInstance, obj); - } - return obj; -} - -var getterFnCache = {}; - -/** - * Implementation of the "Black Hole" variant from: - * - http://jsperf.com/angularjs-parse-getter/4 - * - http://jsperf.com/path-evaluation-simplified/7 - */ -function cspSafeGetterFn(key0, key1, key2, key3, key4) { - return function(scope, locals) { - var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope, - promise; - - if (pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key0]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key1 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key1]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key2 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key2]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key3 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key3]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - if (!key4 || pathVal === null || pathVal === undefined) return pathVal; - - pathVal = pathVal[key4]; - if (pathVal && pathVal.then) { - if (!("$$v" in pathVal)) { - promise = pathVal; - promise.$$v = undefined; - promise.then(function(val) { promise.$$v = val; }); - } - pathVal = pathVal.$$v; - } - return pathVal; - }; -}; - -function getterFn(path, csp) { - if (getterFnCache.hasOwnProperty(path)) { - return getterFnCache[path]; - } - - var pathKeys = path.split('.'), - pathKeysLength = pathKeys.length, - fn; - - if (csp) { - fn = (pathKeysLength < 6) - ? cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4]) - : function(scope, locals) { - var i = 0, val - do { - val = cspSafeGetterFn( - pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++] - )(scope, locals); - - locals = undefined; // clear after first iteration - scope = val; - } while (i < pathKeysLength); - return val; - } - } else { - var code = 'var l, fn, p;\n'; - forEach(pathKeys, function(key, index) { - code += 'if(s === null || s === undefined) return s;\n' + - 'l=s;\n' + - 's='+ (index - // we simply dereference 's' on any .dot notation - ? 's' - // but if we are first then we check locals first, and if so read it first - : '((k&&k.hasOwnProperty("' + key + '"))?k:s)') + '["' + key + '"]' + ';\n' + - 'if (s && s.then) {\n' + - ' if (!("$$v" in s)) {\n' + - ' p=s;\n' + - ' p.$$v = undefined;\n' + - ' p.then(function(v) {p.$$v=v;});\n' + - '}\n' + - ' s=s.$$v\n' + - '}\n'; - }); - code += 'return s;'; - fn = Function('s', 'k', code); // s=scope, k=locals - fn.toString = function() { return code; }; - } - - return getterFnCache[path] = fn; -} - -/////////////////////////////////// - -/** - * @ngdoc function - * @name ng.$parse - * @function - * - * @description - * - * Converts Angular {@link guide/expression expression} into a function. - * - * <pre> - * var getter = $parse('user.name'); - * var setter = getter.assign; - * var context = {user:{name:'angular'}}; - * var locals = {user:{name:'local'}}; - * - * expect(getter(context)).toEqual('angular'); - * setter(context, 'newValue'); - * expect(context.user.name).toEqual('newValue'); - * expect(getter(context, locals)).toEqual('local'); - * </pre> - * - * - * @param {string} expression String expression to compile. - * @returns {function(context, locals)} a function which represents the compiled expression: - * - * * `context`: an object against which any expressions embedded in the strings are evaluated - * against (Topically a scope object). - * * `locals`: local variables context object, useful for overriding values in `context`. - * - * The return function also has an `assign` property, if the expression is assignable, which - * allows one to set values to expressions. - * - */ -function $ParseProvider() { - var cache = {}; - this.$get = ['$filter', '$sniffer', function($filter, $sniffer) { - return function(exp) { - switch(typeof exp) { - case 'string': - return cache.hasOwnProperty(exp) - ? cache[exp] - : cache[exp] = parser(exp, false, $filter, $sniffer.csp); - case 'function': - return exp; - default: - return noop; - } - }; - }]; -} - -/** - * @ngdoc service - * @name ng.$q - * @requires $rootScope - * - * @description - * A promise/deferred implementation inspired by [Kris Kowal's Q](https://github.com/kriskowal/q). - * - * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an - * interface for interacting with an object that represents the result of an action that is - * performed asynchronously, and may or may not be finished at any given point in time. - * - * From the perspective of dealing with error handling, deferred and promise apis are to - * asynchronous programing what `try`, `catch` and `throw` keywords are to synchronous programing. - * - * <pre> - * // for the purpose of this example let's assume that variables `$q` and `scope` are - * // available in the current lexical scope (they could have been injected or passed in). - * - * function asyncGreet(name) { - * var deferred = $q.defer(); - * - * setTimeout(function() { - * // since this fn executes async in a future turn of the event loop, we need to wrap - * // our code into an $apply call so that the model changes are properly observed. - * scope.$apply(function() { - * if (okToGreet(name)) { - * deferred.resolve('Hello, ' + name + '!'); - * } else { - * deferred.reject('Greeting ' + name + ' is not allowed.'); - * } - * }); - * }, 1000); - * - * return deferred.promise; - * } - * - * var promise = asyncGreet('Robin Hood'); - * promise.then(function(greeting) { - * alert('Success: ' + greeting); - * }, function(reason) { - * alert('Failed: ' + reason); - * }); - * </pre> - * - * At first it might not be obvious why this extra complexity is worth the trouble. The payoff - * comes in the way of - * [guarantees that promise and deferred apis make](https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md). - * - * Additionally the promise api allows for composition that is very hard to do with the - * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach. - * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the - * section on serial or parallel joining of promises. - * - * - * # The Deferred API - * - * A new instance of deferred is constructed by calling `$q.defer()`. - * - * The purpose of the deferred object is to expose the associated Promise instance as well as apis - * that can be used for signaling the successful or unsuccessful completion of the task. - * - * **Methods** - * - * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection - * constructed via `$q.reject`, the promise will be rejected instead. - * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to - * resolving it with a rejection constructed via `$q.reject`. - * - * **Properties** - * - * - promise – `{Promise}` – promise object associated with this deferred. - * - * - * # The Promise API - * - * A new promise instance is created when a deferred instance is created and can be retrieved by - * calling `deferred.promise`. - * - * The purpose of the promise object is to allow for interested parties to get access to the result - * of the deferred task when it completes. - * - * **Methods** - * - * - `then(successCallback, errorCallback)` – regardless of when the promise was or will be resolved - * or rejected calls one of the success or error callbacks asynchronously as soon as the result - * is available. The callbacks are called with a single argument the result or rejection reason. - * - * This method *returns a new promise* which is resolved or rejected via the return value of the - * `successCallback` or `errorCallback`. - * - * - * # Chaining promises - * - * Because calling `then` api of a promise returns a new derived promise, it is easily possible - * to create a chain of promises: - * - * <pre> - * promiseB = promiseA.then(function(result) { - * return result + 1; - * }); - * - * // promiseB will be resolved immediately after promiseA is resolved and it's value will be - * // the result of promiseA incremented by 1 - * </pre> - * - * It is possible to create chains of any length and since a promise can be resolved with another - * promise (which will defer its resolution further), it is possible to pause/defer resolution of - * the promises at any point in the chain. This makes it possible to implement powerful apis like - * $http's response interceptors. - * - * - * # Differences between Kris Kowal's Q and $q - * - * There are three main differences: - * - * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation - * mechanism in angular, which means faster propagation of resolution or rejection into your - * models and avoiding unnecessary browser repaints, which would result in flickering UI. - * - $q promises are recognized by the templating engine in angular, which means that in templates - * you can treat promises attached to a scope as if they were the resulting values. - * - Q has many more features that $q, but that comes at a cost of bytes. $q is tiny, but contains - * all the important functionality needed for common async tasks. - */ -function $QProvider() { - - this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) { - return qFactory(function(callback) { - $rootScope.$evalAsync(callback); - }, $exceptionHandler); - }]; -} - - -/** - * Constructs a promise manager. - * - * @param {function(function)} nextTick Function for executing functions in the next turn. - * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for - * debugging purposes. - * @returns {object} Promise manager. - */ -function qFactory(nextTick, exceptionHandler) { - - /** - * @ngdoc - * @name ng.$q#defer - * @methodOf ng.$q - * @description - * Creates a `Deferred` object which represents a task which will finish in the future. - * - * @returns {Deferred} Returns a new instance of deferred. - */ - var defer = function() { - var pending = [], - value, deferred; - - deferred = { - - resolve: function(val) { - if (pending) { - var callbacks = pending; - pending = undefined; - value = ref(val); - - if (callbacks.length) { - nextTick(function() { - var callback; - for (var i = 0, ii = callbacks.length; i < ii; i++) { - callback = callbacks[i]; - value.then(callback[0], callback[1]); - } - }); - } - } - }, - - - reject: function(reason) { - deferred.resolve(reject(reason)); - }, - - - promise: { - then: function(callback, errback) { - var result = defer(); - - var wrappedCallback = function(value) { - try { - result.resolve((callback || defaultCallback)(value)); - } catch(e) { - exceptionHandler(e); - result.reject(e); - } - }; - - var wrappedErrback = function(reason) { - try { - result.resolve((errback || defaultErrback)(reason)); - } catch(e) { - exceptionHandler(e); - result.reject(e); - } - }; - - if (pending) { - pending.push([wrappedCallback, wrappedErrback]); - } else { - value.then(wrappedCallback, wrappedErrback); - } - - return result.promise; - } - } - }; - - return deferred; - }; - - - var ref = function(value) { - if (value && value.then) return value; - return { - then: function(callback) { - var result = defer(); - nextTick(function() { - result.resolve(callback(value)); - }); - return result.promise; - } - }; - }; - - - /** - * @ngdoc - * @name ng.$q#reject - * @methodOf ng.$q - * @description - * Creates a promise that is resolved as rejected with the specified `reason`. This api should be - * used to forward rejection in a chain of promises. If you are dealing with the last promise in - * a promise chain, you don't need to worry about it. - * - * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of - * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via - * a promise error callback and you want to forward the error to the promise derived from the - * current promise, you have to "rethrow" the error by returning a rejection constructed via - * `reject`. - * - * <pre> - * promiseB = promiseA.then(function(result) { - * // success: do something and resolve promiseB - * // with the old or a new result - * return result; - * }, function(reason) { - * // error: handle the error if possible and - * // resolve promiseB with newPromiseOrValue, - * // otherwise forward the rejection to promiseB - * if (canHandle(reason)) { - * // handle the error and recover - * return newPromiseOrValue; - * } - * return $q.reject(reason); - * }); - * </pre> - * - * @param {*} reason Constant, message, exception or an object representing the rejection reason. - * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`. - */ - var reject = function(reason) { - return { - then: function(callback, errback) { - var result = defer(); - nextTick(function() { - result.resolve((errback || defaultErrback)(reason)); - }); - return result.promise; - } - }; - }; - - - /** - * @ngdoc - * @name ng.$q#when - * @methodOf ng.$q - * @description - * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise. - * This is useful when you are dealing with on object that might or might not be a promise, or if - * the promise comes from a source that can't be trusted. - * - * @param {*} value Value or a promise - * @returns {Promise} Returns a single promise that will be resolved with an array of values, - * each value coresponding to the promise at the same index in the `promises` array. If any of - * the promises is resolved with a rejection, this resulting promise will be resolved with the - * same rejection. - */ - var when = function(value, callback, errback) { - var result = defer(), - done; - - var wrappedCallback = function(value) { - try { - return (callback || defaultCallback)(value); - } catch (e) { - exceptionHandler(e); - return reject(e); - } - }; - - var wrappedErrback = function(reason) { - try { - return (errback || defaultErrback)(reason); - } catch (e) { - exceptionHandler(e); - return reject(e); - } - }; - - nextTick(function() { - ref(value).then(function(value) { - if (done) return; - done = true; - result.resolve(ref(value).then(wrappedCallback, wrappedErrback)); - }, function(reason) { - if (done) return; - done = true; - result.resolve(wrappedErrback(reason)); - }); - }); - - return result.promise; - }; - - - function defaultCallback(value) { - return value; - } - - - function defaultErrback(reason) { - return reject(reason); - } - - - /** - * @ngdoc - * @name ng.$q#all - * @methodOf ng.$q - * @description - * Combines multiple promises into a single promise that is resolved when all of the input - * promises are resolved. - * - * @param {Array.<Promise>} promises An array of promises. - * @returns {Promise} Returns a single promise that will be resolved with an array of values, - * each value coresponding to the promise at the same index in the `promises` array. If any of - * the promises is resolved with a rejection, this resulting promise will be resolved with the - * same rejection. - */ - function all(promises) { - var deferred = defer(), - counter = promises.length, - results = []; - - if (counter) { - forEach(promises, function(promise, index) { - ref(promise).then(function(value) { - if (index in results) return; - results[index] = value; - if (!(--counter)) deferred.resolve(results); - }, function(reason) { - if (index in results) return; - deferred.reject(reason); - }); - }); - } else { - deferred.resolve(results); - } - - return deferred.promise; - } - - return { - defer: defer, - reject: reject, - when: when, - all: all - }; -} - -/** - * @ngdoc object - * @name ng.$routeProvider - * @function - * - * @description - * - * Used for configuring routes. See {@link ng.$route $route} for an example. - */ -function $RouteProvider(){ - var routes = {}; - - /** - * @ngdoc method - * @name ng.$routeProvider#when - * @methodOf ng.$routeProvider - * - * @param {string} path Route path (matched against `$location.path`). If `$location.path` - * contains redundant trailing slash or is missing one, the route will still match and the - * `$location.path` will be updated to add or drop the trailing slash to exacly match the - * route definition. - * @param {Object} route Mapping information to be assigned to `$route.current` on route - * match. - * - * Object properties: - * - * - `controller` – `{(string|function()=}` – Controller fn that should be associated with newly - * created scope or the name of a {@link angular.Module#controller registered controller} - * if passed as a string. - * - `template` – `{string=}` – html template as a string that should be used by - * {@link ng.directive:ngView ngView} or - * {@link ng.directive:ngInclude ngInclude} directives. - * this property takes precedence over `templateUrl`. - * - `templateUrl` – `{string=}` – path to an html template that should be used by - * {@link ng.directive:ngView ngView}. - * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should - * be injected into the controller. If any of these dependencies are promises, they will be - * resolved and converted to a value before the controller is instantiated and the - * `$routeChangeSuccess` event is fired. The map object is: - * - * - `key` – `{string}`: a name of a dependency to be injected into the controller. - * - `factory` - `{string|function}`: If `string` then it is an alias for a service. - * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected} - * and the return value is treated as the dependency. If the result is a promise, it is resolved - * before its value is injected into the controller. - * - * - `redirectTo` – {(string|function())=} – value to update - * {@link ng.$location $location} path with and trigger route redirection. - * - * If `redirectTo` is a function, it will be called with the following parameters: - * - * - `{Object.<string>}` - route parameters extracted from the current - * `$location.path()` by applying the current route templateUrl. - * - `{string}` - current `$location.path()` - * - `{Object}` - current `$location.search()` - * - * The custom `redirectTo` function is expected to return a string which will be used - * to update `$location.path()` and `$location.search()`. - * - * - `[reloadOnSearch=true]` - {boolean=} - reload route when only $location.search() - * changes. - * - * If the option is set to `false` and url in the browser changes, then - * `$routeUpdate` event is broadcasted on the root scope. - * - * @returns {Object} self - * - * @description - * Adds a new route definition to the `$route` service. - */ - this.when = function(path, route) { - routes[path] = extend({reloadOnSearch: true}, route); - - // create redirection for trailing slashes - if (path) { - var redirectPath = (path[path.length-1] == '/') - ? path.substr(0, path.length-1) - : path +'/'; - - routes[redirectPath] = {redirectTo: path}; - } - - return this; - }; - - /** - * @ngdoc method - * @name ng.$routeProvider#otherwise - * @methodOf ng.$routeProvider - * - * @description - * Sets route definition that will be used on route change when no other route definition - * is matched. - * - * @param {Object} params Mapping information to be assigned to `$route.current`. - * @returns {Object} self - */ - this.otherwise = function(params) { - this.when(null, params); - return this; - }; - - - this.$get = ['$rootScope', '$location', '$routeParams', '$q', '$injector', '$http', '$templateCache', - function( $rootScope, $location, $routeParams, $q, $injector, $http, $templateCache) { - - /** - * @ngdoc object - * @name ng.$route - * @requires $location - * @requires $routeParams - * - * @property {Object} current Reference to the current route definition. - * The route definition contains: - * - * - `controller`: The controller constructor as define in route definition. - * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for - * controller instantiation. The `locals` contain - * the resolved values of the `resolve` map. Additionally the `locals` also contain: - * - * - `$scope` - The current route scope. - * - `$template` - The current route template HTML. - * - * @property {Array.<Object>} routes Array of all configured routes. - * - * @description - * Is used for deep-linking URLs to controllers and views (HTML partials). - * It watches `$location.url()` and tries to map the path to an existing route definition. - * - * You can define routes through {@link ng.$routeProvider $routeProvider}'s API. - * - * The `$route` service is typically used in conjunction with {@link ng.directive:ngView ngView} - * directive and the {@link ng.$routeParams $routeParams} service. - * - * @example - This example shows how changing the URL hash causes the `$route` to match a route against the - URL, and the `ngView` pulls in the partial. - - Note that this example is using {@link ng.directive:script inlined templates} - to get it working on jsfiddle as well. - - <example module="ngView"> - <file name="index.html"> - <div ng-controller="MainCntl"> - Choose: - <a href="Book/Moby">Moby</a> | - <a href="Book/Moby/ch/1">Moby: Ch1</a> | - <a href="Book/Gatsby">Gatsby</a> | - <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | - <a href="Book/Scarlet">Scarlet Letter</a><br/> - - <div ng-view></div> - <hr /> - - <pre>$location.path() = {{$location.path()}}</pre> - <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre> - <pre>$route.current.params = {{$route.current.params}}</pre> - <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre> - <pre>$routeParams = {{$routeParams}}</pre> - </div> - </file> - - <file name="book.html"> - controller: {{name}}<br /> - Book Id: {{params.bookId}}<br /> - </file> - - <file name="chapter.html"> - controller: {{name}}<br /> - Book Id: {{params.bookId}}<br /> - Chapter Id: {{params.chapterId}} - </file> - - <file name="script.js"> - angular.module('ngView', [], function($routeProvider, $locationProvider) { - $routeProvider.when('/Book/:bookId', { - templateUrl: 'book.html', - controller: BookCntl, - resolve: { - // I will cause a 1 second delay - delay: function($q, $timeout) { - var delay = $q.defer(); - $timeout(delay.resolve, 1000); - return delay.promise; - } - } - }); - $routeProvider.when('/Book/:bookId/ch/:chapterId', { - templateUrl: 'chapter.html', - controller: ChapterCntl - }); - - // configure html5 to get links working on jsfiddle - $locationProvider.html5Mode(true); - }); - - function MainCntl($scope, $route, $routeParams, $location) { - $scope.$route = $route; - $scope.$location = $location; - $scope.$routeParams = $routeParams; - } - - function BookCntl($scope, $routeParams) { - $scope.name = "BookCntl"; - $scope.params = $routeParams; - } - - function ChapterCntl($scope, $routeParams) { - $scope.name = "ChapterCntl"; - $scope.params = $routeParams; - } - </file> - - <file name="scenario.js"> - it('should load and compile correct template', function() { - element('a:contains("Moby: Ch1")').click(); - var content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: ChapterCntl/); - expect(content).toMatch(/Book Id\: Moby/); - expect(content).toMatch(/Chapter Id\: 1/); - - element('a:contains("Scarlet")').click(); - sleep(2); // promises are not part of scenario waiting - content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: BookCntl/); - expect(content).toMatch(/Book Id\: Scarlet/); - }); - </file> - </example> - */ - - /** - * @ngdoc event - * @name ng.$route#$routeChangeStart - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * Broadcasted before a route change. At this point the route services starts - * resolving all of the dependencies needed for the route change to occurs. - * Typically this involves fetching the view template as well as any dependencies - * defined in `resolve` route property. Once all of the dependencies are resolved - * `$routeChangeSuccess` is fired. - * - * @param {Route} next Future route information. - * @param {Route} current Current route information. - */ - - /** - * @ngdoc event - * @name ng.$route#$routeChangeSuccess - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * Broadcasted after a route dependencies are resolved. - * {@link ng.directive:ngView ngView} listens for the directive - * to instantiate the controller and render the view. - * - * @param {Route} current Current route information. - * @param {Route} previous Previous route information. - */ - - /** - * @ngdoc event - * @name ng.$route#$routeChangeError - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * Broadcasted if any of the resolve promises are rejected. - * - * @param {Route} current Current route information. - * @param {Route} previous Previous route information. - * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise. - */ - - /** - * @ngdoc event - * @name ng.$route#$routeUpdate - * @eventOf ng.$route - * @eventType broadcast on root scope - * @description - * - * The `reloadOnSearch` property has been set to false, and we are reusing the same - * instance of the Controller. - */ - - var matcher = switchRouteMatcher, - forceReload = false, - $route = { - routes: routes, - - /** - * @ngdoc method - * @name ng.$route#reload - * @methodOf ng.$route - * - * @description - * Causes `$route` service to reload the current route even if - * {@link ng.$location $location} hasn't changed. - * - * As a result of that, {@link ng.directive:ngView ngView} - * creates new scope, reinstantiates the controller. - */ - reload: function() { - forceReload = true; - $rootScope.$evalAsync(updateRoute); - } - }; - - $rootScope.$on('$locationChangeSuccess', updateRoute); - - return $route; - - ///////////////////////////////////////////////////// - - function switchRouteMatcher(on, when) { - // TODO(i): this code is convoluted and inefficient, we should construct the route matching - // regex only once and then reuse it - var regex = '^' + when.replace(/([\.\\\(\)\^\$])/g, "\\$1") + '$', - params = [], - dst = {}; - forEach(when.split(/\W/), function(param) { - if (param) { - var paramRegExp = new RegExp(":" + param + "([\\W])"); - if (regex.match(paramRegExp)) { - regex = regex.replace(paramRegExp, "([^\\/]*)$1"); - params.push(param); - } - } - }); - var match = on.match(new RegExp(regex)); - if (match) { - forEach(params, function(name, index) { - dst[name] = match[index + 1]; - }); - } - return match ? dst : null; - } - - function updateRoute() { - var next = parseRoute(), - last = $route.current; - - if (next && last && next.$route === last.$route - && equals(next.pathParams, last.pathParams) && !next.reloadOnSearch && !forceReload) { - last.params = next.params; - copy(last.params, $routeParams); - $rootScope.$broadcast('$routeUpdate', last); - } else if (next || last) { - forceReload = false; - $rootScope.$broadcast('$routeChangeStart', next, last); - $route.current = next; - if (next) { - if (next.redirectTo) { - if (isString(next.redirectTo)) { - $location.path(interpolate(next.redirectTo, next.params)).search(next.params) - .replace(); - } else { - $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search())) - .replace(); - } - } - } - - $q.when(next). - then(function() { - if (next) { - var keys = [], - values = [], - template; - - forEach(next.resolve || {}, function(value, key) { - keys.push(key); - values.push(isString(value) ? $injector.get(value) : $injector.invoke(value)); - }); - if (isDefined(template = next.template)) { - } else if (isDefined(template = next.templateUrl)) { - template = $http.get(template, {cache: $templateCache}). - then(function(response) { return response.data; }); - } - if (isDefined(template)) { - keys.push('$template'); - values.push(template); - } - return $q.all(values).then(function(values) { - var locals = {}; - forEach(values, function(value, index) { - locals[keys[index]] = value; - }); - return locals; - }); - } - }). - // after route change - then(function(locals) { - if (next == $route.current) { - if (next) { - next.locals = locals; - copy(next.params, $routeParams); - } - $rootScope.$broadcast('$routeChangeSuccess', next, last); - } - }, function(error) { - if (next == $route.current) { - $rootScope.$broadcast('$routeChangeError', next, last, error); - } - }); - } - } - - - /** - * @returns the current active route, by matching it against the URL - */ - function parseRoute() { - // Match a route - var params, match; - forEach(routes, function(route, path) { - if (!match && (params = matcher($location.path(), path))) { - match = inherit(route, { - params: extend({}, $location.search(), params), - pathParams: params}); - match.$route = route; - } - }); - // No route matched; fallback to "otherwise" route - return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}}); - } - - /** - * @returns interpolation of the redirect path with the parametrs - */ - function interpolate(string, params) { - var result = []; - forEach((string||'').split(':'), function(segment, i) { - if (i == 0) { - result.push(segment); - } else { - var segmentMatch = segment.match(/(\w+)(.*)/); - var key = segmentMatch[1]; - result.push(params[key]); - result.push(segmentMatch[2] || ''); - delete params[key]; - } - }); - return result.join(''); - } - }]; -} - -/** - * @ngdoc object - * @name ng.$routeParams - * @requires $route - * - * @description - * Current set of route parameters. The route parameters are a combination of the - * {@link ng.$location $location} `search()`, and `path()`. The `path` parameters - * are extracted when the {@link ng.$route $route} path is matched. - * - * In case of parameter name collision, `path` params take precedence over `search` params. - * - * The service guarantees that the identity of the `$routeParams` object will remain unchanged - * (but its properties will likely change) even when a route change occurs. - * - * @example - * <pre> - * // Given: - * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby - * // Route: /Chapter/:chapterId/Section/:sectionId - * // - * // Then - * $routeParams ==> {chapterId:1, sectionId:2, search:'moby'} - * </pre> - */ -function $RouteParamsProvider() { - this.$get = valueFn({}); -} - -/** - * DESIGN NOTES - * - * The design decisions behind the scope ware heavily favored for speed and memory consumption. - * - * The typical use of scope is to watch the expressions, which most of the time return the same - * value as last time so we optimize the operation. - * - * Closures construction is expensive from speed as well as memory: - * - no closures, instead ups prototypical inheritance for API - * - Internal state needs to be stored on scope directly, which means that private state is - * exposed as $$____ properties - * - * Loop operations are optimized by using while(count--) { ... } - * - this means that in order to keep the same order of execution as addition we have to add - * items to the array at the begging (shift) instead of at the end (push) - * - * Child scopes are created and removed often - * - Using array would be slow since inserts in meddle are expensive so we use linked list - * - * There are few watches then a lot of observers. This is why you don't want the observer to be - * implemented in the same way as watch. Watch requires return of initialization function which - * are expensive to construct. - */ - - -/** - * @ngdoc object - * @name ng.$rootScopeProvider - * @description - * - * Provider for the $rootScope service. - */ - -/** - * @ngdoc function - * @name ng.$rootScopeProvider#digestTtl - * @methodOf ng.$rootScopeProvider - * @description - * - * Sets the number of digest iteration the scope should attempt to execute before giving up and - * assuming that the model is unstable. - * - * The current default is 10 iterations. - * - * @param {number} limit The number of digest iterations. - */ - - -/** - * @ngdoc object - * @name ng.$rootScope - * @description - * - * Every application has a single root {@link ng.$rootScope.Scope scope}. - * All other scopes are child scopes of the root scope. Scopes provide mechanism for watching the model and provide - * event processing life-cycle. See {@link guide/scope developer guide on scopes}. - */ -function $RootScopeProvider(){ - var TTL = 10; - - this.digestTtl = function(value) { - if (arguments.length) { - TTL = value; - } - return TTL; - }; - - this.$get = ['$injector', '$exceptionHandler', '$parse', - function( $injector, $exceptionHandler, $parse) { - - /** - * @ngdoc function - * @name ng.$rootScope.Scope - * - * @description - * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the - * {@link AUTO.$injector $injector}. Child scopes are created using the - * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when - * compiled HTML template is executed.) - * - * Here is a simple scope snippet to show how you can interact with the scope. - * <pre> - angular.injector(['ng']).invoke(function($rootScope) { - var scope = $rootScope.$new(); - scope.salutation = 'Hello'; - scope.name = 'World'; - - expect(scope.greeting).toEqual(undefined); - - scope.$watch('name', function() { - this.greeting = this.salutation + ' ' + this.name + '!'; - }); // initialize the watch - - expect(scope.greeting).toEqual(undefined); - scope.name = 'Misko'; - // still old value, since watches have not been called yet - expect(scope.greeting).toEqual(undefined); - - scope.$digest(); // fire all the watches - expect(scope.greeting).toEqual('Hello Misko!'); - }); - * </pre> - * - * # Inheritance - * A scope can inherit from a parent scope, as in this example: - * <pre> - var parent = $rootScope; - var child = parent.$new(); - - parent.salutation = "Hello"; - child.name = "World"; - expect(child.salutation).toEqual('Hello'); - - child.salutation = "Welcome"; - expect(child.salutation).toEqual('Welcome'); - expect(parent.salutation).toEqual('Hello'); - * </pre> - * - * - * @param {Object.<string, function()>=} providers Map of service factory which need to be provided - * for the current scope. Defaults to {@link ng}. - * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should - * append/override services provided by `providers`. This is handy when unit-testing and having - * the need to override a default service. - * @returns {Object} Newly created scope. - * - */ - function Scope() { - this.$id = nextUid(); - this.$$phase = this.$parent = this.$$watchers = - this.$$nextSibling = this.$$prevSibling = - this.$$childHead = this.$$childTail = null; - this['this'] = this.$root = this; - this.$$asyncQueue = []; - this.$$listeners = {}; - } - - /** - * @ngdoc property - * @name ng.$rootScope.Scope#$id - * @propertyOf ng.$rootScope.Scope - * @returns {number} Unique scope ID (monotonically increasing alphanumeric sequence) useful for - * debugging. - */ - - - Scope.prototype = { - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$new - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Creates a new child {@link ng.$rootScope.Scope scope}. - * - * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} and - * {@link ng.$rootScope.Scope#$digest $digest()} events. The scope can be removed from the scope - * hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}. - * - * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is desired for - * the scope and its child scopes to be permanently detached from the parent and thus stop - * participating in model change detection and listener notification by invoking. - * - * @param {boolean} isolate if true then the scope does not prototypically inherit from the - * parent scope. The scope is isolated, as it can not see parent scope properties. - * When creating widgets it is useful for the widget to not accidentally read parent - * state. - * - * @returns {Object} The newly created child scope. - * - */ - $new: function(isolate) { - var Child, - child; - - if (isFunction(isolate)) { - // TODO: remove at some point - throw Error('API-CHANGE: Use $controller to instantiate controllers.'); - } - if (isolate) { - child = new Scope(); - child.$root = this.$root; - } else { - Child = function() {}; // should be anonymous; This is so that when the minifier munges - // the name it does not become random set of chars. These will then show up as class - // name in the debugger. - Child.prototype = this; - child = new Child(); - child.$id = nextUid(); - } - child['this'] = child; - child.$$listeners = {}; - child.$parent = this; - child.$$asyncQueue = []; - child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null; - child.$$prevSibling = this.$$childTail; - if (this.$$childHead) { - this.$$childTail.$$nextSibling = child; - this.$$childTail = child; - } else { - this.$$childHead = this.$$childTail = child; - } - return child; - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$watch - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Registers a `listener` callback to be executed whenever the `watchExpression` changes. - * - * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest $digest()} and - * should return the value which will be watched. (Since {@link ng.$rootScope.Scope#$digest $digest()} - * reruns when it detects changes the `watchExpression` can execute multiple times per - * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.) - * - The `listener` is called only when the value from the current `watchExpression` and the - * previous call to `watchExpression` are not equal (with the exception of the initial run, - * see below). The inequality is determined according to - * {@link angular.equals} function. To save the value of the object for later comparison, the - * {@link angular.copy} function is used. It also means that watching complex options will - * have adverse memory and performance implications. - * - The watch `listener` may change the model, which may trigger other `listener`s to fire. This - * is achieved by rerunning the watchers until no changes are detected. The rerun iteration - * limit is 10 to prevent an infinite loop deadlock. - * - * - * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called, - * you can register a `watchExpression` function with no `listener`. (Since `watchExpression` - * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a change is - * detected, be prepared for multiple calls to your listener.) - * - * After a watcher is registered with the scope, the `listener` fn is called asynchronously - * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the - * watcher. In rare cases, this is undesirable because the listener is called when the result - * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you - * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the - * listener was called due to initialization. - * - * - * # Example - * <pre> - // let's assume that scope was dependency injected as the $rootScope - var scope = $rootScope; - scope.name = 'misko'; - scope.counter = 0; - - expect(scope.counter).toEqual(0); - scope.$watch('name', function(newValue, oldValue) { counter = counter + 1; }); - expect(scope.counter).toEqual(0); - - scope.$digest(); - // no variable change - expect(scope.counter).toEqual(0); - - scope.name = 'adam'; - scope.$digest(); - expect(scope.counter).toEqual(1); - * </pre> - * - * - * - * @param {(function()|string)} watchExpression Expression that is evaluated on each - * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers a - * call to the `listener`. - * - * - `string`: Evaluated as {@link guide/expression expression} - * - `function(scope)`: called with current `scope` as a parameter. - * @param {(function()|string)=} listener Callback called whenever the return value of - * the `watchExpression` changes. - * - * - `string`: Evaluated as {@link guide/expression expression} - * - `function(newValue, oldValue, scope)`: called with current and previous values as parameters. - * - * @param {boolean=} objectEquality Compare object for equality rather than for reference. - * @returns {function()} Returns a deregistration function for this listener. - */ - $watch: function(watchExp, listener, objectEquality) { - var scope = this, - get = compileToFn(watchExp, 'watch'), - array = scope.$$watchers, - watcher = { - fn: listener, - last: initWatchVal, - get: get, - exp: watchExp, - eq: !!objectEquality - }; - - // in the case user pass string, we need to compile it, do we really need this ? - if (!isFunction(listener)) { - var listenFn = compileToFn(listener || noop, 'listener'); - watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);}; - } - - if (!array) { - array = scope.$$watchers = []; - } - // we use unshift since we use a while loop in $digest for speed. - // the while loop reads in reverse order. - array.unshift(watcher); - - return function() { - arrayRemove(array, watcher); - }; - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$digest - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Process all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and its children. - * Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change the model, the - * `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers} until no more listeners are - * firing. This means that it is possible to get into an infinite loop. This function will throw - * `'Maximum iteration limit exceeded.'` if the number of iterations exceeds 10. - * - * Usually you don't call `$digest()` directly in - * {@link ng.directive:ngController controllers} or in - * {@link ng.$compileProvider#directive directives}. - * Instead a call to {@link ng.$rootScope.Scope#$apply $apply()} (typically from within a - * {@link ng.$compileProvider#directive directives}) will force a `$digest()`. - * - * If you want to be notified whenever `$digest()` is called, - * you can register a `watchExpression` function with {@link ng.$rootScope.Scope#$watch $watch()} - * with no `listener`. - * - * You may have a need to call `$digest()` from within unit-tests, to simulate the scope - * life-cycle. - * - * # Example - * <pre> - var scope = ...; - scope.name = 'misko'; - scope.counter = 0; - - expect(scope.counter).toEqual(0); - scope.$watch('name', function(newValue, oldValue) { - counter = counter + 1; - }); - expect(scope.counter).toEqual(0); - - scope.$digest(); - // no variable change - expect(scope.counter).toEqual(0); - - scope.name = 'adam'; - scope.$digest(); - expect(scope.counter).toEqual(1); - * </pre> - * - */ - $digest: function() { - var watch, value, last, - watchers, - asyncQueue, - length, - dirty, ttl = TTL, - next, current, target = this, - watchLog = [], - logIdx, logMsg; - - beginPhase('$digest'); - - do { - dirty = false; - current = target; - do { - asyncQueue = current.$$asyncQueue; - while(asyncQueue.length) { - try { - current.$eval(asyncQueue.shift()); - } catch (e) { - $exceptionHandler(e); - } - } - if ((watchers = current.$$watchers)) { - // process our watches - length = watchers.length; - while (length--) { - try { - watch = watchers[length]; - // Most common watches are on primitives, in which case we can short - // circuit it with === operator, only when === fails do we use .equals - if ((value = watch.get(current)) !== (last = watch.last) && - !(watch.eq - ? equals(value, last) - : (typeof value == 'number' && typeof last == 'number' - && isNaN(value) && isNaN(last)))) { - dirty = true; - watch.last = watch.eq ? copy(value) : value; - watch.fn(value, ((last === initWatchVal) ? value : last), current); - if (ttl < 5) { - logIdx = 4 - ttl; - if (!watchLog[logIdx]) watchLog[logIdx] = []; - logMsg = (isFunction(watch.exp)) - ? 'fn: ' + (watch.exp.name || watch.exp.toString()) - : watch.exp; - logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last); - watchLog[logIdx].push(logMsg); - } - } - } catch (e) { - $exceptionHandler(e); - } - } - } - - // Insanity Warning: scope depth-first traversal - // yes, this code is a bit crazy, but it works and we have tests to prove it! - // this piece should be kept in sync with the traversal in $broadcast - if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { - while(current !== target && !(next = current.$$nextSibling)) { - current = current.$parent; - } - } - } while ((current = next)); - - if(dirty && !(ttl--)) { - clearPhase(); - throw Error(TTL + ' $digest() iterations reached. Aborting!\n' + - 'Watchers fired in the last 5 iterations: ' + toJson(watchLog)); - } - } while (dirty || asyncQueue.length); - - clearPhase(); - }, - - - /** - * @ngdoc event - * @name ng.$rootScope.Scope#$destroy - * @eventOf ng.$rootScope.Scope - * @eventType broadcast on scope being destroyed - * - * @description - * Broadcasted when a scope and its children are being destroyed. - */ - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$destroy - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Removes the current scope (and all of its children) from the parent scope. Removal implies - * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer - * propagate to the current scope and its children. Removal also implies that the current - * scope is eligible for garbage collection. - * - * The `$destroy()` is usually used by directives such as - * {@link ng.directive:ngRepeat ngRepeat} for managing the - * unrolling of the loop. - * - * Just before a scope is destroyed a `$destroy` event is broadcasted on this scope. - * Application code can register a `$destroy` event handler that will give it chance to - * perform any necessary cleanup. - */ - $destroy: function() { - if ($rootScope == this) return; // we can't remove the root node; - var parent = this.$parent; - - this.$broadcast('$destroy'); - - if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling; - if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling; - if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling; - if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling; - - // This is bogus code that works around Chrome's GC leak - // see: https://github.com/angular/angular.js/issues/1313#issuecomment-10378451 - this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead = - this.$$childTail = null; - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$eval - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Executes the `expression` on the current scope returning the result. Any exceptions in the - * expression are propagated (uncaught). This is useful when evaluating Angular expressions. - * - * # Example - * <pre> - var scope = ng.$rootScope.Scope(); - scope.a = 1; - scope.b = 2; - - expect(scope.$eval('a+b')).toEqual(3); - expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3); - * </pre> - * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - * @returns {*} The result of evaluating the expression. - */ - $eval: function(expr, locals) { - return $parse(expr)(this, locals); - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$evalAsync - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Executes the expression on the current scope at a later point in time. - * - * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only that: - * - * - it will execute in the current script execution context (before any DOM rendering). - * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after - * `expression` execution. - * - * Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {(string|function())=} expression An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with the current `scope` parameter. - * - */ - $evalAsync: function(expr) { - this.$$asyncQueue.push(expr); - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$apply - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * `$apply()` is used to execute an expression in angular from outside of the angular framework. - * (For example from browser DOM events, setTimeout, XHR or third party libraries). - * Because we are calling into the angular framework we need to perform proper scope life-cycle - * of {@link ng.$exceptionHandler exception handling}, - * {@link ng.$rootScope.Scope#$digest executing watches}. - * - * ## Life cycle - * - * # Pseudo-Code of `$apply()` - * <pre> - function $apply(expr) { - try { - return $eval(expr); - } catch (e) { - $exceptionHandler(e); - } finally { - $root.$digest(); - } - } - * </pre> - * - * - * Scope's `$apply()` method transitions through the following stages: - * - * 1. The {@link guide/expression expression} is executed using the - * {@link ng.$rootScope.Scope#$eval $eval()} method. - * 2. Any exceptions from the execution of the expression are forwarded to the - * {@link ng.$exceptionHandler $exceptionHandler} service. - * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the expression - * was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method. - * - * - * @param {(string|function())=} exp An angular expression to be executed. - * - * - `string`: execute using the rules as defined in {@link guide/expression expression}. - * - `function(scope)`: execute the function with current `scope` parameter. - * - * @returns {*} The result of evaluating the expression. - */ - $apply: function(expr) { - try { - beginPhase('$apply'); - return this.$eval(expr); - } catch (e) { - $exceptionHandler(e); - } finally { - clearPhase(); - try { - $rootScope.$digest(); - } catch (e) { - $exceptionHandler(e); - throw e; - } - } - }, - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$on - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for discussion of - * event life cycle. - * - * @param {string} name Event name to listen on. - * @param {function(event)} listener Function to call when the event is emitted. - * @returns {function()} Returns a deregistration function for this listener. - * - * The event listener function format is: `function(event, args...)`. The `event` object - * passed into the listener has the following attributes: - * - * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or `$broadcast`-ed. - * - `currentScope` - `{Scope}`: the current scope which is handling the event. - * - `name` - `{string}`: Name of the event. - * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel further event - * propagation (available only for events that were `$emit`-ed). - * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag to true. - * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called. - */ - $on: function(name, listener) { - var namedListeners = this.$$listeners[name]; - if (!namedListeners) { - this.$$listeners[name] = namedListeners = []; - } - namedListeners.push(listener); - - return function() { - namedListeners[indexOf(namedListeners, listener)] = null; - }; - }, - - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$emit - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Dispatches an event `name` upwards through the scope hierarchy notifying the - * registered {@link ng.$rootScope.Scope#$on} listeners. - * - * The event life cycle starts at the scope on which `$emit` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified. - * Afterwards, the event traverses upwards toward the root scope and calls all registered - * listeners along the way. The event will stop propagating if one of the listeners cancels it. - * - * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed - * onto the {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {string} name Event name to emit. - * @param {...*} args Optional set of arguments which will be passed onto the event listeners. - * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} - */ - $emit: function(name, args) { - var empty = [], - namedListeners, - scope = this, - stopPropagation = false, - event = { - name: name, - targetScope: scope, - stopPropagation: function() {stopPropagation = true;}, - preventDefault: function() { - event.defaultPrevented = true; - }, - defaultPrevented: false - }, - listenerArgs = concat([event], arguments, 1), - i, length; - - do { - namedListeners = scope.$$listeners[name] || empty; - event.currentScope = scope; - for (i=0, length=namedListeners.length; i<length; i++) { - - // if listeners were deregistered, defragment the array - if (!namedListeners[i]) { - namedListeners.splice(i, 1); - i--; - length--; - continue; - } - try { - namedListeners[i].apply(null, listenerArgs); - if (stopPropagation) return event; - } catch (e) { - $exceptionHandler(e); - } - } - //traverse upwards - scope = scope.$parent; - } while (scope); - - return event; - }, - - - /** - * @ngdoc function - * @name ng.$rootScope.Scope#$broadcast - * @methodOf ng.$rootScope.Scope - * @function - * - * @description - * Dispatches an event `name` downwards to all child scopes (and their children) notifying the - * registered {@link ng.$rootScope.Scope#$on} listeners. - * - * The event life cycle starts at the scope on which `$broadcast` was called. All - * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get notified. - * Afterwards, the event propagates to all direct and indirect scopes of the current scope and - * calls all registered listeners along the way. The event cannot be canceled. - * - * Any exception emmited from the {@link ng.$rootScope.Scope#$on listeners} will be passed - * onto the {@link ng.$exceptionHandler $exceptionHandler} service. - * - * @param {string} name Event name to emit. - * @param {...*} args Optional set of arguments which will be passed onto the event listeners. - * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on} - */ - $broadcast: function(name, args) { - var target = this, - current = target, - next = target, - event = { - name: name, - targetScope: target, - preventDefault: function() { - event.defaultPrevented = true; - }, - defaultPrevented: false - }, - listenerArgs = concat([event], arguments, 1), - listeners, i, length; - - //down while you can, then up and next sibling or up and next sibling until back at root - do { - current = next; - event.currentScope = current; - listeners = current.$$listeners[name] || []; - for (i=0, length = listeners.length; i<length; i++) { - // if listeners were deregistered, defragment the array - if (!listeners[i]) { - listeners.splice(i, 1); - i--; - length--; - continue; - } - - try { - listeners[i].apply(null, listenerArgs); - } catch(e) { - $exceptionHandler(e); - } - } - - // Insanity Warning: scope depth-first traversal - // yes, this code is a bit crazy, but it works and we have tests to prove it! - // this piece should be kept in sync with the traversal in $digest - if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) { - while(current !== target && !(next = current.$$nextSibling)) { - current = current.$parent; - } - } - } while ((current = next)); - - return event; - } - }; - - var $rootScope = new Scope(); - - return $rootScope; - - - function beginPhase(phase) { - if ($rootScope.$$phase) { - throw Error($rootScope.$$phase + ' already in progress'); - } - - $rootScope.$$phase = phase; - } - - function clearPhase() { - $rootScope.$$phase = null; - } - - function compileToFn(exp, name) { - var fn = $parse(exp); - assertArgFn(fn, name); - return fn; - } - - /** - * function used as an initial value for watchers. - * because it's uniqueue we can easily tell it apart from other values - */ - function initWatchVal() {} - }]; -} - -/** - * !!! This is an undocumented "private" service !!! - * - * @name ng.$sniffer - * @requires $window - * - * @property {boolean} history Does the browser support html5 history api ? - * @property {boolean} hashchange Does the browser support hashchange event ? - * - * @description - * This is very simple implementation of testing browser's features. - */ -function $SnifferProvider() { - this.$get = ['$window', function($window) { - var eventSupport = {}, - android = int((/android (\d+)/.exec(lowercase($window.navigator.userAgent)) || [])[1]); - - return { - // Android has history.pushState, but it does not update location correctly - // so let's not use the history API at all. - // http://code.google.com/p/android/issues/detail?id=17471 - // https://github.com/angular/angular.js/issues/904 - history: !!($window.history && $window.history.pushState && !(android < 4)), - hashchange: 'onhashchange' in $window && - // IE8 compatible mode lies - (!$window.document.documentMode || $window.document.documentMode > 7), - hasEvent: function(event) { - // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have - // it. In particular the event is not fired when backspace or delete key are pressed or - // when cut operation is performed. - if (event == 'input' && msie == 9) return false; - - if (isUndefined(eventSupport[event])) { - var divElm = $window.document.createElement('div'); - eventSupport[event] = 'on' + event in divElm; - } - - return eventSupport[event]; - }, - // TODO(i): currently there is no way to feature detect CSP without triggering alerts - csp: false - }; - }]; -} - -/** - * @ngdoc object - * @name ng.$window - * - * @description - * A reference to the browser's `window` object. While `window` - * is globally available in JavaScript, it causes testability problems, because - * it is a global variable. In angular we always refer to it through the - * `$window` service, so it may be overriden, removed or mocked for testing. - * - * All expressions are evaluated with respect to current scope so they don't - * suffer from window globality. - * - * @example - <doc:example> - <doc:source> - <input ng-init="$window = $service('$window'); greeting='Hello World!'" type="text" ng-model="greeting" /> - <button ng-click="$window.alert(greeting)">ALERT</button> - </doc:source> - <doc:scenario> - </doc:scenario> - </doc:example> - */ -function $WindowProvider(){ - this.$get = valueFn(window); -} - -/** - * Parse headers into key value object - * - * @param {string} headers Raw headers as a string - * @returns {Object} Parsed headers as key value object - */ -function parseHeaders(headers) { - var parsed = {}, key, val, i; - - if (!headers) return parsed; - - forEach(headers.split('\n'), function(line) { - i = line.indexOf(':'); - key = lowercase(trim(line.substr(0, i))); - val = trim(line.substr(i + 1)); - - if (key) { - if (parsed[key]) { - parsed[key] += ', ' + val; - } else { - parsed[key] = val; - } - } - }); - - return parsed; -} - - -/** - * Returns a function that provides access to parsed headers. - * - * Headers are lazy parsed when first requested. - * @see parseHeaders - * - * @param {(string|Object)} headers Headers to provide access to. - * @returns {function(string=)} Returns a getter function which if called with: - * - * - if called with single an argument returns a single header value or null - * - if called with no arguments returns an object containing all headers. - */ -function headersGetter(headers) { - var headersObj = isObject(headers) ? headers : undefined; - - return function(name) { - if (!headersObj) headersObj = parseHeaders(headers); - - if (name) { - return headersObj[lowercase(name)] || null; - } - - return headersObj; - }; -} - - -/** - * Chain all given functions - * - * This function is used for both request and response transforming - * - * @param {*} data Data to transform. - * @param {function(string=)} headers Http headers getter fn. - * @param {(function|Array.<function>)} fns Function or an array of functions. - * @returns {*} Transformed data. - */ -function transformData(data, headers, fns) { - if (isFunction(fns)) - return fns(data, headers); - - forEach(fns, function(fn) { - data = fn(data, headers); - }); - - return data; -} - - -function isSuccess(status) { - return 200 <= status && status < 300; -} - - -function $HttpProvider() { - var JSON_START = /^\s*(\[|\{[^\{])/, - JSON_END = /[\}\]]\s*$/, - PROTECTION_PREFIX = /^\)\]\}',?\n/; - - var $config = this.defaults = { - // transform incoming response data - transformResponse: [function(data) { - if (isString(data)) { - // strip json vulnerability protection prefix - data = data.replace(PROTECTION_PREFIX, ''); - if (JSON_START.test(data) && JSON_END.test(data)) - data = fromJson(data, true); - } - return data; - }], - - // transform outgoing request data - transformRequest: [function(d) { - return isObject(d) && !isFile(d) ? toJson(d) : d; - }], - - // default headers - 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'} - } - }; - - var providerResponseInterceptors = this.responseInterceptors = []; - - this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector', - function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) { - - var defaultCache = $cacheFactory('$http'), - responseInterceptors = []; - - forEach(providerResponseInterceptors, function(interceptor) { - responseInterceptors.push( - isString(interceptor) - ? $injector.get(interceptor) - : $injector.invoke(interceptor) - ); - }); - - - /** - * @ngdoc function - * @name ng.$http - * @requires $httpBacked - * @requires $browser - * @requires $cacheFactory - * @requires $rootScope - * @requires $q - * @requires $injector - * - * @description - * The `$http` service is a core Angular service that facilitates communication with the remote - * HTTP servers via browser's {@link https://developer.mozilla.org/en/xmlhttprequest - * XMLHttpRequest} object or via {@link http://en.wikipedia.org/wiki/JSONP JSONP}. - * - * For unit testing applications that use `$http` service, see - * {@link ngMock.$httpBackend $httpBackend mock}. - * - * For a higher level of abstraction, please check out the {@link ngResource.$resource - * $resource} service. - * - * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by - * the $q service. While for simple usage patters this doesn't matter much, for advanced usage, - * it is important to familiarize yourself with these apis and guarantees they provide. - * - * - * # General usage - * The `$http` service is a function which takes a single argument — a configuration object — - * that is used to generate an http request and returns a {@link ng.$q promise} - * with two $http specific methods: `success` and `error`. - * - * <pre> - * $http({method: 'GET', url: '/someUrl'}). - * success(function(data, status, headers, config) { - * // this callback will be called asynchronously - * // when the response is available - * }). - * error(function(data, status, headers, config) { - * // called asynchronously if an error occurs - * // or server returns response with status - * // code outside of the <200, 400) range - * }); - * </pre> - * - * Since the returned value of calling the $http function is a Promise object, you can also use - * the `then` method to register callbacks, and these callbacks will receive a single argument – - * an object representing the response. See the api signature and type info below for more - * details. - * - * - * # Shortcut methods - * - * Since all invocation of the $http service require definition of the http method and url and - * POST and PUT requests require response body/data to be provided as well, shortcut methods - * were created to simplify using the api: - * - * <pre> - * $http.get('/someUrl').success(successCallback); - * $http.post('/someUrl', data).success(successCallback); - * </pre> - * - * Complete list of shortcut methods: - * - * - {@link ng.$http#get $http.get} - * - {@link ng.$http#head $http.head} - * - {@link ng.$http#post $http.post} - * - {@link ng.$http#put $http.put} - * - {@link ng.$http#delete $http.delete} - * - {@link ng.$http#jsonp $http.jsonp} - * - * - * # Setting HTTP Headers - * - * The $http service will automatically add certain http headers to all requests. These defaults - * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration - * object, which currently contains this default configuration: - * - * - `$httpProvider.defaults.headers.common` (headers that are common for all requests): - * - `Accept: application/json, text/plain, * / *` - * - `X-Requested-With: XMLHttpRequest` - * - `$httpProvider.defaults.headers.post`: (header defaults for HTTP POST requests) - * - `Content-Type: application/json` - * - `$httpProvider.defaults.headers.put` (header defaults for HTTP PUT requests) - * - `Content-Type: application/json` - * - * To add or overwrite these defaults, simply add or remove a property from this configuration - * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object - * with name equal to the lower-cased http method name, e.g. - * `$httpProvider.defaults.headers.get['My-Header']='value'`. - * - * Additionally, the defaults can be set at runtime via the `$http.defaults` object in a similar - * fassion as described above. - * - * - * # Transforming Requests and Responses - * - * Both requests and responses can be transformed using transform functions. By default, Angular - * applies these transformations: - * - * Request transformations: - * - * - if the `data` property of the request config object contains an object, serialize it into - * JSON format. - * - * Response transformations: - * - * - if XSRF prefix is detected, strip it (see Security Considerations section below) - * - if json response is detected, deserialize it using a JSON parser - * - * To override these transformation locally, specify transform functions as `transformRequest` - * and/or `transformResponse` properties of the config object. To globally override the default - * transforms, override the `$httpProvider.defaults.transformRequest` and - * `$httpProvider.defaults.transformResponse` properties of the `$httpProvider`. - * - * - * # Caching - * - * To enable caching set the configuration property `cache` to `true`. When the cache is - * enabled, `$http` stores the response from the server in local cache. Next time the - * response is served from the cache without sending a request to the server. - * - * Note that even if the response is served from cache, delivery of the data is asynchronous in - * the same way that real requests are. - * - * If there are multiple GET requests for the same url that should be cached using the same - * cache, but the cache is not populated yet, only one request to the server will be made and - * the remaining requests will be fulfilled using the response for the first request. - * - * - * # Response interceptors - * - * Before you start creating interceptors, be sure to understand the - * {@link ng.$q $q and deferred/promise APIs}. - * - * For purposes of global error handling, authentication or any kind of synchronous or - * asynchronous preprocessing of received responses, it is desirable to be able to intercept - * responses for http requests before they are handed over to the application code that - * initiated these requests. The response interceptors leverage the {@link ng.$q - * promise apis} to fulfil this need for both synchronous and asynchronous preprocessing. - * - * The interceptors are service factories that are registered with the $httpProvider by - * adding them to the `$httpProvider.responseInterceptors` array. The factory is called and - * injected with dependencies (if specified) and returns the interceptor — a function that - * takes a {@link ng.$q promise} and returns the original or a new promise. - * - * <pre> - * // register the interceptor as a service - * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) { - * return function(promise) { - * return promise.then(function(response) { - * // do something on success - * }, function(response) { - * // do something on error - * if (canRecover(response)) { - * return responseOrNewPromise - * } - * return $q.reject(response); - * }); - * } - * }); - * - * $httpProvider.responseInterceptors.push('myHttpInterceptor'); - * - * - * // register the interceptor via an anonymous factory - * $httpProvider.responseInterceptors.push(function($q, dependency1, dependency2) { - * return function(promise) { - * // same as above - * } - * }); - * </pre> - * - * - * # Security Considerations - * - * When designing web applications, consider security threats from: - * - * - {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx - * JSON Vulnerability} - * - {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} - * - * Both server and the client must cooperate in order to eliminate these threats. Angular comes - * pre-configured with strategies that address these issues, but for this to work backend server - * cooperation is required. - * - * ## JSON Vulnerability Protection - * - * A {@link http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx - * JSON Vulnerability} allows third party web-site to turn your JSON resource URL into - * {@link http://en.wikipedia.org/wiki/JSON#JSONP JSONP} request under some conditions. To - * counter this your server can prefix all JSON requests with following string `")]}',\n"`. - * Angular will automatically strip the prefix before processing it as JSON. - * - * For example if your server needs to return: - * <pre> - * ['one','two'] - * </pre> - * - * which is vulnerable to attack, your server can return: - * <pre> - * )]}', - * ['one','two'] - * </pre> - * - * Angular will strip the prefix, before processing the JSON. - * - * - * ## Cross Site Request Forgery (XSRF) Protection - * - * {@link http://en.wikipedia.org/wiki/Cross-site_request_forgery XSRF} is a technique by which - * an unauthorized site can gain your user's private data. Angular provides following mechanism - * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie - * called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that - * runs on your domain could read the cookie, your server can be assured that the XHR came from - * JavaScript running on your domain. - * - * To take advantage of this, your server needs to set a token in a JavaScript readable session - * cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the - * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure - * that only JavaScript running on your domain could have read the token. The token must be - * unique for each user and must be verifiable by the server (to prevent the JavaScript making - * up its own tokens). We recommend that the token is a digest of your site's authentication - * cookie with {@link http://en.wikipedia.org/wiki/Rainbow_table salt for added security}. - * - * - * @param {object} config Object describing the request to be made and how it should be - * processed. The object has following properties: - * - * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc) - * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested. - * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned to - * `?key1=value1&key2=value2` after the url. If the value is not a string, it will be JSONified. - * - **data** – `{string|Object}` – Data to be sent as the request message data. - * - **headers** – `{Object}` – Map of strings representing HTTP headers to send to the server. - * - **transformRequest** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – - * transform function or an array of such functions. The transform function takes the http - * request body and headers and returns its transformed (typically serialized) version. - * - **transformResponse** – `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` – - * transform function or an array of such functions. The transform function takes the http - * response body and headers and returns its transformed (typically deserialized) version. - * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the - * GET request, otherwise if a cache instance built with - * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for - * caching. - * - **timeout** – `{number}` – timeout in milliseconds. - * - **withCredentials** - `{boolean}` - whether to to set the `withCredentials` flag on the - * XHR object. See {@link https://developer.mozilla.org/en/http_access_control#section_5 - * requests with credentials} for more information. - * - * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the - * standard `then` method and two http specific methods: `success` and `error`. The `then` - * method takes two arguments a success and an error callback which will be called with a - * response object. The `success` and `error` methods take a single argument - a function that - * will be called when the request succeeds or fails respectively. The arguments passed into - * these functions are destructured representation of the response object passed into the - * `then` method. The response object has these properties: - * - * - **data** – `{string|Object}` – The response body transformed with the transform functions. - * - **status** – `{number}` – HTTP status code of the response. - * - **headers** – `{function([headerName])}` – Header getter function. - * - **config** – `{Object}` – The configuration object that was used to generate the request. - * - * @property {Array.<Object>} pendingRequests Array of config objects for currently pending - * requests. This is primarily meant to be used for debugging purposes. - * - * - * @example - <example> - <file name="index.html"> - <div ng-controller="FetchCtrl"> - <select ng-model="method"> - <option>GET</option> - <option>JSONP</option> - </select> - <input type="text" ng-model="url" size="80"/> - <button ng-click="fetch()">fetch</button><br> - <button ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button> - <button ng-click="updateModel('JSONP', 'http://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">Sample JSONP</button> - <button ng-click="updateModel('JSONP', 'http://angularjs.org/doesntexist&callback=JSON_CALLBACK')">Invalid JSONP</button> - <pre>http status code: {{status}}</pre> - <pre>http response data: {{data}}</pre> - </div> - </file> - <file name="script.js"> - function FetchCtrl($scope, $http, $templateCache) { - $scope.method = 'GET'; - $scope.url = 'http-hello.html'; - - $scope.fetch = function() { - $scope.code = null; - $scope.response = null; - - $http({method: $scope.method, url: $scope.url, cache: $templateCache}). - success(function(data, status) { - $scope.status = status; - $scope.data = data; - }). - error(function(data, status) { - $scope.data = data || "Request failed"; - $scope.status = status; - }); - }; - - $scope.updateModel = function(method, url) { - $scope.method = method; - $scope.url = url; - }; - } - </file> - <file name="http-hello.html"> - Hello, $http! - </file> - <file name="scenario.js"> - it('should make an xhr GET request', function() { - element(':button:contains("Sample GET")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('200'); - expect(binding('data')).toMatch(/Hello, \$http!/); - }); - - it('should make a JSONP request to angularjs.org', function() { - element(':button:contains("Sample JSONP")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('200'); - expect(binding('data')).toMatch(/Super Hero!/); - }); - - it('should make JSONP request to invalid URL and invoke the error handler', - function() { - element(':button:contains("Invalid JSONP")').click(); - element(':button:contains("fetch")').click(); - expect(binding('status')).toBe('0'); - expect(binding('data')).toBe('Request failed'); - }); - </file> - </example> - */ - function $http(config) { - config.method = uppercase(config.method); - - var reqTransformFn = config.transformRequest || $config.transformRequest, - respTransformFn = config.transformResponse || $config.transformResponse, - defHeaders = $config.headers, - reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']}, - defHeaders.common, defHeaders[lowercase(config.method)], config.headers), - reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn), - promise; - - // strip content-type if data is undefined - if (isUndefined(config.data)) { - delete reqHeaders['Content-Type']; - } - - // send request - promise = sendReq(config, reqData, reqHeaders); - - - // transform future response - promise = promise.then(transformResponse, transformResponse); - - // apply interceptors - forEach(responseInterceptors, function(interceptor) { - promise = interceptor(promise); - }); - - promise.success = function(fn) { - promise.then(function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - - promise.error = function(fn) { - promise.then(null, function(response) { - fn(response.data, response.status, response.headers, config); - }); - return promise; - }; - - return promise; - - function transformResponse(response) { - // make a copy since the response must be cacheable - var resp = extend({}, response, { - data: transformData(response.data, response.headers, respTransformFn) - }); - return (isSuccess(response.status)) - ? resp - : $q.reject(resp); - } - } - - $http.pendingRequests = []; - - /** - * @ngdoc method - * @name ng.$http#get - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `GET` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#delete - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `DELETE` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#head - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `HEAD` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#jsonp - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `JSONP` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request. - * Should contain `JSON_CALLBACK` string. - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethods('get', 'delete', 'head', 'jsonp'); - - /** - * @ngdoc method - * @name ng.$http#post - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `POST` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - - /** - * @ngdoc method - * @name ng.$http#put - * @methodOf ng.$http - * - * @description - * Shortcut method to perform `PUT` request - * - * @param {string} url Relative or absolute URL specifying the destination of the request - * @param {*} data Request content - * @param {Object=} config Optional configuration object - * @returns {HttpPromise} Future object - */ - createShortMethodsWithData('post', 'put'); - - /** - * @ngdoc property - * @name ng.$http#defaults - * @propertyOf ng.$http - * - * @description - * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of - * default headers as well as request and response transformations. - * - * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above. - */ - $http.defaults = $config; - - - return $http; - - - function createShortMethods(names) { - forEach(arguments, function(name) { - $http[name] = function(url, config) { - return $http(extend(config || {}, { - method: name, - url: url - })); - }; - }); - } - - - function createShortMethodsWithData(name) { - forEach(arguments, function(name) { - $http[name] = function(url, data, config) { - return $http(extend(config || {}, { - method: name, - url: url, - data: data - })); - }; - }); - } - - - /** - * Makes the request - * - * !!! ACCESSES CLOSURE VARS: - * $httpBackend, $config, $log, $rootScope, defaultCache, $http.pendingRequests - */ - function sendReq(config, reqData, reqHeaders) { - var deferred = $q.defer(), - promise = deferred.promise, - cache, - cachedResp, - url = buildUrl(config.url, config.params); - - $http.pendingRequests.push(config); - promise.then(removePendingReq, removePendingReq); - - - if (config.cache && config.method == 'GET') { - cache = isObject(config.cache) ? config.cache : defaultCache; - } - - if (cache) { - cachedResp = cache.get(url); - if (cachedResp) { - if (cachedResp.then) { - // cached request has already been sent, but there is no response yet - cachedResp.then(removePendingReq, removePendingReq); - return cachedResp; - } else { - // serving from cache - if (isArray(cachedResp)) { - resolvePromise(cachedResp[1], cachedResp[0], copy(cachedResp[2])); - } else { - resolvePromise(cachedResp, 200, {}); - } - } - } else { - // put the promise for the non-transformed response into cache as a placeholder - cache.put(url, promise); - } - } - - // if we won't have the response in cache, send the request to the backend - if (!cachedResp) { - $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout, - config.withCredentials); - } - - return promise; - - - /** - * Callback registered to $httpBackend(): - * - caches the response if desired - * - resolves the raw $http promise - * - calls $apply - */ - function done(status, response, headersString) { - if (cache) { - if (isSuccess(status)) { - cache.put(url, [status, response, parseHeaders(headersString)]); - } else { - // remove promise from the cache - cache.remove(url); - } - } - - resolvePromise(response, status, headersString); - $rootScope.$apply(); - } - - - /** - * Resolves the raw $http promise. - */ - function resolvePromise(response, status, headers) { - // normalize internal statuses to 0 - status = Math.max(status, 0); - - (isSuccess(status) ? deferred.resolve : deferred.reject)({ - data: response, - status: status, - headers: headersGetter(headers), - config: config - }); - } - - - function removePendingReq() { - var idx = indexOf($http.pendingRequests, config); - if (idx !== -1) $http.pendingRequests.splice(idx, 1); - } - } - - - function buildUrl(url, params) { - if (!params) return url; - var parts = []; - forEachSorted(params, function(value, key) { - if (value == null || value == undefined) return; - if (isObject(value)) { - value = toJson(value); - } - parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); - }); - return url + ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&'); - } - - - }]; -} -var XHR = window.XMLHttpRequest || function() { - try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } catch (e1) {} - try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } catch (e2) {} - try { return new ActiveXObject("Msxml2.XMLHTTP"); } catch (e3) {} - throw new Error("This browser does not support XMLHttpRequest."); -}; - - -/** - * @ngdoc object - * @name ng.$httpBackend - * @requires $browser - * @requires $window - * @requires $document - * - * @description - * HTTP backend used by the {@link ng.$http service} that delegates to - * XMLHttpRequest object or JSONP and deals with browser incompatibilities. - * - * You should never need to use this service directly, instead use the higher-level abstractions: - * {@link ng.$http $http} or {@link ngResource.$resource $resource}. - * - * During testing this implementation is swapped with {@link ngMock.$httpBackend mock - * $httpBackend} which can be trained with responses. - */ -function $HttpBackendProvider() { - this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) { - return createHttpBackend($browser, XHR, $browser.defer, $window.angular.callbacks, - $document[0], $window.location.protocol.replace(':', '')); - }]; -} - -function createHttpBackend($browser, XHR, $browserDefer, callbacks, rawDocument, locationProtocol) { - // TODO(vojta): fix the signature - return function(method, url, post, callback, headers, timeout, withCredentials) { - $browser.$$incOutstandingRequestCount(); - url = url || $browser.url(); - - if (lowercase(method) == 'jsonp') { - var callbackId = '_' + (callbacks.counter++).toString(36); - callbacks[callbackId] = function(data) { - callbacks[callbackId].data = data; - }; - - jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId), - function() { - if (callbacks[callbackId].data) { - completeRequest(callback, 200, callbacks[callbackId].data); - } else { - completeRequest(callback, -2); - } - delete callbacks[callbackId]; - }); - } else { - var xhr = new XHR(); - xhr.open(method, url, true); - forEach(headers, function(value, key) { - if (value) xhr.setRequestHeader(key, value); - }); - - var status; - - // In IE6 and 7, this might be called synchronously when xhr.send below is called and the - // response is in the cache. the promise api will ensure that to the app code the api is - // always async - xhr.onreadystatechange = function() { - if (xhr.readyState == 4) { - completeRequest( - callback, status || xhr.status, xhr.responseText, xhr.getAllResponseHeaders()); - } - }; - - if (withCredentials) { - xhr.withCredentials = true; - } - - xhr.send(post || ''); - - if (timeout > 0) { - $browserDefer(function() { - status = -1; - xhr.abort(); - }, timeout); - } - } - - - function completeRequest(callback, status, response, headersString) { - // URL_MATCH is defined in src/service/location.js - var protocol = (url.match(URL_MATCH) || ['', locationProtocol])[1]; - - // fix status code for file protocol (it's always 0) - status = (protocol == 'file') ? (response ? 200 : 404) : status; - - // normalize IE bug (http://bugs.jquery.com/ticket/1450) - status = status == 1223 ? 204 : status; - - callback(status, response, headersString); - $browser.$$completeOutstandingRequest(noop); - } - }; - - function jsonpReq(url, done) { - // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.: - // - fetches local scripts via XHR and evals them - // - adds and immediately removes script elements from the document - var script = rawDocument.createElement('script'), - doneWrapper = function() { - rawDocument.body.removeChild(script); - if (done) done(); - }; - - script.type = 'text/javascript'; - script.src = url; - - if (msie) { - script.onreadystatechange = function() { - if (/loaded|complete/.test(script.readyState)) doneWrapper(); - }; - } else { - script.onload = script.onerror = doneWrapper; - } - - rawDocument.body.appendChild(script); - } -} - -/** - * @ngdoc object - * @name ng.$locale - * - * @description - * $locale service provides localization rules for various Angular components. As of right now the - * only public api is: - * - * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`) - */ -function $LocaleProvider(){ - this.$get = function() { - return { - id: 'en-us', - - NUMBER_FORMATS: { - DECIMAL_SEP: '.', - GROUP_SEP: ',', - PATTERNS: [ - { // Decimal Pattern - minInt: 1, - minFrac: 0, - maxFrac: 3, - posPre: '', - posSuf: '', - negPre: '-', - negSuf: '', - gSize: 3, - lgSize: 3 - },{ //Currency Pattern - 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(','), - 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(num) { - if (num === 1) { - return 'one'; - } - return 'other'; - } - }; - }; -} - -function $TimeoutProvider() { - this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler', - function($rootScope, $browser, $q, $exceptionHandler) { - var deferreds = {}; - - - /** - * @ngdoc function - * @name ng.$timeout - * @requires $browser - * - * @description - * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch - * block and delegates any exceptions to - * {@link ng.$exceptionHandler $exceptionHandler} service. - * - * The return value of registering a timeout function is a promise which will be resolved when - * the timeout is reached and the timeout function is executed. - * - * To cancel a the timeout request, call `$timeout.cancel(promise)`. - * - * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to - * synchronously flush the queue of deferred functions. - * - * @param {function()} fn A function, who's execution should be delayed. - * @param {number=} [delay=0] Delay in milliseconds. - * @param {boolean=} [invokeApply=true] If set to false skips model dirty checking, otherwise - * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block. - * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this - * promise will be resolved with is the return value of the `fn` function. - */ - function timeout(fn, delay, invokeApply) { - var deferred = $q.defer(), - promise = deferred.promise, - skipApply = (isDefined(invokeApply) && !invokeApply), - timeoutId, cleanup; - - timeoutId = $browser.defer(function() { - try { - deferred.resolve(fn()); - } catch(e) { - deferred.reject(e); - $exceptionHandler(e); - } - - if (!skipApply) $rootScope.$apply(); - }, delay); - - cleanup = function() { - delete deferreds[promise.$$timeoutId]; - }; - - promise.$$timeoutId = timeoutId; - deferreds[timeoutId] = deferred; - promise.then(cleanup, cleanup); - - return promise; - } - - - /** - * @ngdoc function - * @name ng.$timeout#cancel - * @methodOf ng.$timeout - * - * @description - * Cancels a task associated with the `promise`. As a result of this the promise will be - * resolved with a rejection. - * - * @param {Promise=} promise Promise returned by the `$timeout` function. - * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully - * canceled. - */ - timeout.cancel = function(promise) { - if (promise && promise.$$timeoutId in deferreds) { - deferreds[promise.$$timeoutId].reject('canceled'); - return $browser.defer.cancel(promise.$$timeoutId); - } - return false; - }; - - return timeout; - }]; -} - -/** - * @ngdoc object - * @name ng.$filterProvider - * @description - * - * Filters are just functions which transform input to an output. However filters need to be Dependency Injected. To - * achieve this a filter definition consists of a factory function which is annotated with dependencies and is - * responsible for creating a the filter function. - * - * <pre> - * // Filter registration - * function MyModule($provide, $filterProvider) { - * // create a service to demonstrate injection (not always needed) - * $provide.value('greet', function(name){ - * return 'Hello ' + name + '!'; - * }); - * - * // register a filter factory which uses the - * // greet service to demonstrate DI. - * $filterProvider.register('greet', function(greet){ - * // return the filter function which uses the greet service - * // to generate salutation - * return function(text) { - * // filters need to be forgiving so check input validity - * return text && greet(text) || text; - * }; - * }); - * } - * </pre> - * - * The filter function is registered with the `$injector` under the filter name suffixe with `Filter`. - * <pre> - * it('should be the same instance', inject( - * function($filterProvider) { - * $filterProvider.register('reverse', function(){ - * return ...; - * }); - * }, - * function($filter, reverseFilter) { - * expect($filter('reverse')).toBe(reverseFilter); - * }); - * </pre> - * - * - * For more information about how angular filters work, and how to create your own filters, see - * {@link guide/dev_guide.templates.filters Understanding Angular Filters} in the angular Developer - * Guide. - */ -/** - * @ngdoc method - * @name ng.$filterProvider#register - * @methodOf ng.$filterProvider - * @description - * Register filter factory function. - * - * @param {String} name Name of the filter. - * @param {function} fn The filter factory function which is injectable. - */ - - -/** - * @ngdoc function - * @name ng.$filter - * @function - * @description - * Filters are used for formatting data displayed to the user. - * - * The general syntax in templates is as follows: - * - * {{ expression | [ filter_name ] }} - * - * @param {String} name Name of the filter function to retrieve - * @return {Function} the filter function - */ -$FilterProvider.$inject = ['$provide']; -function $FilterProvider($provide) { - var suffix = 'Filter'; - - function register(name, factory) { - return $provide.factory(name + suffix, factory); - } - this.register = register; - - this.$get = ['$injector', function($injector) { - return function(name) { - return $injector.get(name + suffix); - } - }]; - - //////////////////////////////////////// - - register('currency', currencyFilter); - register('date', dateFilter); - register('filter', filterFilter); - register('json', jsonFilter); - register('limitTo', limitToFilter); - register('lowercase', lowercaseFilter); - register('number', numberFilter); - register('orderBy', orderByFilter); - register('uppercase', uppercaseFilter); -} - -/** - * @ngdoc filter - * @name ng.filter:filter - * @function - * - * @description - * Selects a subset of items from `array` and returns it as a new array. - * - * Note: This function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * - * @param {Array} array The source array. - * @param {string|Object|function()} expression The predicate to be used for selecting items from - * `array`. - * - * Can be one of: - * - * - `string`: Predicate that results in a substring match using the value of `expression` - * string. All strings or objects with string properties in `array` that contain this string - * will be returned. The predicate can be negated by prefixing the string with `!`. - * - * - `Object`: A pattern object can be used to filter specific properties on objects contained - * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items - * which have property `name` containing "M" and property `phone` containing "1". A special - * property name `$` can be used (as in `{$:"text"}`) to accept a match against any - * property of the object. That's equivalent to the simple substring match with a `string` - * as described above. - * - * - `function`: A predicate function can be used to write arbitrary filters. The function is - * called for each element of `array`. The final result is an array of those elements that - * the predicate returned true for. - * - * @example - <doc:example> - <doc:source> - <div ng-init="friends = [{name:'John', phone:'555-1276'}, - {name:'Mary', phone:'800-BIG-MARY'}, - {name:'Mike', phone:'555-4321'}, - {name:'Adam', phone:'555-5678'}, - {name:'Julie', phone:'555-8765'}]"></div> - - Search: <input ng-model="searchText"> - <table id="searchTextResults"> - <tr><th>Name</th><th>Phone</th><tr> - <tr ng-repeat="friend in friends | filter:searchText"> - <td>{{friend.name}}</td> - <td>{{friend.phone}}</td> - <tr> - </table> - <hr> - Any: <input ng-model="search.$"> <br> - Name only <input ng-model="search.name"><br> - Phone only <input ng-model="search.phone"å><br> - <table id="searchObjResults"> - <tr><th>Name</th><th>Phone</th><tr> - <tr ng-repeat="friend in friends | filter:search"> - <td>{{friend.name}}</td> - <td>{{friend.phone}}</td> - <tr> - </table> - </doc:source> - <doc:scenario> - it('should search across all fields when filtering with a string', function() { - input('searchText').enter('m'); - expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Mike', 'Adam']); - - input('searchText').enter('76'); - expect(repeater('#searchTextResults tr', 'friend in friends').column('friend.name')). - toEqual(['John', 'Julie']); - }); - - it('should search in specific fields when filtering with a predicate object', function() { - input('search.$').enter('i'); - expect(repeater('#searchObjResults tr', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Mike', 'Julie']); - }); - </doc:scenario> - </doc:example> - */ -function filterFilter() { - return function(array, expression) { - if (!(array instanceof Array)) return array; - var predicates = []; - predicates.check = function(value) { - for (var j = 0; j < predicates.length; j++) { - if(!predicates[j](value)) { - return false; - } - } - return true; - }; - var search = function(obj, text){ - if (text.charAt(0) === '!') { - return !search(obj, text.substr(1)); - } - switch (typeof obj) { - case "boolean": - case "number": - case "string": - return ('' + obj).toLowerCase().indexOf(text) > -1; - case "object": - for ( var objKey in obj) { - if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) { - return true; - } - } - return false; - case "array": - for ( var i = 0; i < obj.length; i++) { - if (search(obj[i], text)) { - return true; - } - } - return false; - default: - return false; - } - }; - switch (typeof expression) { - case "boolean": - case "number": - case "string": - expression = {$:expression}; - case "object": - for (var key in expression) { - if (key == '$') { - (function() { - var text = (''+expression[key]).toLowerCase(); - if (!text) return; - predicates.push(function(value) { - return search(value, text); - }); - })(); - } else { - (function() { - var path = key; - var text = (''+expression[key]).toLowerCase(); - if (!text) return; - predicates.push(function(value) { - return search(getter(value, path), text); - }); - })(); - } - } - break; - case 'function': - predicates.push(expression); - break; - default: - return array; - } - var filtered = []; - for ( var j = 0; j < array.length; j++) { - var value = array[j]; - if (predicates.check(value)) { - filtered.push(value); - } - } - return filtered; - } -} - -/** - * @ngdoc filter - * @name ng.filter:currency - * @function - * - * @description - * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default - * symbol for current locale is used. - * - * @param {number} amount Input to filter. - * @param {string=} symbol Currency symbol or identifier to be displayed. - * @returns {string} Formatted number. - * - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.amount = 1234.56; - } - </script> - <div ng-controller="Ctrl"> - <input type="number" ng-model="amount"> <br> - default currency symbol ($): {{amount | currency}}<br> - custom currency identifier (USD$): {{amount | currency:"USD$"}} - </div> - </doc:source> - <doc:scenario> - it('should init with 1234.56', function() { - expect(binding('amount | currency')).toBe('$1,234.56'); - expect(binding('amount | currency:"USD$"')).toBe('USD$1,234.56'); - }); - it('should update', function() { - input('amount').enter('-1234'); - expect(binding('amount | currency')).toBe('($1,234.00)'); - expect(binding('amount | currency:"USD$"')).toBe('(USD$1,234.00)'); - }); - </doc:scenario> - </doc:example> - */ -currencyFilter.$inject = ['$locale']; -function currencyFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(amount, currencySymbol){ - if (isUndefined(currencySymbol)) currencySymbol = formats.CURRENCY_SYM; - return formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, 2). - replace(/\u00A4/g, currencySymbol); - }; -} - -/** - * @ngdoc filter - * @name ng.filter:number - * @function - * - * @description - * Formats a number as text. - * - * If the input is not a number an empty string is returned. - * - * @param {number|string} number Number to format. - * @param {(number|string)=} [fractionSize=2] Number of decimal places to round the number to. - * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.val = 1234.56789; - } - </script> - <div ng-controller="Ctrl"> - Enter number: <input ng-model='val'><br> - Default formatting: {{val | number}}<br> - No fractions: {{val | number:0}}<br> - Negative number: {{-val | number:4}} - </div> - </doc:source> - <doc:scenario> - it('should format numbers', function() { - expect(binding('val | number')).toBe('1,234.568'); - expect(binding('val | number:0')).toBe('1,235'); - expect(binding('-val | number:4')).toBe('-1,234.5679'); - }); - - it('should update', function() { - input('val').enter('3374.333'); - expect(binding('val | number')).toBe('3,374.333'); - expect(binding('val | number:0')).toBe('3,374'); - expect(binding('-val | number:4')).toBe('-3,374.3330'); - }); - </doc:scenario> - </doc:example> - */ - - -numberFilter.$inject = ['$locale']; -function numberFilter($locale) { - var formats = $locale.NUMBER_FORMATS; - return function(number, fractionSize) { - return formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP, - fractionSize); - }; -} - -var DECIMAL_SEP = '.'; -function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) { - if (isNaN(number) || !isFinite(number)) return ''; - - var isNegative = number < 0; - number = Math.abs(number); - var numStr = number + '', - formatedText = '', - parts = []; - - var hasExponent = false; - if (numStr.indexOf('e') !== -1) { - var match = numStr.match(/([\d\.]+)e(-?)(\d+)/); - if (match && match[2] == '-' && match[3] > fractionSize + 1) { - numStr = '0'; - } else { - formatedText = numStr; - hasExponent = true; - } - } - - if (!hasExponent) { - var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length; - - // determine fractionSize if it is not specified - if (isUndefined(fractionSize)) { - fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac); - } - - var pow = Math.pow(10, fractionSize); - number = Math.round(number * pow) / pow; - var fraction = ('' + number).split(DECIMAL_SEP); - var whole = fraction[0]; - fraction = fraction[1] || ''; - - var pos = 0, - lgroup = pattern.lgSize, - group = pattern.gSize; - - if (whole.length >= (lgroup + group)) { - pos = whole.length - lgroup; - for (var i = 0; i < pos; i++) { - if ((pos - i)%group === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - } - - for (i = pos; i < whole.length; i++) { - if ((whole.length - i)%lgroup === 0 && i !== 0) { - formatedText += groupSep; - } - formatedText += whole.charAt(i); - } - - // format fraction part. - while(fraction.length < fractionSize) { - fraction += '0'; - } - - if (fractionSize) formatedText += decimalSep + fraction.substr(0, fractionSize); - } - - parts.push(isNegative ? pattern.negPre : pattern.posPre); - parts.push(formatedText); - parts.push(isNegative ? pattern.negSuf : pattern.posSuf); - return parts.join(''); -} - -function padNumber(num, digits, trim) { - var neg = ''; - if (num < 0) { - neg = '-'; - num = -num; - } - num = '' + num; - while(num.length < digits) num = '0' + num; - if (trim) - num = num.substr(num.length - digits); - return neg + num; -} - - -function dateGetter(name, size, offset, trim) { - return function(date) { - var value = date['get' + name](); - if (offset > 0 || value > -offset) - value += offset; - if (value === 0 && offset == -12 ) value = 12; - return padNumber(value, size, trim); - }; -} - -function dateStrGetter(name, shortForm) { - return function(date, formats) { - var value = date['get' + name](); - var get = uppercase(shortForm ? ('SHORT' + name) : name); - - return formats[get][value]; - }; -} - -function timeZoneGetter(date) { - var offset = date.getTimezoneOffset(); - return padNumber(offset / 60, 2) + padNumber(Math.abs(offset % 60), 2); -} - -function ampmGetter(date, formats) { - return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]; -} - -var DATE_FORMATS = { - yyyy: dateGetter('FullYear', 4), - yy: dateGetter('FullYear', 2, 0, true), - y: dateGetter('FullYear', 1), - MMMM: dateStrGetter('Month'), - MMM: dateStrGetter('Month', true), - MM: dateGetter('Month', 2, 1), - M: dateGetter('Month', 1, 1), - dd: dateGetter('Date', 2), - d: dateGetter('Date', 1), - HH: dateGetter('Hours', 2), - H: dateGetter('Hours', 1), - hh: dateGetter('Hours', 2, -12), - h: dateGetter('Hours', 1, -12), - mm: dateGetter('Minutes', 2), - m: dateGetter('Minutes', 1), - ss: dateGetter('Seconds', 2), - s: dateGetter('Seconds', 1), - EEEE: dateStrGetter('Day'), - EEE: dateStrGetter('Day', true), - a: ampmGetter, - Z: timeZoneGetter -}; - -var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/, - NUMBER_STRING = /^\d+$/; - -/** - * @ngdoc filter - * @name ng.filter:date - * @function - * - * @description - * Formats `date` to a string based on the requested `format`. - * - * `format` string can be composed of the following elements: - * - * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) - * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) - * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) - * * `'MMMM'`: Month in year (January-December) - * * `'MMM'`: Month in year (Jan-Dec) - * * `'MM'`: Month in year, padded (01-12) - * * `'M'`: Month in year (1-12) - * * `'dd'`: Day in month, padded (01-31) - * * `'d'`: Day in month (1-31) - * * `'EEEE'`: Day in Week,(Sunday-Saturday) - * * `'EEE'`: Day in Week, (Sun-Sat) - * * `'HH'`: Hour in day, padded (00-23) - * * `'H'`: Hour in day (0-23) - * * `'hh'`: Hour in am/pm, padded (01-12) - * * `'h'`: Hour in am/pm, (1-12) - * * `'mm'`: Minute in hour, padded (00-59) - * * `'m'`: Minute in hour (0-59) - * * `'ss'`: Second in minute, padded (00-59) - * * `'s'`: Second in minute (0-59) - * * `'a'`: am/pm marker - * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-1200) - * - * `format` string can also be one of the following predefined - * {@link guide/i18n localizable formats}: - * - * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale - * (e.g. Sep 3, 2010 12:05:08 pm) - * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 pm) - * * `'fullDate'`: equivalent to `'EEEE, MMMM d,y'` for en_US locale - * (e.g. Friday, September 3, 2010) - * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010 - * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010) - * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10) - * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 pm) - * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 pm) - * - * `format` string can contain literal values. These need to be quoted with single quotes (e.g. - * `"h 'in the morning'"`). In order to output single quote, use two single quotes in a sequence - * (e.g. `"h o''clock"`). - * - * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or - * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.SSSZ and it's - * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). - * @param {string=} format Formatting rules (see Description). If not specified, - * `mediumDate` is used. - * @returns {string} Formatted string or the input if input is not recognized as date/millis. - * - * @example - <doc:example> - <doc:source> - <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>: - {{1288323623006 | date:'medium'}}<br> - <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>: - {{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}<br> - <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>: - {{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}<br> - </doc:source> - <doc:scenario> - it('should format date', function() { - expect(binding("1288323623006 | date:'medium'")). - toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/); - expect(binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")). - toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} \-?\d{4}/); - expect(binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")). - toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/); - }); - </doc:scenario> - </doc:example> - */ -dateFilter.$inject = ['$locale']; -function dateFilter($locale) { - - - var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/; - function jsonStringToDate(string){ - var match; - if (match = string.match(R_ISO8601_STR)) { - var date = new Date(0), - tzHour = 0, - tzMin = 0; - if (match[9]) { - tzHour = int(match[9] + match[10]); - tzMin = int(match[9] + match[11]); - } - date.setUTCFullYear(int(match[1]), int(match[2]) - 1, int(match[3])); - date.setUTCHours(int(match[4]||0) - tzHour, int(match[5]||0) - tzMin, int(match[6]||0), int(match[7]||0)); - return date; - } - return string; - } - - - return function(date, format) { - var text = '', - parts = [], - fn, match; - - format = format || 'mediumDate'; - format = $locale.DATETIME_FORMATS[format] || format; - if (isString(date)) { - if (NUMBER_STRING.test(date)) { - date = int(date); - } else { - date = jsonStringToDate(date); - } - } - - if (isNumber(date)) { - date = new Date(date); - } - - if (!isDate(date)) { - return date; - } - - while(format) { - match = DATE_FORMATS_SPLIT.exec(format); - if (match) { - parts = concat(parts, match, 1); - format = parts.pop(); - } else { - parts.push(format); - format = null; - } - } - - forEach(parts, function(value){ - fn = DATE_FORMATS[value]; - text += fn ? fn(date, $locale.DATETIME_FORMATS) - : value.replace(/(^'|'$)/g, '').replace(/''/g, "'"); - }); - - return text; - }; -} - - -/** - * @ngdoc filter - * @name ng.filter:json - * @function - * - * @description - * Allows you to convert a JavaScript object into JSON string. - * - * This filter is mostly useful for debugging. When using the double curly {{value}} notation - * the binding is automatically converted to JSON. - * - * @param {*} object Any JavaScript object (including arrays and primitive types) to filter. - * @returns {string} JSON string. - * - * - * @example: - <doc:example> - <doc:source> - <pre>{{ {'name':'value'} | json }}</pre> - </doc:source> - <doc:scenario> - it('should jsonify filtered objects', function() { - expect(binding("{'name':'value'}")).toMatch(/\{\n "name": ?"value"\n}/); - }); - </doc:scenario> - </doc:example> - * - */ -function jsonFilter() { - return function(object) { - return toJson(object, true); - }; -} - - -/** - * @ngdoc filter - * @name ng.filter:lowercase - * @function - * @description - * Converts string to lowercase. - * @see angular.lowercase - */ -var lowercaseFilter = valueFn(lowercase); - - -/** - * @ngdoc filter - * @name ng.filter:uppercase - * @function - * @description - * Converts string to uppercase. - * @see angular.uppercase - */ -var uppercaseFilter = valueFn(uppercase); - -/** - * @ngdoc function - * @name ng.filter:limitTo - * @function - * - * @description - * Creates a new array containing only a specified number of elements in an array. The elements - * are taken from either the beginning or the end of the source array, as specified by the - * value and sign (positive or negative) of `limit`. - * - * Note: This function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more information about Angular arrays. - * - * @param {Array} array Source array to be limited. - * @param {string|Number} limit The length of the returned array. If the `limit` number is - * positive, `limit` number of items from the beginning of the source array are copied. - * If the number is negative, `limit` number of items from the end of the source array are - * copied. The `limit` will be trimmed if it exceeds `array.length` - * @returns {Array} A new sub-array of length `limit` or less if input array had less than `limit` - * elements. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.numbers = [1,2,3,4,5,6,7,8,9]; - $scope.limit = 3; - } - </script> - <div ng-controller="Ctrl"> - Limit {{numbers}} to: <input type="integer" ng-model="limit"> - <p>Output: {{ numbers | limitTo:limit }}</p> - </div> - </doc:source> - <doc:scenario> - it('should limit the numer array to first three items', function() { - expect(element('.doc-example-live input[ng-model=limit]').val()).toBe('3'); - expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3]'); - }); - - it('should update the output when -3 is entered', function() { - input('limit').enter(-3); - expect(binding('numbers | limitTo:limit')).toEqual('[7,8,9]'); - }); - - it('should not exceed the maximum size of input array', function() { - input('limit').enter(100); - expect(binding('numbers | limitTo:limit')).toEqual('[1,2,3,4,5,6,7,8,9]'); - }); - </doc:scenario> - </doc:example> - */ -function limitToFilter(){ - return function(array, limit) { - if (!(array instanceof Array)) return array; - limit = int(limit); - var out = [], - i, n; - - // check that array is iterable - if (!array || !(array instanceof Array)) - return out; - - // if abs(limit) exceeds maximum length, trim it - if (limit > array.length) - limit = array.length; - else if (limit < -array.length) - limit = -array.length; - - if (limit > 0) { - i = 0; - n = limit; - } else { - i = array.length + limit; - n = array.length; - } - - for (; i<n; i++) { - out.push(array[i]); - } - - return out; - } -} - -/** - * @ngdoc function - * @name ng.filter:orderBy - * @function - * - * @description - * Orders a specified `array` by the `expression` predicate. - * - * Note: this function is used to augment the `Array` type in Angular expressions. See - * {@link ng.$filter} for more informaton about Angular arrays. - * - * @param {Array} array The array to sort. - * @param {function(*)|string|Array.<(function(*)|string)>} expression A predicate to be - * used by the comparator to determine the order of elements. - * - * Can be one of: - * - * - `function`: Getter function. The result of this function will be sorted using the - * `<`, `=`, `>` operator. - * - `string`: An Angular expression which evaluates to an object to order by, such as 'name' - * to sort by a property called 'name'. Optionally prefixed with `+` or `-` to control - * ascending or descending sort order (for example, +name or -name). - * - `Array`: An array of function or string predicates. The first predicate in the array - * is used for sorting, but when two items are equivalent, the next predicate is used. - * - * @param {boolean=} reverse Reverse the order the array. - * @returns {Array} Sorted copy of the source array. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.friends = - [{name:'John', phone:'555-1212', age:10}, - {name:'Mary', phone:'555-9876', age:19}, - {name:'Mike', phone:'555-4321', age:21}, - {name:'Adam', phone:'555-5678', age:35}, - {name:'Julie', phone:'555-8765', age:29}] - $scope.predicate = '-age'; - } - </script> - <div ng-controller="Ctrl"> - <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre> - <hr/> - [ <a href="" ng-click="predicate=''">unsorted</a> ] - <table class="friend"> - <tr> - <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a> - (<a href ng-click="predicate = '-name'; reverse=false">^</a>)</th> - <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th> - <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th> - <tr> - <tr ng-repeat="friend in friends | orderBy:predicate:reverse"> - <td>{{friend.name}}</td> - <td>{{friend.phone}}</td> - <td>{{friend.age}}</td> - <tr> - </table> - </div> - </doc:source> - <doc:scenario> - it('should be reverse ordered by aged', function() { - expect(binding('predicate')).toBe('-age'); - expect(repeater('table.friend', 'friend in friends').column('friend.age')). - toEqual(['35', '29', '21', '19', '10']); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Adam', 'Julie', 'Mike', 'Mary', 'John']); - }); - - it('should reorder the table when user selects different predicate', function() { - element('.doc-example-live a:contains("Name")').click(); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Adam', 'John', 'Julie', 'Mary', 'Mike']); - expect(repeater('table.friend', 'friend in friends').column('friend.age')). - toEqual(['35', '10', '29', '19', '21']); - - element('.doc-example-live a:contains("Phone")').click(); - expect(repeater('table.friend', 'friend in friends').column('friend.phone')). - toEqual(['555-9876', '555-8765', '555-5678', '555-4321', '555-1212']); - expect(repeater('table.friend', 'friend in friends').column('friend.name')). - toEqual(['Mary', 'Julie', 'Adam', 'Mike', 'John']); - }); - </doc:scenario> - </doc:example> - */ -orderByFilter.$inject = ['$parse']; -function orderByFilter($parse){ - return function(array, sortPredicate, reverseOrder) { - if (!(array instanceof Array)) return array; - if (!sortPredicate) return array; - sortPredicate = isArray(sortPredicate) ? sortPredicate: [sortPredicate]; - sortPredicate = map(sortPredicate, function(predicate){ - var descending = false, get = predicate || identity; - if (isString(predicate)) { - if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) { - descending = predicate.charAt(0) == '-'; - predicate = predicate.substring(1); - } - get = $parse(predicate); - } - return reverseComparator(function(a,b){ - return compare(get(a),get(b)); - }, descending); - }); - var arrayCopy = []; - for ( var i = 0; i < array.length; i++) { arrayCopy.push(array[i]); } - return arrayCopy.sort(reverseComparator(comparator, reverseOrder)); - - function comparator(o1, o2){ - for ( var i = 0; i < sortPredicate.length; i++) { - var comp = sortPredicate[i](o1, o2); - if (comp !== 0) return comp; - } - return 0; - } - function reverseComparator(comp, descending) { - return toBoolean(descending) - ? function(a,b){return comp(b,a);} - : comp; - } - function compare(v1, v2){ - var t1 = typeof v1; - var t2 = typeof v2; - if (t1 == t2) { - if (t1 == "string") v1 = v1.toLowerCase(); - if (t1 == "string") v2 = v2.toLowerCase(); - if (v1 === v2) return 0; - return v1 < v2 ? -1 : 1; - } else { - return t1 < t2 ? -1 : 1; - } - } - } -} - -function ngDirective(directive) { - if (isFunction(directive)) { - directive = { - link: directive - } - } - directive.restrict = directive.restrict || 'AC'; - return valueFn(directive); -} - -/** - * @ngdoc directive - * @name ng.directive:a - * @restrict E - * - * @description - * Modifies the default behavior of html A tag, so that the default action is prevented when href - * attribute is empty. - * - * The reasoning for this change is to allow easy creation of action links with `ngClick` directive - * without changing the location or causing page reloads, e.g.: - * <a href="" ng-click="model.$save()">Save</a> - */ -var htmlAnchorDirective = valueFn({ - restrict: 'E', - compile: function(element, attr) { - // turn <a href ng-click="..">link</a> into a link in IE - // but only if it doesn't have name attribute, in which case it's an anchor - if (!attr.href) { - attr.$set('href', ''); - } - - return function(scope, element) { - element.bind('click', function(event){ - // if we have no href url, then don't navigate anywhere. - if (!element.attr('href')) { - event.preventDefault(); - return false; // Needed for opera - } - }); - } - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngHref - * @restrict A - * - * @description - * Using Angular markup like {{hash}} in an href attribute makes - * the page open to a wrong URL, if the user clicks that link before - * angular has a chance to replace the {{hash}} with actual URL, the - * link will be broken and will most likely return a 404 error. - * The `ngHref` directive solves this problem. - * - * The buggy way to write it: - * <pre> - * <a href="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * The correct way to write it: - * <pre> - * <a ng-href="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * @element A - * @param {template} ngHref any string which can contain `{{}}` markup. - * - * @example - * This example uses `link` variable inside `href` attribute: - <doc:example> - <doc:source> - <input ng-model="value" /><br /> - <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br /> - <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br /> - <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br /> - <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br /> - <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br /> - <a id="link-6" ng-href="{{value}}">link</a> (link, change location) - </doc:source> - <doc:scenario> - it('should execute ng-click but not reload when href without value', function() { - element('#link-1').click(); - expect(input('value').val()).toEqual('1'); - expect(element('#link-1').attr('href')).toBe(""); - }); - - it('should execute ng-click but not reload when href empty string', function() { - element('#link-2').click(); - expect(input('value').val()).toEqual('2'); - expect(element('#link-2').attr('href')).toBe(""); - }); - - it('should execute ng-click and change url when ng-href specified', function() { - expect(element('#link-3').attr('href')).toBe("/123"); - - element('#link-3').click(); - expect(browser().window().path()).toEqual('/123'); - }); - - it('should execute ng-click but not reload when href empty string and name specified', function() { - element('#link-4').click(); - expect(input('value').val()).toEqual('4'); - expect(element('#link-4').attr('href')).toBe(''); - }); - - it('should execute ng-click but not reload when no href but name specified', function() { - element('#link-5').click(); - expect(input('value').val()).toEqual('5'); - expect(element('#link-5').attr('href')).toBe(''); - }); - - it('should only change url when only ng-href', function() { - input('value').enter('6'); - expect(element('#link-6').attr('href')).toBe('6'); - - element('#link-6').click(); - expect(browser().location().url()).toEqual('/6'); - }); - </doc:scenario> - </doc:example> - */ - -/** - * @ngdoc directive - * @name ng.directive:ngSrc - * @restrict A - * - * @description - * Using Angular markup like `{{hash}}` in a `src` attribute doesn't - * work right: The browser will fetch from the URL with the literal - * text `{{hash}}` until Angular replaces the expression inside - * `{{hash}}`. The `ngSrc` directive solves this problem. - * - * The buggy way to write it: - * <pre> - * <img src="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * The correct way to write it: - * <pre> - * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/> - * </pre> - * - * @element IMG - * @param {template} ngSrc any string which can contain `{{}}` markup. - */ - -/** - * @ngdoc directive - * @name ng.directive:ngDisabled - * @restrict A - * - * @description - * - * The following markup will make the button enabled on Chrome/Firefox but not on IE8 and older IEs: - * <pre> - * <div ng-init="scope = { isDisabled: false }"> - * <button disabled="{{scope.isDisabled}}">Disabled</button> - * </div> - * </pre> - * - * The HTML specs do not require browsers to preserve the special attributes such as disabled. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngDisabled` directive. - * - * @example - <doc:example> - <doc:source> - Click me to toggle: <input type="checkbox" ng-model="checked"><br/> - <button ng-model="button" ng-disabled="checked">Button</button> - </doc:source> - <doc:scenario> - it('should toggle button', function() { - expect(element('.doc-example-live :button').prop('disabled')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live :button').prop('disabled')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element INPUT - * @param {expression} ngDisabled Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngChecked - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as checked. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngChecked` directive. - * @example - <doc:example> - <doc:source> - Check me to check both: <input type="checkbox" ng-model="master"><br/> - <input id="checkSlave" type="checkbox" ng-checked="master"> - </doc:source> - <doc:scenario> - it('should check both checkBoxes', function() { - expect(element('.doc-example-live #checkSlave').prop('checked')).toBeFalsy(); - input('master').check(); - expect(element('.doc-example-live #checkSlave').prop('checked')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element INPUT - * @param {expression} ngChecked Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMultiple - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as multiple. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngMultiple` directive. - * - * @example - <doc:example> - <doc:source> - Check me check multiple: <input type="checkbox" ng-model="checked"><br/> - <select id="select" ng-multiple="checked"> - <option>Misko</option> - <option>Igor</option> - <option>Vojta</option> - <option>Di</option> - </select> - </doc:source> - <doc:scenario> - it('should toggle multiple', function() { - expect(element('.doc-example-live #select').prop('multiple')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live #select').prop('multiple')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element SELECT - * @param {expression} ngMultiple Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngReadonly - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as readonly. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduce the `ngReadonly` directive. - * @example - <doc:example> - <doc:source> - Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/> - <input type="text" ng-readonly="checked" value="I'm Angular"/> - </doc:source> - <doc:scenario> - it('should toggle readonly attr', function() { - expect(element('.doc-example-live :text').prop('readonly')).toBeFalsy(); - input('checked').check(); - expect(element('.doc-example-live :text').prop('readonly')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element INPUT - * @param {string} expression Angular expression that will be evaluated. - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngSelected - * @restrict A - * - * @description - * The HTML specs do not require browsers to preserve the special attributes such as selected. - * (The presence of them means true and absence means false) - * This prevents the angular compiler from correctly retrieving the binding expression. - * To solve this problem, we introduced the `ngSelected` directive. - * @example - <doc:example> - <doc:source> - Check me to select: <input type="checkbox" ng-model="selected"><br/> - <select> - <option>Hello!</option> - <option id="greet" ng-selected="selected">Greetings!</option> - </select> - </doc:source> - <doc:scenario> - it('should select Greetings!', function() { - expect(element('.doc-example-live #greet').prop('selected')).toBeFalsy(); - input('selected').check(); - expect(element('.doc-example-live #greet').prop('selected')).toBeTruthy(); - }); - </doc:scenario> - </doc:example> - * - * @element OPTION - * @param {string} expression Angular expression that will be evaluated. - */ - - -var ngAttributeAliasDirectives = {}; - - -// boolean attrs are evaluated -forEach(BOOLEAN_ATTR, function(propName, attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - priority: 100, - compile: function() { - return function(scope, element, attr) { - scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) { - attr.$set(attrName, !!value); - }); - }; - } - }; - }; -}); - - -// ng-src, ng-href are interpolated -forEach(['src', 'href'], function(attrName) { - var normalized = directiveNormalize('ng-' + attrName); - ngAttributeAliasDirectives[normalized] = function() { - return { - priority: 99, // it needs to run after the attributes are interpolated - link: function(scope, element, attr) { - attr.$observe(normalized, function(value) { - if (!value) - return; - - attr.$set(attrName, value); - - // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist - // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need - // to set the property as well to achieve the desired effect - if (msie) element.prop(attrName, value); - }); - } - }; - }; -}); - -var nullFormCtrl = { - $addControl: noop, - $removeControl: noop, - $setValidity: noop, - $setDirty: noop -}; - -/** - * @ngdoc object - * @name ng.directive:form.FormController - * - * @property {boolean} $pristine True if user has not interacted with the form yet. - * @property {boolean} $dirty True if user has already interacted with the form. - * @property {boolean} $valid True if all of the containg forms and controls are valid. - * @property {boolean} $invalid True if at least one containing control or form is invalid. - * - * @property {Object} $error Is an object hash, containing references to all invalid controls or - * forms, where: - * - * - keys are validation tokens (error names) — such as `REQUIRED`, `URL` or `EMAIL`), - * - values are arrays of controls or forms that are invalid with given error. - * - * @description - * `FormController` keeps track of all its controls and nested forms as well as state of them, - * such as being valid/invalid or dirty/pristine. - * - * Each {@link ng.directive:form form} directive creates an instance - * of `FormController`. - * - */ -//asks for $scope to fool the BC controller module -FormController.$inject = ['$element', '$attrs', '$scope']; -function FormController(element, attrs) { - var form = this, - parentForm = element.parent().controller('form') || nullFormCtrl, - invalidCount = 0, // used to easily determine if we are valid - errors = form.$error = {}; - - // init state - form.$name = attrs.name; - form.$dirty = false; - form.$pristine = true; - form.$valid = true; - form.$invalid = false; - - parentForm.$addControl(form); - - // Setup initial state of the control - element.addClass(PRISTINE_CLASS); - toggleValidCss(true); - - // convenience method for easy toggling of classes - function toggleValidCss(isValid, validationErrorKey) { - validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; - element. - removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). - addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); - } - - form.$addControl = function(control) { - if (control.$name && !form.hasOwnProperty(control.$name)) { - form[control.$name] = control; - } - }; - - form.$removeControl = function(control) { - if (control.$name && form[control.$name] === control) { - delete form[control.$name]; - } - forEach(errors, function(queue, validationToken) { - form.$setValidity(validationToken, true, control); - }); - }; - - form.$setValidity = function(validationToken, isValid, control) { - var queue = errors[validationToken]; - - if (isValid) { - if (queue) { - arrayRemove(queue, control); - if (!queue.length) { - invalidCount--; - if (!invalidCount) { - toggleValidCss(isValid); - form.$valid = true; - form.$invalid = false; - } - errors[validationToken] = false; - toggleValidCss(true, validationToken); - parentForm.$setValidity(validationToken, true, form); - } - } - - } else { - if (!invalidCount) { - toggleValidCss(isValid); - } - if (queue) { - if (includes(queue, control)) return; - } else { - errors[validationToken] = queue = []; - invalidCount++; - toggleValidCss(false, validationToken); - parentForm.$setValidity(validationToken, false, form); - } - queue.push(control); - - form.$valid = false; - form.$invalid = true; - } - }; - - form.$setDirty = function() { - element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); - form.$dirty = true; - form.$pristine = false; - parentForm.$setDirty(); - }; - -} - - -/** - * @ngdoc directive - * @name ng.directive:ngForm - * @restrict EAC - * - * @description - * Nestable alias of {@link ng.directive:form `form`} directive. HTML - * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a - * sub-group of controls needs to be determined. - * - * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - */ - - /** - * @ngdoc directive - * @name ng.directive:form - * @restrict E - * - * @description - * Directive that instantiates - * {@link ng.directive:form.FormController FormController}. - * - * If `name` attribute is specified, the form controller is published onto the current scope under - * this name. - * - * # Alias: {@link ng.directive:ngForm `ngForm`} - * - * In angular forms can be nested. This means that the outer form is valid when all of the child - * forms are valid as well. However browsers do not allow nesting of `<form>` elements, for this - * reason angular provides {@link ng.directive:ngForm `ngForm`} alias - * which behaves identical to `<form>` but allows form nesting. - * - * - * # CSS classes - * - `ng-valid` Is set if the form is valid. - * - `ng-invalid` Is set if the form is invalid. - * - `ng-pristine` Is set if the form is pristine. - * - `ng-dirty` Is set if the form is dirty. - * - * - * # Submitting a form and preventing default action - * - * Since the role of forms in client-side Angular applications is different than in classical - * roundtrip apps, it is desirable for the browser not to translate the form submission into a full - * page reload that sends the data to the server. Instead some javascript logic should be triggered - * to handle the form submission in application specific way. - * - * For this reason, Angular prevents the default action (form submission to the server) unless the - * `<form>` element has an `action` attribute specified. - * - * You can use one of the following two ways to specify what javascript method should be called when - * a form is submitted: - * - * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element - * - {@link ng.directive:ngClick ngClick} directive on the first - * button or input field of type submit (input[type=submit]) - * - * To prevent double execution of the handler, use only one of ngSubmit or ngClick directives. This - * is because of the following form submission rules coming from the html spec: - * - * - If a form has only one input field then hitting enter in this field triggers form submit - * (`ngSubmit`) - * - if a form has has 2+ input fields and no buttons or input[type=submit] then hitting enter - * doesn't trigger submit - * - if a form has one or more input fields and one or more buttons or input[type=submit] then - * hitting enter in any of the input fields will trigger the click handler on the *first* button or - * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`) - * - * @param {string=} name Name of the form. If specified, the form controller will be published into - * related scope, under this name. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.userType = 'guest'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - userType: <input name="input" ng-model="userType" required> - <span class="error" ng-show="myForm.input.$error.REQUIRED">Required!</span><br> - <tt>userType = {{userType}}</tt><br> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br> - <tt>myForm.$error.REQUIRED = {{!!myForm.$error.REQUIRED}}</tt><br> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('userType')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('userType').enter(''); - expect(binding('userType')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ -var formDirectiveFactory = function(isNgForm) { - return ['$timeout', function($timeout) { - var formDirective = { - name: 'form', - restrict: 'E', - controller: FormController, - compile: function() { - return { - pre: function(scope, formElement, attr, controller) { - if (!attr.action) { - // we can't use jq events because if a form is destroyed during submission the default - // action is not prevented. see #1238 - // - // IE 9 is not affected because it doesn't fire a submit event and try to do a full - // page reload if the form was destroyed by submission of the form via a click handler - // on a button in the form. Looks like an IE9 specific bug. - var preventDefaultListener = function(event) { - event.preventDefault - ? event.preventDefault() - : event.returnValue = false; // IE - }; - - addEventListenerFn(formElement[0], 'submit', preventDefaultListener); - - // unregister the preventDefault listener so that we don't not leak memory but in a - // way that will achieve the prevention of the default action. - formElement.bind('$destroy', function() { - $timeout(function() { - removeEventListenerFn(formElement[0], 'submit', preventDefaultListener); - }, 0, false); - }); - } - - var parentFormCtrl = formElement.parent().controller('form'), - alias = attr.name || attr.ngForm; - - if (alias) { - scope[alias] = controller; - } - if (parentFormCtrl) { - formElement.bind('$destroy', function() { - parentFormCtrl.$removeControl(controller); - if (alias) { - scope[alias] = undefined; - } - extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards - }); - } - } - }; - } - }; - - return isNgForm ? extend(copy(formDirective), {restrict: 'EAC'}) : formDirective; - }]; -}; - -var formDirective = formDirectiveFactory(); -var ngFormDirective = formDirectiveFactory(true); - -var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/; -var EMAIL_REGEXP = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/; -var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/; - -var inputType = { - - /** - * @ngdoc inputType - * @name ng.directive:input.text - * - * @description - * Standard HTML text input with angular data binding. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Adds `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.text = 'guest'; - $scope.word = /^\w*$/; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Single word: <input type="text" name="input" ng-model="text" - ng-pattern="word" required> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.pattern"> - Single word only!</span> - - <tt>text = {{text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('text')).toEqual('guest'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if multi word', function() { - input('text').enter('hello world'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'text': textInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.number - * - * @description - * Text input with number validation and transformation. Sets the `number` validation - * error if not a valid number. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} min Sets the `min` validation error key if the value entered is less then `min`. - * @param {string=} max Sets the `max` validation error key if the value entered is greater then `min`. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.value = 12; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Number: <input type="number" name="input" ng-model="value" - min="0" max="99" required> - <span class="error" ng-show="myForm.list.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.list.$error.number"> - Not valid number!</span> - <tt>value = {{value}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('value')).toEqual('12'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('value').enter(''); - expect(binding('value')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if over max', function() { - input('value').enter('123'); - expect(binding('value')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'number': numberInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.url - * - * @description - * Text input with URL validation. Sets the `url` validation error key if the content is not a - * valid URL. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.text = 'http://google.com'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - URL: <input type="url" name="input" ng-model="text" required> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.url"> - Not valid url!</span> - <tt>text = {{text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('text')).toEqual('http://google.com'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if not url', function() { - input('text').enter('xxx'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'url': urlInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.email - * - * @description - * Text input with email validation. Sets the `email` validation error key if not a valid email - * address. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.text = 'me@example.com'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Email: <input type="email" name="input" ng-model="text" required> - <span class="error" ng-show="myForm.input.$error.required"> - Required!</span> - <span class="error" ng-show="myForm.input.$error.email"> - Not valid email!</span> - <tt>text = {{text}}</tt><br/> - <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/> - <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('text')).toEqual('me@example.com'); - expect(binding('myForm.input.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('text').enter(''); - expect(binding('text')).toEqual(''); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - - it('should be invalid if not email', function() { - input('text').enter('xxx'); - expect(binding('myForm.input.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ - 'email': emailInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.radio - * - * @description - * HTML radio button. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string} value The value to which the expression should be set when selected. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.color = 'blue'; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - <input type="radio" ng-model="color" value="red"> Red <br/> - <input type="radio" ng-model="color" value="green"> Green <br/> - <input type="radio" ng-model="color" value="blue"> Blue <br/> - <tt>color = {{color}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should change state', function() { - expect(binding('color')).toEqual('blue'); - - input('color').select('red'); - expect(binding('color')).toEqual('red'); - }); - </doc:scenario> - </doc:example> - */ - 'radio': radioInputType, - - - /** - * @ngdoc inputType - * @name ng.directive:input.checkbox - * - * @description - * HTML checkbox. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} ngTrueValue The value to which the expression should be set when selected. - * @param {string=} ngFalseValue The value to which the expression should be set when not selected. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.value1 = true; - $scope.value2 = 'YES' - } - </script> - <form name="myForm" ng-controller="Ctrl"> - Value1: <input type="checkbox" ng-model="value1"> <br/> - Value2: <input type="checkbox" ng-model="value2" - ng-true-value="YES" ng-false-value="NO"> <br/> - <tt>value1 = {{value1}}</tt><br/> - <tt>value2 = {{value2}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should change state', function() { - expect(binding('value1')).toEqual('true'); - expect(binding('value2')).toEqual('YES'); - - input('value1').check(); - input('value2').check(); - expect(binding('value1')).toEqual('false'); - expect(binding('value2')).toEqual('NO'); - }); - </doc:scenario> - </doc:example> - */ - 'checkbox': checkboxInputType, - - 'hidden': noop, - 'button': noop, - 'submit': noop, - 'reset': noop -}; - - -function isEmpty(value) { - return isUndefined(value) || value === '' || value === null || value !== value; -} - - -function textInputType(scope, element, attr, ctrl, $sniffer, $browser) { - - var listener = function() { - var value = trim(element.val()); - - if (ctrl.$viewValue !== value) { - scope.$apply(function() { - ctrl.$setViewValue(value); - }); - } - }; - - // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the - // input event on backspace, delete or cut - if ($sniffer.hasEvent('input')) { - element.bind('input', listener); - } else { - var timeout; - - element.bind('keydown', function(event) { - var key = event.keyCode; - - // ignore - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return; - - if (!timeout) { - timeout = $browser.defer(function() { - listener(); - timeout = null; - }); - } - }); - - // if user paste into input using mouse, we need "change" event to catch it - element.bind('change', listener); - } - - - ctrl.$render = function() { - element.val(isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue); - }; - - // pattern validator - var pattern = attr.ngPattern, - patternValidator; - - var validate = function(regexp, value) { - if (isEmpty(value) || regexp.test(value)) { - ctrl.$setValidity('pattern', true); - return value; - } else { - ctrl.$setValidity('pattern', false); - return undefined; - } - }; - - if (pattern) { - if (pattern.match(/^\/(.*)\/$/)) { - pattern = new RegExp(pattern.substr(1, pattern.length - 2)); - patternValidator = function(value) { - return validate(pattern, value) - }; - } else { - patternValidator = function(value) { - var patternObj = scope.$eval(pattern); - - if (!patternObj || !patternObj.test) { - throw new Error('Expected ' + pattern + ' to be a RegExp but was ' + patternObj); - } - return validate(patternObj, value); - }; - } - - ctrl.$formatters.push(patternValidator); - ctrl.$parsers.push(patternValidator); - } - - // min length validator - if (attr.ngMinlength) { - var minlength = int(attr.ngMinlength); - var minLengthValidator = function(value) { - if (!isEmpty(value) && value.length < minlength) { - ctrl.$setValidity('minlength', false); - return undefined; - } else { - ctrl.$setValidity('minlength', true); - return value; - } - }; - - ctrl.$parsers.push(minLengthValidator); - ctrl.$formatters.push(minLengthValidator); - } - - // max length validator - if (attr.ngMaxlength) { - var maxlength = int(attr.ngMaxlength); - var maxLengthValidator = function(value) { - if (!isEmpty(value) && value.length > maxlength) { - ctrl.$setValidity('maxlength', false); - return undefined; - } else { - ctrl.$setValidity('maxlength', true); - return value; - } - }; - - ctrl.$parsers.push(maxLengthValidator); - ctrl.$formatters.push(maxLengthValidator); - } -} - -function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) { - textInputType(scope, element, attr, ctrl, $sniffer, $browser); - - ctrl.$parsers.push(function(value) { - var empty = isEmpty(value); - if (empty || NUMBER_REGEXP.test(value)) { - ctrl.$setValidity('number', true); - return value === '' ? null : (empty ? value : parseFloat(value)); - } else { - ctrl.$setValidity('number', false); - return undefined; - } - }); - - ctrl.$formatters.push(function(value) { - return isEmpty(value) ? '' : '' + value; - }); - - if (attr.min) { - var min = parseFloat(attr.min); - var minValidator = function(value) { - if (!isEmpty(value) && value < min) { - ctrl.$setValidity('min', false); - return undefined; - } else { - ctrl.$setValidity('min', true); - return value; - } - }; - - ctrl.$parsers.push(minValidator); - ctrl.$formatters.push(minValidator); - } - - if (attr.max) { - var max = parseFloat(attr.max); - var maxValidator = function(value) { - if (!isEmpty(value) && value > max) { - ctrl.$setValidity('max', false); - return undefined; - } else { - ctrl.$setValidity('max', true); - return value; - } - }; - - ctrl.$parsers.push(maxValidator); - ctrl.$formatters.push(maxValidator); - } - - ctrl.$formatters.push(function(value) { - - if (isEmpty(value) || isNumber(value)) { - ctrl.$setValidity('number', true); - return value; - } else { - ctrl.$setValidity('number', false); - return undefined; - } - }); -} - -function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) { - textInputType(scope, element, attr, ctrl, $sniffer, $browser); - - var urlValidator = function(value) { - if (isEmpty(value) || URL_REGEXP.test(value)) { - ctrl.$setValidity('url', true); - return value; - } else { - ctrl.$setValidity('url', false); - return undefined; - } - }; - - ctrl.$formatters.push(urlValidator); - ctrl.$parsers.push(urlValidator); -} - -function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) { - textInputType(scope, element, attr, ctrl, $sniffer, $browser); - - var emailValidator = function(value) { - if (isEmpty(value) || EMAIL_REGEXP.test(value)) { - ctrl.$setValidity('email', true); - return value; - } else { - ctrl.$setValidity('email', false); - return undefined; - } - }; - - ctrl.$formatters.push(emailValidator); - ctrl.$parsers.push(emailValidator); -} - -function radioInputType(scope, element, attr, ctrl) { - // make the name unique, if not defined - if (isUndefined(attr.name)) { - element.attr('name', nextUid()); - } - - element.bind('click', function() { - if (element[0].checked) { - scope.$apply(function() { - ctrl.$setViewValue(attr.value); - }); - } - }); - - ctrl.$render = function() { - var value = attr.value; - element[0].checked = (value == ctrl.$viewValue); - }; - - attr.$observe('value', ctrl.$render); -} - -function checkboxInputType(scope, element, attr, ctrl) { - var trueValue = attr.ngTrueValue, - falseValue = attr.ngFalseValue; - - if (!isString(trueValue)) trueValue = true; - if (!isString(falseValue)) falseValue = false; - - element.bind('click', function() { - scope.$apply(function() { - ctrl.$setViewValue(element[0].checked); - }); - }); - - ctrl.$render = function() { - element[0].checked = ctrl.$viewValue; - }; - - ctrl.$formatters.push(function(value) { - return value === trueValue; - }); - - ctrl.$parsers.push(function(value) { - return value ? trueValue : falseValue; - }); -} - - -/** - * @ngdoc directive - * @name ng.directive:textarea - * @restrict E - * - * @description - * HTML textarea element control with angular data-binding. The data-binding and validation - * properties of this element are exactly the same as those of the - * {@link ng.directive:input input element}. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - */ - - -/** - * @ngdoc directive - * @name ng.directive:input - * @restrict E - * - * @description - * HTML input element control with angular data-binding. Input control follows HTML5 input types - * and polyfills the HTML5 validation behavior for older browsers. - * - * @param {string} ngModel Assignable angular expression to data-bind to. - * @param {string=} name Property name of the form under which the control is published. - * @param {string=} required Sets `required` validation error key if the value is not entered. - * @param {boolean=} ngRequired Sets `required` attribute if set to true - * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than - * minlength. - * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than - * maxlength. - * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the - * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for - * patterns defined as scope expressions. - * @param {string=} ngChange Angular expression to be executed when input changes due to user - * interaction with the input element. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.user = {name: 'guest', last: 'visitor'}; - } - </script> - <div ng-controller="Ctrl"> - <form name="myForm"> - User name: <input type="text" name="userName" ng-model="user.name" required> - <span class="error" ng-show="myForm.userName.$error.required"> - Required!</span><br> - Last name: <input type="text" name="lastName" ng-model="user.last" - ng-minlength="3" ng-maxlength="10"> - <span class="error" ng-show="myForm.lastName.$error.minlength"> - Too short!</span> - <span class="error" ng-show="myForm.lastName.$error.maxlength"> - Too long!</span><br> - </form> - <hr> - <tt>user = {{user}}</tt><br/> - <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br> - <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br> - <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br> - <tt>myForm.userName.$error = {{myForm.lastName.$error}}</tt><br> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br> - <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br> - <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br> - </div> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('user')).toEqual('{"name":"guest","last":"visitor"}'); - expect(binding('myForm.userName.$valid')).toEqual('true'); - expect(binding('myForm.$valid')).toEqual('true'); - }); - - it('should be invalid if empty when required', function() { - input('user.name').enter(''); - expect(binding('user')).toEqual('{"last":"visitor"}'); - expect(binding('myForm.userName.$valid')).toEqual('false'); - expect(binding('myForm.$valid')).toEqual('false'); - }); - - it('should be valid if empty when min length is set', function() { - input('user.last').enter(''); - expect(binding('user')).toEqual('{"name":"guest","last":""}'); - expect(binding('myForm.lastName.$valid')).toEqual('true'); - expect(binding('myForm.$valid')).toEqual('true'); - }); - - it('should be invalid if less than required min length', function() { - input('user.last').enter('xx'); - expect(binding('user')).toEqual('{"name":"guest"}'); - expect(binding('myForm.lastName.$valid')).toEqual('false'); - expect(binding('myForm.lastName.$error')).toMatch(/minlength/); - expect(binding('myForm.$valid')).toEqual('false'); - }); - - it('should be invalid if longer than max length', function() { - input('user.last').enter('some ridiculously long name'); - expect(binding('user')) - .toEqual('{"name":"guest"}'); - expect(binding('myForm.lastName.$valid')).toEqual('false'); - expect(binding('myForm.lastName.$error')).toMatch(/maxlength/); - expect(binding('myForm.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ -var inputDirective = ['$browser', '$sniffer', function($browser, $sniffer) { - return { - restrict: 'E', - require: '?ngModel', - link: function(scope, element, attr, ctrl) { - if (ctrl) { - (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrl, $sniffer, - $browser); - } - } - }; -}]; - -var VALID_CLASS = 'ng-valid', - INVALID_CLASS = 'ng-invalid', - PRISTINE_CLASS = 'ng-pristine', - DIRTY_CLASS = 'ng-dirty'; - -/** - * @ngdoc object - * @name ng.directive:ngModel.NgModelController - * - * @property {string} $viewValue Actual string value in the view. - * @property {*} $modelValue The value in the model, that the control is bound to. - * @property {Array.<Function>} $parsers Whenever the control reads value from the DOM, it executes - * all of these functions to sanitize / convert the value as well as validate. - * - * @property {Array.<Function>} $formatters Whenever the model value changes, it executes all of - * these functions to convert the value as well as validate. - * - * @property {Object} $error An bject hash with all errors as keys. - * - * @property {boolean} $pristine True if user has not interacted with the control yet. - * @property {boolean} $dirty True if user has already interacted with the control. - * @property {boolean} $valid True if there is no error. - * @property {boolean} $invalid True if at least one error on the control. - * - * @description - * - * `NgModelController` provides API for the `ng-model` directive. The controller contains - * services for data-binding, validation, CSS update, value formatting and parsing. It - * specifically does not contain any logic which deals with DOM rendering or listening to - * DOM events. The `NgModelController` is meant to be extended by other directives where, the - * directive provides DOM manipulation and the `NgModelController` provides the data-binding. - * - * This example shows how to use `NgModelController` with a custom control to achieve - * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`) - * collaborate together to achieve the desired result. - * - * <example module="customControl"> - <file name="style.css"> - [contenteditable] { - border: 1px solid black; - background-color: white; - min-height: 20px; - } - - .ng-invalid { - border: 1px solid red; - } - - </file> - <file name="script.js"> - angular.module('customControl', []). - directive('contenteditable', function() { - return { - restrict: 'A', // only activate on element attribute - require: '?ngModel', // get a hold of NgModelController - link: function(scope, element, attrs, ngModel) { - if(!ngModel) return; // do nothing if no ng-model - - // Specify how UI should be updated - ngModel.$render = function() { - element.html(ngModel.$viewValue || ''); - }; - - // Listen for change events to enable binding - element.bind('blur keyup change', function() { - scope.$apply(read); - }); - read(); // initialize - - // Write data to the model - function read() { - ngModel.$setViewValue(element.html()); - } - } - }; - }); - </file> - <file name="index.html"> - <form name="myForm"> - <div contenteditable - name="myWidget" ng-model="userContent" - required>Change me!</div> - <span ng-show="myForm.myWidget.$error.required">Required!</span> - <hr> - <textarea ng-model="userContent"></textarea> - </form> - </file> - <file name="scenario.js"> - it('should data-bind and become invalid', function() { - var contentEditable = element('[contenteditable]'); - - expect(contentEditable.text()).toEqual('Change me!'); - input('userContent').enter(''); - expect(contentEditable.text()).toEqual(''); - expect(contentEditable.prop('className')).toMatch(/ng-invalid-required/); - }); - </file> - * </example> - * - */ -var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', - function($scope, $exceptionHandler, $attr, $element, $parse) { - this.$viewValue = Number.NaN; - this.$modelValue = Number.NaN; - this.$parsers = []; - this.$formatters = []; - this.$viewChangeListeners = []; - this.$pristine = true; - this.$dirty = false; - this.$valid = true; - this.$invalid = false; - this.$name = $attr.name; - - var ngModelGet = $parse($attr.ngModel), - ngModelSet = ngModelGet.assign; - - if (!ngModelSet) { - throw Error(NON_ASSIGNABLE_MODEL_EXPRESSION + $attr.ngModel + - ' (' + startingTag($element) + ')'); - } - - /** - * @ngdoc function - * @name ng.directive:ngModel.NgModelController#$render - * @methodOf ng.directive:ngModel.NgModelController - * - * @description - * Called when the view needs to be updated. It is expected that the user of the ng-model - * directive will implement this method. - */ - this.$render = noop; - - var parentForm = $element.inheritedData('$formController') || nullFormCtrl, - invalidCount = 0, // used to easily determine if we are valid - $error = this.$error = {}; // keep invalid keys here - - - // Setup initial state of the control - $element.addClass(PRISTINE_CLASS); - toggleValidCss(true); - - // convenience method for easy toggling of classes - function toggleValidCss(isValid, validationErrorKey) { - validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : ''; - $element. - removeClass((isValid ? INVALID_CLASS : VALID_CLASS) + validationErrorKey). - addClass((isValid ? VALID_CLASS : INVALID_CLASS) + validationErrorKey); - } - - /** - * @ngdoc function - * @name ng.directive:ngModel.NgModelController#$setValidity - * @methodOf ng.directive:ngModel.NgModelController - * - * @description - * Change the validity state, and notifies the form when the control changes validity. (i.e. it - * does not notify form if given validator is already marked as invalid). - * - * This method should be called by validators - i.e. the parser or formatter functions. - * - * @param {string} validationErrorKey Name of the validator. the `validationErrorKey` will assign - * to `$error[validationErrorKey]=isValid` so that it is available for data-binding. - * The `validationErrorKey` should be in camelCase and will get converted into dash-case - * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error` - * class and can be bound to as `{{someForm.someControl.$error.myError}}` . - * @param {boolean} isValid Whether the current state is valid (true) or invalid (false). - */ - this.$setValidity = function(validationErrorKey, isValid) { - if ($error[validationErrorKey] === !isValid) return; - - if (isValid) { - if ($error[validationErrorKey]) invalidCount--; - if (!invalidCount) { - toggleValidCss(true); - this.$valid = true; - this.$invalid = false; - } - } else { - toggleValidCss(false); - this.$invalid = true; - this.$valid = false; - invalidCount++; - } - - $error[validationErrorKey] = !isValid; - toggleValidCss(isValid, validationErrorKey); - - parentForm.$setValidity(validationErrorKey, isValid, this); - }; - - - /** - * @ngdoc function - * @name ng.directive:ngModel.NgModelController#$setViewValue - * @methodOf ng.directive:ngModel.NgModelController - * - * @description - * Read a value from view. - * - * This method should be called from within a DOM event handler. - * For example {@link ng.directive:input input} or - * {@link ng.directive:select select} directives call it. - * - * It internally calls all `formatters` and if resulted value is valid, updates the model and - * calls all registered change listeners. - * - * @param {string} value Value from the view. - */ - this.$setViewValue = function(value) { - this.$viewValue = value; - - // change to dirty - if (this.$pristine) { - this.$dirty = true; - this.$pristine = false; - $element.removeClass(PRISTINE_CLASS).addClass(DIRTY_CLASS); - parentForm.$setDirty(); - } - - forEach(this.$parsers, function(fn) { - value = fn(value); - }); - - if (this.$modelValue !== value) { - this.$modelValue = value; - ngModelSet($scope, value); - forEach(this.$viewChangeListeners, function(listener) { - try { - listener(); - } catch(e) { - $exceptionHandler(e); - } - }) - } - }; - - // model -> value - var ctrl = this; - - $scope.$watch(function ngModelWatch() { - var value = ngModelGet($scope); - - // if scope model value and ngModel value are out of sync - if (ctrl.$modelValue !== value) { - - var formatters = ctrl.$formatters, - idx = formatters.length; - - ctrl.$modelValue = value; - while(idx--) { - value = formatters[idx](value); - } - - if (ctrl.$viewValue !== value) { - ctrl.$viewValue = value; - ctrl.$render(); - } - } - }); -}]; - - -/** - * @ngdoc directive - * @name ng.directive:ngModel - * - * @element input - * - * @description - * Is directive that tells Angular to do two-way data binding. It works together with `input`, - * `select`, `textarea`. You can easily write your own directives to use `ngModel` as well. - * - * `ngModel` is responsible for: - * - * - binding the view into the model, which other directives such as `input`, `textarea` or `select` - * require, - * - providing validation behavior (i.e. required, number, email, url), - * - keeping state of the control (valid/invalid, dirty/pristine, validation errors), - * - setting related css class onto the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`), - * - register the control with parent {@link ng.directive:form form}. - * - * For basic examples, how to use `ngModel`, see: - * - * - {@link ng.directive:input input} - * - {@link ng.directive:input.text text} - * - {@link ng.directive:input.checkbox checkbox} - * - {@link ng.directive:input.radio radio} - * - {@link ng.directive:input.number number} - * - {@link ng.directive:input.email email} - * - {@link ng.directive:input.url url} - * - {@link ng.directive:select select} - * - {@link ng.directive:textarea textarea} - * - */ -var ngModelDirective = function() { - return { - require: ['ngModel', '^?form'], - controller: NgModelController, - link: function(scope, element, attr, ctrls) { - // notify others, especially parent forms - - var modelCtrl = ctrls[0], - formCtrl = ctrls[1] || nullFormCtrl; - - formCtrl.$addControl(modelCtrl); - - element.bind('$destroy', function() { - formCtrl.$removeControl(modelCtrl); - }); - } - }; -}; - - -/** - * @ngdoc directive - * @name ng.directive:ngChange - * @restrict E - * - * @description - * Evaluate given expression when user changes the input. - * The expression is not evaluated when the value change is coming from the model. - * - * Note, this directive requires `ngModel` to be present. - * - * @element input - * - * @example - * <doc:example> - * <doc:source> - * <script> - * function Controller($scope) { - * $scope.counter = 0; - * $scope.change = function() { - * $scope.counter++; - * }; - * } - * </script> - * <div ng-controller="Controller"> - * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" /> - * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" /> - * <label for="ng-change-example2">Confirmed</label><br /> - * debug = {{confirmed}}<br /> - * counter = {{counter}} - * </div> - * </doc:source> - * <doc:scenario> - * it('should evaluate the expression if changing from view', function() { - * expect(binding('counter')).toEqual('0'); - * element('#ng-change-example1').click(); - * expect(binding('counter')).toEqual('1'); - * expect(binding('confirmed')).toEqual('true'); - * }); - * - * it('should not evaluate the expression if changing from model', function() { - * element('#ng-change-example2').click(); - * expect(binding('counter')).toEqual('0'); - * expect(binding('confirmed')).toEqual('true'); - * }); - * </doc:scenario> - * </doc:example> - */ -var ngChangeDirective = valueFn({ - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - ctrl.$viewChangeListeners.push(function() { - scope.$eval(attr.ngChange); - }); - } -}); - - -var requiredDirective = function() { - return { - require: '?ngModel', - link: function(scope, elm, attr, ctrl) { - if (!ctrl) return; - attr.required = true; // force truthy in case we are on non input element - - var validator = function(value) { - if (attr.required && (isEmpty(value) || value === false)) { - ctrl.$setValidity('required', false); - return; - } else { - ctrl.$setValidity('required', true); - return value; - } - }; - - ctrl.$formatters.push(validator); - ctrl.$parsers.unshift(validator); - - attr.$observe('required', function() { - validator(ctrl.$viewValue); - }); - } - }; -}; - - -/** - * @ngdoc directive - * @name ng.directive:ngList - * - * @description - * Text input that converts between comma-separated string into an array of strings. - * - * @element input - * @param {string=} ngList optional delimiter that should be used to split the value. If - * specified in form `/something/` then the value will be converted into a regular expression. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.names = ['igor', 'misko', 'vojta']; - } - </script> - <form name="myForm" ng-controller="Ctrl"> - List: <input name="namesInput" ng-model="names" ng-list required> - <span class="error" ng-show="myForm.list.$error.required"> - Required!</span> - <tt>names = {{names}}</tt><br/> - <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/> - <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/> - <tt>myForm.$valid = {{myForm.$valid}}</tt><br/> - <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/> - </form> - </doc:source> - <doc:scenario> - it('should initialize to model', function() { - expect(binding('names')).toEqual('["igor","misko","vojta"]'); - expect(binding('myForm.namesInput.$valid')).toEqual('true'); - }); - - it('should be invalid if empty', function() { - input('names').enter(''); - expect(binding('names')).toEqual('[]'); - expect(binding('myForm.namesInput.$valid')).toEqual('false'); - }); - </doc:scenario> - </doc:example> - */ -var ngListDirective = function() { - return { - require: 'ngModel', - link: function(scope, element, attr, ctrl) { - var match = /\/(.*)\//.exec(attr.ngList), - separator = match && new RegExp(match[1]) || attr.ngList || ','; - - var parse = function(viewValue) { - var list = []; - - if (viewValue) { - forEach(viewValue.split(separator), function(value) { - if (value) list.push(trim(value)); - }); - } - - return list; - }; - - ctrl.$parsers.push(parse); - ctrl.$formatters.push(function(value) { - if (isArray(value)) { - return value.join(', '); - } - - return undefined; - }); - } - }; -}; - - -var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/; - -var ngValueDirective = function() { - return { - priority: 100, - compile: function(tpl, tplAttr) { - if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) { - return function(scope, elm, attr) { - attr.$set('value', scope.$eval(attr.ngValue)); - }; - } else { - return function(scope, elm, attr) { - scope.$watch(attr.ngValue, function valueWatchAction(value) { - attr.$set('value', value, false); - }); - }; - } - } - }; -}; - -/** - * @ngdoc directive - * @name ng.directive:ngBind - * - * @description - * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element - * with the value of a given expression, and to update the text content when the value of that - * expression changes. - * - * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like - * `{{ expression }}` which is similar but less verbose. - * - * Once scenario in which the use of `ngBind` is prefered over `{{ expression }}` binding is when - * it's desirable to put bindings into template that is momentarily displayed by the browser in its - * raw state before Angular compiles it. Since `ngBind` is an element attribute, it makes the - * bindings invisible to the user while the page is loading. - * - * An alternative solution to this problem would be using the - * {@link ng.directive:ngCloak ngCloak} directive. - * - * - * @element ANY - * @param {expression} ngBind {@link guide/expression Expression} to evaluate. - * - * @example - * Enter a name in the Live Preview text box; the greeting below the text box changes instantly. - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.name = 'Whirled'; - } - </script> - <div ng-controller="Ctrl"> - Enter name: <input type="text" ng-model="name"><br> - Hello <span ng-bind="name"></span>! - </div> - </doc:source> - <doc:scenario> - it('should check ng-bind', function() { - expect(using('.doc-example-live').binding('name')).toBe('Whirled'); - using('.doc-example-live').input('name').enter('world'); - expect(using('.doc-example-live').binding('name')).toBe('world'); - }); - </doc:scenario> - </doc:example> - */ -var ngBindDirective = ngDirective(function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.ngBind); - scope.$watch(attr.ngBind, function ngBindWatchAction(value) { - element.text(value == undefined ? '' : value); - }); -}); - - -/** - * @ngdoc directive - * @name ng.directive:ngBindTemplate - * - * @description - * The `ngBindTemplate` directive specifies that the element - * text should be replaced with the template in ngBindTemplate. - * Unlike ngBind the ngBindTemplate can contain multiple `{{` `}}` - * expressions. (This is required since some HTML elements - * can not have SPAN elements such as TITLE, or OPTION to name a few.) - * - * @element ANY - * @param {string} ngBindTemplate template of form - * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval. - * - * @example - * Try it here: enter text in text box and watch the greeting change. - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.salutation = 'Hello'; - $scope.name = 'World'; - } - </script> - <div ng-controller="Ctrl"> - Salutation: <input type="text" ng-model="salutation"><br> - Name: <input type="text" ng-model="name"><br> - <pre ng-bind-template="{{salutation}} {{name}}!"></pre> - </div> - </doc:source> - <doc:scenario> - it('should check ng-bind', function() { - expect(using('.doc-example-live').binding('salutation')). - toBe('Hello'); - expect(using('.doc-example-live').binding('name')). - toBe('World'); - using('.doc-example-live').input('salutation').enter('Greetings'); - using('.doc-example-live').input('name').enter('user'); - expect(using('.doc-example-live').binding('salutation')). - toBe('Greetings'); - expect(using('.doc-example-live').binding('name')). - toBe('user'); - }); - </doc:scenario> - </doc:example> - */ -var ngBindTemplateDirective = ['$interpolate', function($interpolate) { - return function(scope, element, attr) { - // TODO: move this to scenario runner - var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate)); - element.addClass('ng-binding').data('$binding', interpolateFn); - attr.$observe('ngBindTemplate', function(value) { - element.text(value); - }); - } -}]; - - -/** - * @ngdoc directive - * @name ng.directive:ngBindHtmlUnsafe - * - * @description - * Creates a binding that will innerHTML the result of evaluating the `expression` into the current - * element. *The innerHTML-ed content will not be sanitized!* You should use this directive only if - * {@link ngSanitize.directive:ngBindHtml ngBindHtml} directive is too - * restrictive and when you absolutely trust the source of the content you are binding to. - * - * See {@link ngSanitize.$sanitize $sanitize} docs for examples. - * - * @element ANY - * @param {expression} ngBindHtmlUnsafe {@link guide/expression Expression} to evaluate. - */ -var ngBindHtmlUnsafeDirective = [function() { - return function(scope, element, attr) { - element.addClass('ng-binding').data('$binding', attr.ngBindHtmlUnsafe); - scope.$watch(attr.ngBindHtmlUnsafe, function ngBindHtmlUnsafeWatchAction(value) { - element.html(value || ''); - }); - }; -}]; - -function classDirective(name, selector) { - name = 'ngClass' + name; - return ngDirective(function(scope, element, attr) { - - scope.$watch(attr[name], ngClassWatchAction, true); - - attr.$observe('class', function(value) { - var ngClass = scope.$eval(attr[name]); - ngClassWatchAction(ngClass, ngClass); - }); - - - if (name !== 'ngClass') { - scope.$watch('$index', function($index, old$index) { - var mod = $index % 2; - if (mod !== old$index % 2) { - if (mod == selector) { - addClass(scope.$eval(attr[name])); - } else { - removeClass(scope.$eval(attr[name])); - } - } - }); - } - - - function ngClassWatchAction(newVal, oldVal) { - if (selector === true || scope.$index % 2 === selector) { - if (oldVal && (newVal !== oldVal)) { - removeClass(oldVal); - } - addClass(newVal); - } - } - - - function removeClass(classVal) { - if (isObject(classVal) && !isArray(classVal)) { - classVal = map(classVal, function(v, k) { if (v) return k }); - } - element.removeClass(isArray(classVal) ? classVal.join(' ') : classVal); - } - - - function addClass(classVal) { - if (isObject(classVal) && !isArray(classVal)) { - classVal = map(classVal, function(v, k) { if (v) return k }); - } - if (classVal) { - element.addClass(isArray(classVal) ? classVal.join(' ') : classVal); - } - } - }); -} - -/** - * @ngdoc directive - * @name ng.directive:ngClass - * - * @description - * The `ngClass` allows you to set CSS class on HTML element dynamically by databinding an - * expression that represents all classes to be added. - * - * The directive won't add duplicate classes if a particular class was already set. - * - * When the expression changes, the previously added classes are removed and only then the classes - * new classes are added. - * - * @element ANY - * @param {expression} ngClass {@link guide/expression Expression} to eval. The result - * of the evaluation can be a string representing space delimited class - * names, an array, or a map of class names to boolean values. - * - * @example - <example> - <file name="index.html"> - <input type="button" value="set" ng-click="myVar='my-class'"> - <input type="button" value="clear" ng-click="myVar=''"> - <br> - <span ng-class="myVar">Sample Text</span> - </file> - <file name="style.css"> - .my-class { - color: red; - } - </file> - <file name="scenario.js"> - it('should check ng-class', function() { - expect(element('.doc-example-live span').prop('className')).not(). - toMatch(/my-class/); - - using('.doc-example-live').element(':button:first').click(); - - expect(element('.doc-example-live span').prop('className')). - toMatch(/my-class/); - - using('.doc-example-live').element(':button:last').click(); - - expect(element('.doc-example-live span').prop('className')).not(). - toMatch(/my-class/); - }); - </file> - </example> - */ -var ngClassDirective = classDirective('', true); - -/** - * @ngdoc directive - * @name ng.directive:ngClassOdd - * - * @description - * The `ngClassOdd` and `ngClassEven` directives work exactly as - * {@link ng.directive:ngClass ngClass}, except it works in - * conjunction with `ngRepeat` and takes affect only on odd (even) rows. - * - * This directive can be applied only within a scope of an - * {@link ng.directive:ngRepeat ngRepeat}. - * - * @element ANY - * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result - * of the evaluation can be a string representing space delimited class names or an array. - * - * @example - <example> - <file name="index.html"> - <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> - <li ng-repeat="name in names"> - <span ng-class-odd="'odd'" ng-class-even="'even'"> - {{name}} - </span> - </li> - </ol> - </file> - <file name="style.css"> - .odd { - color: red; - } - .even { - color: blue; - } - </file> - <file name="scenario.js"> - it('should check ng-class-odd and ng-class-even', function() { - expect(element('.doc-example-live li:first span').prop('className')). - toMatch(/odd/); - expect(element('.doc-example-live li:last span').prop('className')). - toMatch(/even/); - }); - </file> - </example> - */ -var ngClassOddDirective = classDirective('Odd', 0); - -/** - * @ngdoc directive - * @name ng.directive:ngClassEven - * - * @description - * The `ngClassOdd` and `ngClassEven` works exactly as - * {@link ng.directive:ngClass ngClass}, except it works in - * conjunction with `ngRepeat` and takes affect only on odd (even) rows. - * - * This directive can be applied only within a scope of an - * {@link ng.directive:ngRepeat ngRepeat}. - * - * @element ANY - * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The - * result of the evaluation can be a string representing space delimited class names or an array. - * - * @example - <example> - <file name="index.html"> - <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']"> - <li ng-repeat="name in names"> - <span ng-class-odd="'odd'" ng-class-even="'even'"> - {{name}} - </span> - </li> - </ol> - </file> - <file name="style.css"> - .odd { - color: red; - } - .even { - color: blue; - } - </file> - <file name="scenario.js"> - it('should check ng-class-odd and ng-class-even', function() { - expect(element('.doc-example-live li:first span').prop('className')). - toMatch(/odd/); - expect(element('.doc-example-live li:last span').prop('className')). - toMatch(/even/); - }); - </file> - </example> - */ -var ngClassEvenDirective = classDirective('Even', 1); - -/** - * @ngdoc directive - * @name ng.directive:ngCloak - * - * @description - * The `ngCloak` directive is used to prevent the Angular html template from being briefly - * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this - * directive to avoid the undesirable flicker effect caused by the html template display. - * - * The directive can be applied to the `<body>` element, but typically a fine-grained application is - * prefered in order to benefit from progressive rendering of the browser view. - * - * `ngCloak` works in cooperation with a css rule that is embedded within `angular.js` and - * `angular.min.js` files. Following is the css rule: - * - * <pre> - * [ng\:cloak], [ng-cloak], .ng-cloak { - * display: none; - * } - * </pre> - * - * When this css rule is loaded by the browser, all html elements (including their children) that - * are tagged with the `ng-cloak` directive are hidden. When Angular comes across this directive - * during the compilation of the template it deletes the `ngCloak` element attribute, which - * makes the compiled element visible. - * - * For the best result, `angular.js` script must be loaded in the head section of the html file; - * alternatively, the css rule (above) must be included in the external stylesheet of the - * application. - * - * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they - * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css - * class `ngCloak` in addition to `ngCloak` directive as shown in the example below. - * - * @element ANY - * - * @example - <doc:example> - <doc:source> - <div id="template1" ng-cloak>{{ 'hello' }}</div> - <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div> - </doc:source> - <doc:scenario> - it('should remove the template directive and css class', function() { - expect(element('.doc-example-live #template1').attr('ng-cloak')). - not().toBeDefined(); - expect(element('.doc-example-live #template2').attr('ng-cloak')). - not().toBeDefined(); - }); - </doc:scenario> - </doc:example> - * - */ -var ngCloakDirective = ngDirective({ - compile: function(element, attr) { - attr.$set('ngCloak', undefined); - element.removeClass('ng-cloak'); - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngController - * - * @description - * The `ngController` directive assigns behavior to a scope. This is a key aspect of how angular - * supports the principles behind the Model-View-Controller design pattern. - * - * MVC components in angular: - * - * * Model — The Model is data in scope properties; scopes are attached to the DOM. - * * View — The template (HTML with data bindings) is rendered into the View. - * * Controller — The `ngController` directive specifies a Controller class; the class has - * methods that typically express the business logic behind the application. - * - * Note that an alternative way to define controllers is via the `{@link ng.$route}` - * service. - * - * @element ANY - * @scope - * @param {expression} ngController Name of a globally accessible constructor function or an - * {@link guide/expression expression} that on the current scope evaluates to a - * constructor function. - * - * @example - * Here is a simple form for editing user contact information. Adding, removing, clearing, and - * greeting are methods declared on the controller (see source tab). These methods can - * easily be called from the angular markup. Notice that the scope becomes the `this` for the - * controller's instance. This allows for easy access to the view data from the controller. Also - * notice that any changes to the data are automatically reflected in the View without the need - * for a manual update. - <doc:example> - <doc:source> - <script> - function SettingsController($scope) { - $scope.name = "John Smith"; - $scope.contacts = [ - {type:'phone', value:'408 555 1212'}, - {type:'email', value:'john.smith@example.org'} ]; - - $scope.greet = function() { - alert(this.name); - }; - - $scope.addContact = function() { - this.contacts.push({type:'email', value:'yourname@example.org'}); - }; - - $scope.removeContact = function(contactToRemove) { - var index = this.contacts.indexOf(contactToRemove); - this.contacts.splice(index, 1); - }; - - $scope.clearContact = function(contact) { - contact.type = 'phone'; - contact.value = ''; - }; - } - </script> - <div ng-controller="SettingsController"> - Name: <input type="text" ng-model="name"/> - [ <a href="" ng-click="greet()">greet</a> ]<br/> - Contact: - <ul> - <li ng-repeat="contact in contacts"> - <select ng-model="contact.type"> - <option>phone</option> - <option>email</option> - </select> - <input type="text" ng-model="contact.value"/> - [ <a href="" ng-click="clearContact(contact)">clear</a> - | <a href="" ng-click="removeContact(contact)">X</a> ] - </li> - <li>[ <a href="" ng-click="addContact()">add</a> ]</li> - </ul> - </div> - </doc:source> - <doc:scenario> - it('should check controller', function() { - expect(element('.doc-example-live div>:input').val()).toBe('John Smith'); - expect(element('.doc-example-live li:nth-child(1) input').val()) - .toBe('408 555 1212'); - expect(element('.doc-example-live li:nth-child(2) input').val()) - .toBe('john.smith@example.org'); - - element('.doc-example-live li:first a:contains("clear")').click(); - expect(element('.doc-example-live li:first input').val()).toBe(''); - - element('.doc-example-live li:last a:contains("add")').click(); - expect(element('.doc-example-live li:nth-child(3) input').val()) - .toBe('yourname@example.org'); - }); - </doc:scenario> - </doc:example> - */ -var ngControllerDirective = [function() { - return { - scope: true, - controller: '@' - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngCsp - * @priority 1000 - * - * @description - * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support. - * This directive should be used on the root element of the application (typically the `<html>` - * element or other element with the {@link ng.directive:ngApp ngApp} - * directive). - * - * If enabled the performance of template expression evaluator will suffer slightly, so don't enable - * this mode unless you need it. - * - * @element html - */ - -var ngCspDirective = ['$sniffer', function($sniffer) { - return { - priority: 1000, - compile: function() { - $sniffer.csp = true; - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngClick - * - * @description - * The ngClick allows you to specify custom behavior when - * element is clicked. - * - * @element ANY - * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon - * click. (Event object is available as `$event`) - * - * @example - <doc:example> - <doc:source> - <button ng-click="count = count + 1" ng-init="count=0"> - Increment - </button> - count: {{count}} - </doc:source> - <doc:scenario> - it('should check ng-click', function() { - expect(binding('count')).toBe('0'); - element('.doc-example-live :button').click(); - expect(binding('count')).toBe('1'); - }); - </doc:scenario> - </doc:example> - */ -/* - * A directive that allows creation of custom onclick handlers that are defined as angular - * expressions and are compiled and executed within the current scope. - * - * Events that are handled via these handler are always configured not to propagate further. - */ -var ngEventDirectives = {}; -forEach( - 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave'.split(' '), - function(name) { - var directiveName = directiveNormalize('ng-' + name); - ngEventDirectives[directiveName] = ['$parse', function($parse) { - return function(scope, element, attr) { - var fn = $parse(attr[directiveName]); - element.bind(lowercase(name), function(event) { - scope.$apply(function() { - fn(scope, {$event:event}); - }); - }); - }; - }]; - } -); - -/** - * @ngdoc directive - * @name ng.directive:ngDblclick - * - * @description - * The `ngDblclick` directive allows you to specify custom behavior on dblclick event. - * - * @element ANY - * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon - * dblclick. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMousedown - * - * @description - * The ngMousedown directive allows you to specify custom behavior on mousedown event. - * - * @element ANY - * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon - * mousedown. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMouseup - * - * @description - * Specify custom behavior on mouseup event. - * - * @element ANY - * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon - * mouseup. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - -/** - * @ngdoc directive - * @name ng.directive:ngMouseover - * - * @description - * Specify custom behavior on mouseover event. - * - * @element ANY - * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon - * mouseover. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMouseenter - * - * @description - * Specify custom behavior on mouseenter event. - * - * @element ANY - * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon - * mouseenter. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMouseleave - * - * @description - * Specify custom behavior on mouseleave event. - * - * @element ANY - * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon - * mouseleave. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngMousemove - * - * @description - * Specify custom behavior on mousemove event. - * - * @element ANY - * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon - * mousemove. (Event object is available as `$event`) - * - * @example - * See {@link ng.directive:ngClick ngClick} - */ - - -/** - * @ngdoc directive - * @name ng.directive:ngSubmit - * - * @description - * Enables binding angular expressions to onsubmit events. - * - * Additionally it prevents the default action (which for form means sending the request to the - * server and reloading the current page). - * - * @element form - * @param {expression} ngSubmit {@link guide/expression Expression} to eval. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.list = []; - $scope.text = 'hello'; - $scope.submit = function() { - if (this.text) { - this.list.push(this.text); - this.text = ''; - } - }; - } - </script> - <form ng-submit="submit()" ng-controller="Ctrl"> - Enter text and hit enter: - <input type="text" ng-model="text" name="text" /> - <input type="submit" id="submit" value="Submit" /> - <pre>list={{list}}</pre> - </form> - </doc:source> - <doc:scenario> - it('should check ng-submit', function() { - expect(binding('list')).toBe('[]'); - element('.doc-example-live #submit').click(); - expect(binding('list')).toBe('["hello"]'); - expect(input('text').val()).toBe(''); - }); - it('should ignore empty strings', function() { - expect(binding('list')).toBe('[]'); - element('.doc-example-live #submit').click(); - element('.doc-example-live #submit').click(); - expect(binding('list')).toBe('["hello"]'); - }); - </doc:scenario> - </doc:example> - */ -var ngSubmitDirective = ngDirective(function(scope, element, attrs) { - element.bind('submit', function() { - scope.$apply(attrs.ngSubmit); - }); -}); - -/** - * @ngdoc directive - * @name ng.directive:ngInclude - * @restrict ECA - * - * @description - * Fetches, compiles and includes an external HTML fragment. - * - * Keep in mind that Same Origin Policy applies to included resources - * (e.g. ngInclude won't work for cross-domain requests on all browsers and for - * file:// access on some browsers). - * - * @scope - * - * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant, - * make sure you wrap it in quotes, e.g. `src="'myPartialTemplate.html'"`. - * @param {string=} onload Expression to evaluate when a new partial is loaded. - * - * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll - * $anchorScroll} to scroll the viewport after the content is loaded. - * - * - If the attribute is not set, disable scrolling. - * - If the attribute is set without value, enable scrolling. - * - Otherwise enable scrolling only if the expression evaluates to truthy value. - * - * @example - <example> - <file name="index.html"> - <div ng-controller="Ctrl"> - <select ng-model="template" ng-options="t.name for t in templates"> - <option value="">(blank)</option> - </select> - url of the template: <tt>{{template.url}}</tt> - <hr/> - <div ng-include src="template.url"></div> - </div> - </file> - <file name="script.js"> - function Ctrl($scope) { - $scope.templates = - [ { name: 'template1.html', url: 'template1.html'} - , { name: 'template2.html', url: 'template2.html'} ]; - $scope.template = $scope.templates[0]; - } - </file> - <file name="template1.html"> - Content of template1.html - </file> - <file name="template2.html"> - Content of template2.html - </file> - <file name="scenario.js"> - it('should load template1.html', function() { - expect(element('.doc-example-live [ng-include]').text()). - toMatch(/Content of template1.html/); - }); - it('should load template2.html', function() { - select('template').option('1'); - expect(element('.doc-example-live [ng-include]').text()). - toMatch(/Content of template2.html/); - }); - it('should change to blank', function() { - select('template').option(''); - expect(element('.doc-example-live [ng-include]').text()).toEqual(''); - }); - </file> - </example> - */ - - -/** - * @ngdoc event - * @name ng.directive:ngInclude#$includeContentLoaded - * @eventOf ng.directive:ngInclude - * @eventType emit on the current ngInclude scope - * @description - * Emitted every time the ngInclude content is reloaded. - */ -var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', - function($http, $templateCache, $anchorScroll, $compile) { - return { - restrict: 'ECA', - terminal: true, - compile: function(element, attr) { - var srcExp = attr.ngInclude || attr.src, - onloadExp = attr.onload || '', - autoScrollExp = attr.autoscroll; - - return function(scope, element) { - var changeCounter = 0, - childScope; - - var clearContent = function() { - if (childScope) { - childScope.$destroy(); - childScope = null; - } - - element.html(''); - }; - - scope.$watch(srcExp, function ngIncludeWatchAction(src) { - var thisChangeId = ++changeCounter; - - if (src) { - $http.get(src, {cache: $templateCache}).success(function(response) { - if (thisChangeId !== changeCounter) return; - - if (childScope) childScope.$destroy(); - childScope = scope.$new(); - - element.html(response); - $compile(element.contents())(childScope); - - if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) { - $anchorScroll(); - } - - childScope.$emit('$includeContentLoaded'); - scope.$eval(onloadExp); - }).error(function() { - if (thisChangeId === changeCounter) clearContent(); - }); - } else clearContent(); - }); - }; - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngInit - * - * @description - * The `ngInit` directive specifies initialization tasks to be executed - * before the template enters execution mode during bootstrap. - * - * @element ANY - * @param {expression} ngInit {@link guide/expression Expression} to eval. - * - * @example - <doc:example> - <doc:source> - <div ng-init="greeting='Hello'; person='World'"> - {{greeting}} {{person}}! - </div> - </doc:source> - <doc:scenario> - it('should check greeting', function() { - expect(binding('greeting')).toBe('Hello'); - expect(binding('person')).toBe('World'); - }); - </doc:scenario> - </doc:example> - */ -var ngInitDirective = ngDirective({ - compile: function() { - return { - pre: function(scope, element, attrs) { - scope.$eval(attrs.ngInit); - } - } - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngNonBindable - * @priority 1000 - * - * @description - * Sometimes it is necessary to write code which looks like bindings but which should be left alone - * by angular. Use `ngNonBindable` to make angular ignore a chunk of HTML. - * - * @element ANY - * - * @example - * In this example there are two location where a simple binding (`{{}}`) is present, but the one - * wrapped in `ngNonBindable` is left alone. - * - * @example - <doc:example> - <doc:source> - <div>Normal: {{1 + 2}}</div> - <div ng-non-bindable>Ignored: {{1 + 2}}</div> - </doc:source> - <doc:scenario> - it('should check ng-non-bindable', function() { - expect(using('.doc-example-live').binding('1 + 2')).toBe('3'); - expect(using('.doc-example-live').element('div:last').text()). - toMatch(/1 \+ 2/); - }); - </doc:scenario> - </doc:example> - */ -var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 }); - -/** - * @ngdoc directive - * @name ng.directive:ngPluralize - * @restrict EA - * - * @description - * # Overview - * `ngPluralize` is a directive that displays messages according to en-US localization rules. - * These rules are bundled with angular.js and the rules can be overridden - * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive - * by specifying the mappings between - * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - * plural categories} and the strings to be displayed. - * - * # Plural categories and explicit number rules - * There are two - * {@link http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - * plural categories} in Angular's default en-US locale: "one" and "other". - * - * While a pural category may match many numbers (for example, in en-US locale, "other" can match - * any number that is not 1), an explicit number rule can only match one number. For example, the - * explicit number rule for "3" matches the number 3. You will see the use of plural categories - * and explicit number rules throughout later parts of this documentation. - * - * # Configuring ngPluralize - * You configure ngPluralize by providing 2 attributes: `count` and `when`. - * You can also provide an optional attribute, `offset`. - * - * The value of the `count` attribute can be either a string or an {@link guide/expression - * Angular expression}; these are evaluated on the current scope for its bound value. - * - * The `when` attribute specifies the mappings between plural categories and the actual - * string to be displayed. The value of the attribute should be a JSON object so that Angular - * can interpret it correctly. - * - * The following example shows how to configure ngPluralize: - * - * <pre> - * <ng-pluralize count="personCount" - when="{'0': 'Nobody is viewing.', - * 'one': '1 person is viewing.', - * 'other': '{} people are viewing.'}"> - * </ng-pluralize> - *</pre> - * - * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not - * specify this rule, 0 would be matched to the "other" category and "0 people are viewing" - * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for - * other numbers, for example 12, so that instead of showing "12 people are viewing", you can - * show "a dozen people are viewing". - * - * You can use a set of closed braces(`{}`) as a placeholder for the number that you want substituted - * into pluralized strings. In the previous example, Angular will replace `{}` with - * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder - * for <span ng-non-bindable>{{numberExpression}}</span>. - * - * # Configuring ngPluralize with offset - * The `offset` attribute allows further customization of pluralized text, which can result in - * a better user experience. For example, instead of the message "4 people are viewing this document", - * you might display "John, Kate and 2 others are viewing this document". - * The offset attribute allows you to offset a number by any desired value. - * Let's take a look at an example: - * - * <pre> - * <ng-pluralize count="personCount" offset=2 - * when="{'0': 'Nobody is viewing.', - * '1': '{{person1}} is viewing.', - * '2': '{{person1}} and {{person2}} are viewing.', - * 'one': '{{person1}}, {{person2}} and one other person are viewing.', - * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> - * </ng-pluralize> - * </pre> - * - * Notice that we are still using two plural categories(one, other), but we added - * three explicit number rules 0, 1 and 2. - * When one person, perhaps John, views the document, "John is viewing" will be shown. - * When three people view the document, no explicit number rule is found, so - * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category. - * In this case, plural category 'one' is matched and "John, Marry and one other person are viewing" - * is shown. - * - * Note that when you specify offsets, you must provide explicit number rules for - * numbers from 0 up to and including the offset. If you use an offset of 3, for example, - * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for - * plural categories "one" and "other". - * - * @param {string|expression} count The variable to be bounded to. - * @param {string} when The mapping between plural category to its correspoding strings. - * @param {number=} offset Offset to deduct from the total number. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.person1 = 'Igor'; - $scope.person2 = 'Misko'; - $scope.personCount = 1; - } - </script> - <div ng-controller="Ctrl"> - Person 1:<input type="text" ng-model="person1" value="Igor" /><br/> - Person 2:<input type="text" ng-model="person2" value="Misko" /><br/> - Number of People:<input type="text" ng-model="personCount" value="1" /><br/> - - <!--- Example with simple pluralization rules for en locale ---> - Without Offset: - <ng-pluralize count="personCount" - when="{'0': 'Nobody is viewing.', - 'one': '1 person is viewing.', - 'other': '{} people are viewing.'}"> - </ng-pluralize><br> - - <!--- Example with offset ---> - With Offset(2): - <ng-pluralize count="personCount" offset=2 - when="{'0': 'Nobody is viewing.', - '1': '{{person1}} is viewing.', - '2': '{{person1}} and {{person2}} are viewing.', - 'one': '{{person1}}, {{person2}} and one other person are viewing.', - 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}"> - </ng-pluralize> - </div> - </doc:source> - <doc:scenario> - it('should show correct pluralized string', function() { - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('1 person is viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor is viewing.'); - - using('.doc-example-live').input('personCount').enter('0'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('Nobody is viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Nobody is viewing.'); - - using('.doc-example-live').input('personCount').enter('2'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('2 people are viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor and Misko are viewing.'); - - using('.doc-example-live').input('personCount').enter('3'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('3 people are viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor, Misko and one other person are viewing.'); - - using('.doc-example-live').input('personCount').enter('4'); - expect(element('.doc-example-live ng-pluralize:first').text()). - toBe('4 people are viewing.'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor, Misko and 2 other people are viewing.'); - }); - - it('should show data-binded names', function() { - using('.doc-example-live').input('personCount').enter('4'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Igor, Misko and 2 other people are viewing.'); - - using('.doc-example-live').input('person1').enter('Di'); - using('.doc-example-live').input('person2').enter('Vojta'); - expect(element('.doc-example-live ng-pluralize:last').text()). - toBe('Di, Vojta and 2 other people are viewing.'); - }); - </doc:scenario> - </doc:example> - */ -var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) { - var BRACE = /{}/g; - return { - restrict: 'EA', - link: function(scope, element, attr) { - var numberExp = attr.count, - whenExp = element.attr(attr.$attr.when), // this is because we have {{}} in attrs - offset = attr.offset || 0, - whens = scope.$eval(whenExp), - whensExpFns = {}, - startSymbol = $interpolate.startSymbol(), - endSymbol = $interpolate.endSymbol(); - - forEach(whens, function(expression, key) { - whensExpFns[key] = - $interpolate(expression.replace(BRACE, startSymbol + numberExp + '-' + - offset + endSymbol)); - }); - - scope.$watch(function ngPluralizeWatch() { - var value = parseFloat(scope.$eval(numberExp)); - - if (!isNaN(value)) { - //if explicit number rule such as 1, 2, 3... is defined, just use it. Otherwise, - //check it against pluralization rules in $locale service - if (!whens[value]) value = $locale.pluralCat(value - offset); - return whensExpFns[value](scope, element, true); - } else { - return ''; - } - }, function ngPluralizeWatchAction(newVal) { - element.text(newVal); - }); - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:ngRepeat - * - * @description - * The `ngRepeat` directive instantiates a template once per item from a collection. Each template - * instance gets its own scope, where the given loop variable is set to the current collection item, - * and `$index` is set to the item index or key. - * - * Special properties are exposed on the local scope of each template instance, including: - * - * * `$index` – `{number}` – iterator offset of the repeated element (0..length-1) - * * `$first` – `{boolean}` – true if the repeated element is first in the iterator. - * * `$middle` – `{boolean}` – true if the repeated element is between the first and last in the iterator. - * * `$last` – `{boolean}` – true if the repeated element is last in the iterator. - * - * - * @element ANY - * @scope - * @priority 1000 - * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. Two - * formats are currently supported: - * - * * `variable in expression` – where variable is the user defined loop variable and `expression` - * is a scope expression giving the collection to enumerate. - * - * For example: `track in cd.tracks`. - * - * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers, - * and `expression` is the scope expression giving the collection to enumerate. - * - * For example: `(name, age) in {'adam':10, 'amalie':12}`. - * - * @example - * This example initializes the scope to a list of names and - * then uses `ngRepeat` to display every person: - <doc:example> - <doc:source> - <div ng-init="friends = [{name:'John', age:25}, {name:'Mary', age:28}]"> - I have {{friends.length}} friends. They are: - <ul> - <li ng-repeat="friend in friends"> - [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old. - </li> - </ul> - </div> - </doc:source> - <doc:scenario> - it('should check ng-repeat', function() { - var r = using('.doc-example-live').repeater('ul li'); - expect(r.count()).toBe(2); - expect(r.row(0)).toEqual(["1","John","25"]); - expect(r.row(1)).toEqual(["2","Mary","28"]); - }); - </doc:scenario> - </doc:example> - */ -var ngRepeatDirective = ngDirective({ - transclude: 'element', - priority: 1000, - terminal: true, - compile: function(element, attr, linker) { - return function(scope, iterStartElement, attr){ - var expression = attr.ngRepeat; - var match = expression.match(/^\s*(.+)\s+in\s+(.*)\s*$/), - lhs, rhs, valueIdent, keyIdent; - if (! match) { - throw Error("Expected ngRepeat in form of '_item_ in _collection_' but got '" + - expression + "'."); - } - lhs = match[1]; - rhs = match[2]; - match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/); - if (!match) { - throw Error("'item' in 'item in collection' should be identifier or (key, value) but got '" + - lhs + "'."); - } - valueIdent = match[3] || match[1]; - keyIdent = match[2]; - - // Store a list of elements from previous run. This is a hash where key is the item from the - // iterator, and the value is an array of objects with following properties. - // - scope: bound scope - // - element: previous element. - // - index: position - // We need an array of these objects since the same object can be returned from the iterator. - // We expect this to be a rare case. - var lastOrder = new HashQueueMap(); - - scope.$watch(function ngRepeatWatch(scope){ - var index, length, - collection = scope.$eval(rhs), - collectionLength = size(collection, true), - childScope, - // Same as lastOrder but it has the current state. It will become the - // lastOrder on the next iteration. - nextOrder = new HashQueueMap(), - key, value, // key/value of iteration - array, last, // last object information {scope, element, index} - cursor = iterStartElement; // current position of the node - - if (!isArray(collection)) { - // if object, extract keys, sort them and use to determine order of iteration over obj props - array = []; - for(key in collection) { - if (collection.hasOwnProperty(key) && key.charAt(0) != '$') { - array.push(key); - } - } - array.sort(); - } else { - array = collection || []; - } - - // we are not using forEach for perf reasons (trying to avoid #call) - for (index = 0, length = array.length; index < length; index++) { - key = (collection === array) ? index : array[index]; - value = collection[key]; - - last = lastOrder.shift(value); - - if (last) { - // if we have already seen this object, then we need to reuse the - // associated scope/element - childScope = last.scope; - nextOrder.push(value, last); - - if (index === last.index) { - // do nothing - cursor = last.element; - } else { - // existing item which got moved - last.index = index; - // This may be a noop, if the element is next, but I don't know of a good way to - // figure this out, since it would require extra DOM access, so let's just hope that - // the browsers realizes that it is noop, and treats it as such. - cursor.after(last.element); - cursor = last.element; - } - } else { - // new item which we don't know about - childScope = scope.$new(); - } - - childScope[valueIdent] = value; - if (keyIdent) childScope[keyIdent] = key; - childScope.$index = index; - - childScope.$first = (index === 0); - childScope.$last = (index === (collectionLength - 1)); - childScope.$middle = !(childScope.$first || childScope.$last); - - if (!last) { - linker(childScope, function(clone){ - cursor.after(clone); - last = { - scope: childScope, - element: (cursor = clone), - index: index - }; - nextOrder.push(value, last); - }); - } - } - - //shrink children - for (key in lastOrder) { - if (lastOrder.hasOwnProperty(key)) { - array = lastOrder[key]; - while(array.length) { - value = array.pop(); - value.element.remove(); - value.scope.$destroy(); - } - } - } - - lastOrder = nextOrder; - }); - }; - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngShow - * - * @description - * The `ngShow` and `ngHide` directives show or hide a portion of the DOM tree (HTML) - * conditionally. - * - * @element ANY - * @param {expression} ngShow If the {@link guide/expression expression} is truthy - * then the element is shown or hidden respectively. - * - * @example - <doc:example> - <doc:source> - Click me: <input type="checkbox" ng-model="checked"><br/> - Show: <span ng-show="checked">I show up when your checkbox is checked.</span> <br/> - Hide: <span ng-hide="checked">I hide when your checkbox is checked.</span> - </doc:source> - <doc:scenario> - it('should check ng-show / ng-hide', function() { - expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); - expect(element('.doc-example-live span:last:visible').count()).toEqual(1); - - input('checked').check(); - - expect(element('.doc-example-live span:first:visible').count()).toEqual(1); - expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); - }); - </doc:scenario> - </doc:example> - */ -//TODO(misko): refactor to remove element from the DOM -var ngShowDirective = ngDirective(function(scope, element, attr){ - scope.$watch(attr.ngShow, function ngShowWatchAction(value){ - element.css('display', toBoolean(value) ? '' : 'none'); - }); -}); - - -/** - * @ngdoc directive - * @name ng.directive:ngHide - * - * @description - * The `ngHide` and `ngShow` directives hide or show a portion of the DOM tree (HTML) - * conditionally. - * - * @element ANY - * @param {expression} ngHide If the {@link guide/expression expression} is truthy then - * the element is shown or hidden respectively. - * - * @example - <doc:example> - <doc:source> - Click me: <input type="checkbox" ng-model="checked"><br/> - Show: <span ng-show="checked">I show up when you checkbox is checked?</span> <br/> - Hide: <span ng-hide="checked">I hide when you checkbox is checked?</span> - </doc:source> - <doc:scenario> - it('should check ng-show / ng-hide', function() { - expect(element('.doc-example-live span:first:hidden').count()).toEqual(1); - expect(element('.doc-example-live span:last:visible').count()).toEqual(1); - - input('checked').check(); - - expect(element('.doc-example-live span:first:visible').count()).toEqual(1); - expect(element('.doc-example-live span:last:hidden').count()).toEqual(1); - }); - </doc:scenario> - </doc:example> - */ -//TODO(misko): refactor to remove element from the DOM -var ngHideDirective = ngDirective(function(scope, element, attr){ - scope.$watch(attr.ngHide, function ngHideWatchAction(value){ - element.css('display', toBoolean(value) ? 'none' : ''); - }); -}); - -/** - * @ngdoc directive - * @name ng.directive:ngStyle - * - * @description - * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally. - * - * @element ANY - * @param {expression} ngStyle {@link guide/expression Expression} which evals to an - * object whose keys are CSS style names and values are corresponding values for those CSS - * keys. - * - * @example - <example> - <file name="index.html"> - <input type="button" value="set" ng-click="myStyle={color:'red'}"> - <input type="button" value="clear" ng-click="myStyle={}"> - <br/> - <span ng-style="myStyle">Sample Text</span> - <pre>myStyle={{myStyle}}</pre> - </file> - <file name="style.css"> - span { - color: black; - } - </file> - <file name="scenario.js"> - it('should check ng-style', function() { - expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); - element('.doc-example-live :button[value=set]').click(); - expect(element('.doc-example-live span').css('color')).toBe('rgb(255, 0, 0)'); - element('.doc-example-live :button[value=clear]').click(); - expect(element('.doc-example-live span').css('color')).toBe('rgb(0, 0, 0)'); - }); - </file> - </example> - */ -var ngStyleDirective = ngDirective(function(scope, element, attr) { - scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) { - if (oldStyles && (newStyles !== oldStyles)) { - forEach(oldStyles, function(val, style) { element.css(style, '');}); - } - if (newStyles) element.css(newStyles); - }, true); -}); - -/** - * @ngdoc directive - * @name ng.directive:ngSwitch - * @restrict EA - * - * @description - * Conditionally change the DOM structure. - * - * @usageContent - * <ANY ng-switch-when="matchValue1">...</ANY> - * <ANY ng-switch-when="matchValue2">...</ANY> - * ... - * <ANY ng-switch-default>...</ANY> - * - * @scope - * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>. - * @paramDescription - * On child elments add: - * - * * `ngSwitchWhen`: the case statement to match against. If match then this - * case will be displayed. - * * `ngSwitchDefault`: the default case when no other casses match. - * - * @example - <doc:example> - <doc:source> - <script> - function Ctrl($scope) { - $scope.items = ['settings', 'home', 'other']; - $scope.selection = $scope.items[0]; - } - </script> - <div ng-controller="Ctrl"> - <select ng-model="selection" ng-options="item for item in items"> - </select> - <tt>selection={{selection}}</tt> - <hr/> - <div ng-switch on="selection" > - <div ng-switch-when="settings">Settings Div</div> - <span ng-switch-when="home">Home Span</span> - <span ng-switch-default>default</span> - </div> - </div> - </doc:source> - <doc:scenario> - it('should start in settings', function() { - expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Settings Div/); - }); - it('should change to home', function() { - select('selection').option('home'); - expect(element('.doc-example-live [ng-switch]').text()).toMatch(/Home Span/); - }); - it('should select deafault', function() { - select('selection').option('other'); - expect(element('.doc-example-live [ng-switch]').text()).toMatch(/default/); - }); - </doc:scenario> - </doc:example> - */ -var NG_SWITCH = 'ng-switch'; -var ngSwitchDirective = valueFn({ - restrict: 'EA', - compile: function(element, attr) { - var watchExpr = attr.ngSwitch || attr.on, - cases = {}; - - element.data(NG_SWITCH, cases); - return function(scope, element){ - var selectedTransclude, - selectedElement, - selectedScope; - - scope.$watch(watchExpr, function ngSwitchWatchAction(value) { - if (selectedElement) { - selectedScope.$destroy(); - selectedElement.remove(); - selectedElement = selectedScope = null; - } - if ((selectedTransclude = cases['!' + value] || cases['?'])) { - scope.$eval(attr.change); - selectedScope = scope.$new(); - selectedTransclude(selectedScope, function(caseElement) { - selectedElement = caseElement; - element.append(caseElement); - }); - } - }); - }; - } -}); - -var ngSwitchWhenDirective = ngDirective({ - transclude: 'element', - priority: 500, - compile: function(element, attrs, transclude) { - var cases = element.inheritedData(NG_SWITCH); - assertArg(cases); - cases['!' + attrs.ngSwitchWhen] = transclude; - } -}); - -var ngSwitchDefaultDirective = ngDirective({ - transclude: 'element', - priority: 500, - compile: function(element, attrs, transclude) { - var cases = element.inheritedData(NG_SWITCH); - assertArg(cases); - cases['?'] = transclude; - } -}); - -/** - * @ngdoc directive - * @name ng.directive:ngTransclude - * - * @description - * Insert the transcluded DOM here. - * - * @element ANY - * - * @example - <doc:example module="transclude"> - <doc:source> - <script> - function Ctrl($scope) { - $scope.title = 'Lorem Ipsum'; - $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...'; - } - - angular.module('transclude', []) - .directive('pane', function(){ - return { - restrict: 'E', - transclude: true, - scope: 'isolate', - locals: { title:'bind' }, - template: '<div style="border: 1px solid black;">' + - '<div style="background-color: gray">{{title}}</div>' + - '<div ng-transclude></div>' + - '</div>' - }; - }); - </script> - <div ng-controller="Ctrl"> - <input ng-model="title"><br> - <textarea ng-model="text"></textarea> <br/> - <pane title="{{title}}">{{text}}</pane> - </div> - </doc:source> - <doc:scenario> - it('should have transcluded', function() { - input('title').enter('TITLE'); - input('text').enter('TEXT'); - expect(binding('title')).toEqual('TITLE'); - expect(binding('text')).toEqual('TEXT'); - }); - </doc:scenario> - </doc:example> - * - */ -var ngTranscludeDirective = ngDirective({ - controller: ['$transclude', '$element', function($transclude, $element) { - $transclude(function(clone) { - $element.append(clone); - }); - }] -}); - -/** - * @ngdoc directive - * @name ng.directive:ngView - * @restrict ECA - * - * @description - * # Overview - * `ngView` is a directive that complements the {@link ng.$route $route} service by - * including the rendered template of the current route into the main layout (`index.html`) file. - * Every time the current route changes, the included view changes with it according to the - * configuration of the `$route` service. - * - * @scope - * @example - <example module="ngView"> - <file name="index.html"> - <div ng-controller="MainCntl"> - Choose: - <a href="Book/Moby">Moby</a> | - <a href="Book/Moby/ch/1">Moby: Ch1</a> | - <a href="Book/Gatsby">Gatsby</a> | - <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> | - <a href="Book/Scarlet">Scarlet Letter</a><br/> - - <div ng-view></div> - <hr /> - - <pre>$location.path() = {{$location.path()}}</pre> - <pre>$route.current.template = {{$route.current.template}}</pre> - <pre>$route.current.params = {{$route.current.params}}</pre> - <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre> - <pre>$routeParams = {{$routeParams}}</pre> - </div> - </file> - - <file name="book.html"> - controller: {{name}}<br /> - Book Id: {{params.bookId}}<br /> - </file> - - <file name="chapter.html"> - controller: {{name}}<br /> - Book Id: {{params.bookId}}<br /> - Chapter Id: {{params.chapterId}} - </file> - - <file name="script.js"> - angular.module('ngView', [], function($routeProvider, $locationProvider) { - $routeProvider.when('/Book/:bookId', { - templateUrl: 'book.html', - controller: BookCntl - }); - $routeProvider.when('/Book/:bookId/ch/:chapterId', { - templateUrl: 'chapter.html', - controller: ChapterCntl - }); - - // configure html5 to get links working on jsfiddle - $locationProvider.html5Mode(true); - }); - - function MainCntl($scope, $route, $routeParams, $location) { - $scope.$route = $route; - $scope.$location = $location; - $scope.$routeParams = $routeParams; - } - - function BookCntl($scope, $routeParams) { - $scope.name = "BookCntl"; - $scope.params = $routeParams; - } - - function ChapterCntl($scope, $routeParams) { - $scope.name = "ChapterCntl"; - $scope.params = $routeParams; - } - </file> - - <file name="scenario.js"> - it('should load and compile correct template', function() { - element('a:contains("Moby: Ch1")').click(); - var content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: ChapterCntl/); - expect(content).toMatch(/Book Id\: Moby/); - expect(content).toMatch(/Chapter Id\: 1/); - - element('a:contains("Scarlet")').click(); - content = element('.doc-example-live [ng-view]').text(); - expect(content).toMatch(/controller\: BookCntl/); - expect(content).toMatch(/Book Id\: Scarlet/); - }); - </file> - </example> - */ - - -/** - * @ngdoc event - * @name ng.directive:ngView#$viewContentLoaded - * @eventOf ng.directive:ngView - * @eventType emit on the current ngView scope - * @description - * Emitted every time the ngView content is reloaded. - */ -var ngViewDirective = ['$http', '$templateCache', '$route', '$anchorScroll', '$compile', - '$controller', - function($http, $templateCache, $route, $anchorScroll, $compile, - $controller) { - return { - restrict: 'ECA', - terminal: true, - link: function(scope, element, attr) { - var lastScope, - onloadExp = attr.onload || ''; - - scope.$on('$routeChangeSuccess', update); - update(); - - - function destroyLastScope() { - if (lastScope) { - lastScope.$destroy(); - lastScope = null; - } - } - - function clearContent() { - element.html(''); - destroyLastScope(); - } - - function update() { - var locals = $route.current && $route.current.locals, - template = locals && locals.$template; - - if (template) { - element.html(template); - destroyLastScope(); - - var link = $compile(element.contents()), - current = $route.current, - controller; - - lastScope = current.scope = scope.$new(); - if (current.controller) { - locals.$scope = lastScope; - controller = $controller(current.controller, locals); - element.contents().data('$ngControllerController', controller); - } - - link(lastScope); - lastScope.$emit('$viewContentLoaded'); - lastScope.$eval(onloadExp); - - // $anchorScroll might listen on event... - $anchorScroll(); - } else { - clearContent(); - } - } - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:script - * - * @description - * Load content of a script tag, with type `text/ng-template`, into `$templateCache`, so that the - * template can be used by `ngInclude`, `ngView` or directive templates. - * - * @restrict E - * @param {'text/ng-template'} type must be set to `'text/ng-template'` - * - * @example - <doc:example> - <doc:source> - <script type="text/ng-template" id="/tpl.html"> - Content of the template. - </script> - - <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a> - <div id="tpl-content" ng-include src="currentTpl"></div> - </doc:source> - <doc:scenario> - it('should load template defined inside script tag', function() { - element('#tpl-link').click(); - expect(element('#tpl-content').text()).toMatch(/Content of the template/); - }); - </doc:scenario> - </doc:example> - */ -var scriptDirective = ['$templateCache', function($templateCache) { - return { - restrict: 'E', - terminal: true, - compile: function(element, attr) { - if (attr.type == 'text/ng-template') { - var templateUrl = attr.id, - // IE is not consistent, in scripts we have to read .text but in other nodes we have to read .textContent - text = element[0].text; - - $templateCache.put(templateUrl, text); - } - } - }; -}]; - -/** - * @ngdoc directive - * @name ng.directive:select - * @restrict E - * - * @description - * HTML `SELECT` element with angular data-binding. - * - * # `ngOptions` - * - * Optionally `ngOptions` attribute can be used to dynamically generate a list of `<option>` - * elements for a `<select>` element using an array or an object obtained by evaluating the - * `ngOptions` expression. - *˝˝ - * When an item in the select menu is select, the value of array element or object property - * represented by the selected option will be bound to the model identified by the `ngModel` - * directive of the parent select element. - * - * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can - * be nested into the `<select>` element. This element will then represent `null` or "not selected" - * option. See example below for demonstration. - * - * Note: `ngOptions` provides iterator facility for `<option>` element which should be used instead - * of {@link ng.directive:ngRepeat ngRepeat} when you want the - * `select` model to be bound to a non-string value. This is because an option element can currently - * be bound to string values only. - * - * @param {string} name assignable expression to data-bind to. - * @param {string=} required The control is considered valid only if value is entered. - * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to - * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of - * `required` when you want to data-bind to the `required` attribute. - * @param {comprehension_expression=} ngOptions in one of the following forms: - * - * * for array data sources: - * * `label` **`for`** `value` **`in`** `array` - * * `select` **`as`** `label` **`for`** `value` **`in`** `array` - * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` - * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` - * * for object data sources: - * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object` - * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object` - * * `select` **`as`** `label` **`group by`** `group` - * **`for` `(`**`key`**`,`** `value`**`) in`** `object` - * - * Where: - * - * * `array` / `object`: an expression which evaluates to an array / object to iterate over. - * * `value`: local variable which will refer to each item in the `array` or each property value - * of `object` during iteration. - * * `key`: local variable which will refer to a property name in `object` during iteration. - * * `label`: The result of this expression will be the label for `<option>` element. The - * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`). - * * `select`: The result of this expression will be bound to the model of the parent `<select>` - * element. If not specified, `select` expression will default to `value`. - * * `group`: The result of this expression will be used to group options using the `<optgroup>` - * DOM element. - * - * @example - <doc:example> - <doc:source> - <script> - function MyCntrl($scope) { - $scope.colors = [ - {name:'black', shade:'dark'}, - {name:'white', shade:'light'}, - {name:'red', shade:'dark'}, - {name:'blue', shade:'dark'}, - {name:'yellow', shade:'light'} - ]; - $scope.color = $scope.colors[2]; // red - } - </script> - <div ng-controller="MyCntrl"> - <ul> - <li ng-repeat="color in colors"> - Name: <input ng-model="color.name"> - [<a href ng-click="colors.splice($index, 1)">X</a>] - </li> - <li> - [<a href ng-click="colors.push({})">add</a>] - </li> - </ul> - <hr/> - Color (null not allowed): - <select ng-model="color" ng-options="c.name for c in colors"></select><br> - - Color (null allowed): - <span class="nullable"> - <select ng-model="color" ng-options="c.name for c in colors"> - <option value="">-- chose color --</option> - </select> - </span><br/> - - Color grouped by shade: - <select ng-model="color" ng-options="c.name group by c.shade for c in colors"> - </select><br/> - - - Select <a href ng-click="color={name:'not in list'}">bogus</a>.<br> - <hr/> - Currently selected: {{ {selected_color:color} }} - <div style="border:solid 1px black; height:20px" - ng-style="{'background-color':color.name}"> - </div> - </div> - </doc:source> - <doc:scenario> - it('should check ng-options', function() { - expect(binding('{selected_color:color}')).toMatch('red'); - select('color').option('0'); - expect(binding('{selected_color:color}')).toMatch('black'); - using('.nullable').select('color').option(''); - expect(binding('{selected_color:color}')).toMatch('null'); - }); - </doc:scenario> - </doc:example> - */ - -var ngOptionsDirective = valueFn({ terminal: true }); -var selectDirective = ['$compile', '$parse', function($compile, $parse) { - //00001111100000000000222200000000000000000000003333000000000000044444444444444444000000000555555555555555550000000666666666666666660000000000000007777 - var NG_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/, - nullModelCtrl = {$setViewValue: noop}; - - return { - restrict: 'E', - require: ['select', '?ngModel'], - controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) { - var self = this, - optionsMap = {}, - ngModelCtrl = nullModelCtrl, - nullOption, - unknownOption; - - - self.databound = $attrs.ngModel; - - - self.init = function(ngModelCtrl_, nullOption_, unknownOption_) { - ngModelCtrl = ngModelCtrl_; - nullOption = nullOption_; - unknownOption = unknownOption_; - } - - - self.addOption = function(value) { - optionsMap[value] = true; - - if (ngModelCtrl.$viewValue == value) { - $element.val(value); - if (unknownOption.parent()) unknownOption.remove(); - } - }; - - - self.removeOption = function(value) { - if (this.hasOption(value)) { - delete optionsMap[value]; - if (ngModelCtrl.$viewValue == value) { - this.renderUnknownOption(value); - } - } - }; - - - self.renderUnknownOption = function(val) { - var unknownVal = '? ' + hashKey(val) + ' ?'; - unknownOption.val(unknownVal); - $element.prepend(unknownOption); - $element.val(unknownVal); - unknownOption.prop('selected', true); // needed for IE - } - - - self.hasOption = function(value) { - return optionsMap.hasOwnProperty(value); - } - - $scope.$on('$destroy', function() { - // disable unknown option so that we don't do work when the whole select is being destroyed - self.renderUnknownOption = noop; - }); - }], - - link: function(scope, element, attr, ctrls) { - // if ngModel is not defined, we don't need to do anything - if (!ctrls[1]) return; - - var selectCtrl = ctrls[0], - ngModelCtrl = ctrls[1], - multiple = attr.multiple, - optionsExp = attr.ngOptions, - nullOption = false, // if false, user will not be able to select it (used by ngOptions) - emptyOption, - // we can't just jqLite('<option>') since jqLite is not smart enough - // to create it in <select> and IE barfs otherwise. - optionTemplate = jqLite(document.createElement('option')), - optGroupTemplate =jqLite(document.createElement('optgroup')), - unknownOption = optionTemplate.clone(); - - // find "null" option - for(var i = 0, children = element.children(), ii = children.length; i < ii; i++) { - if (children[i].value == '') { - emptyOption = nullOption = children.eq(i); - break; - } - } - - selectCtrl.init(ngModelCtrl, nullOption, unknownOption); - - // required validator - if (multiple && (attr.required || attr.ngRequired)) { - var requiredValidator = function(value) { - ngModelCtrl.$setValidity('required', !attr.required || (value && value.length)); - return value; - }; - - ngModelCtrl.$parsers.push(requiredValidator); - ngModelCtrl.$formatters.unshift(requiredValidator); - - attr.$observe('required', function() { - requiredValidator(ngModelCtrl.$viewValue); - }); - } - - if (optionsExp) Options(scope, element, ngModelCtrl); - else if (multiple) Multiple(scope, element, ngModelCtrl); - else Single(scope, element, ngModelCtrl, selectCtrl); - - - //////////////////////////// - - - - function Single(scope, selectElement, ngModelCtrl, selectCtrl) { - ngModelCtrl.$render = function() { - var viewValue = ngModelCtrl.$viewValue; - - if (selectCtrl.hasOption(viewValue)) { - if (unknownOption.parent()) unknownOption.remove(); - selectElement.val(viewValue); - if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy - } else { - if (isUndefined(viewValue) && emptyOption) { - selectElement.val(''); - } else { - selectCtrl.renderUnknownOption(viewValue); - } - } - }; - - selectElement.bind('change', function() { - scope.$apply(function() { - if (unknownOption.parent()) unknownOption.remove(); - ngModelCtrl.$setViewValue(selectElement.val()); - }); - }); - } - - function Multiple(scope, selectElement, ctrl) { - var lastView; - ctrl.$render = function() { - var items = new HashMap(ctrl.$viewValue); - forEach(selectElement.children(), function(option) { - option.selected = isDefined(items.get(option.value)); - }); - }; - - // we have to do it on each watch since ngModel watches reference, but - // we need to work of an array, so we need to see if anything was inserted/removed - scope.$watch(function selectMultipleWatch() { - if (!equals(lastView, ctrl.$viewValue)) { - lastView = copy(ctrl.$viewValue); - ctrl.$render(); - } - }); - - selectElement.bind('change', function() { - scope.$apply(function() { - var array = []; - forEach(selectElement.children(), function(option) { - if (option.selected) { - array.push(option.value); - } - }); - ctrl.$setViewValue(array); - }); - }); - } - - function Options(scope, selectElement, ctrl) { - var match; - - if (! (match = optionsExp.match(NG_OPTIONS_REGEXP))) { - throw Error( - "Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" + - " but got '" + optionsExp + "'."); - } - - var displayFn = $parse(match[2] || match[1]), - valueName = match[4] || match[6], - keyName = match[5], - groupByFn = $parse(match[3] || ''), - valueFn = $parse(match[2] ? match[1] : valueName), - valuesFn = $parse(match[7]), - // This is an array of array of existing option groups in DOM. We try to reuse these if possible - // optionGroupsCache[0] is the options with no option group - // optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element - optionGroupsCache = [[{element: selectElement, label:''}]]; - - if (nullOption) { - // compile the element since there might be bindings in it - $compile(nullOption)(scope); - - // remove the class, which is added automatically because we recompile the element and it - // becomes the compilation root - nullOption.removeClass('ng-scope'); - - // we need to remove it before calling selectElement.html('') because otherwise IE will - // remove the label from the element. wtf? - nullOption.remove(); - } - - // clear contents, we'll add what's needed based on the model - selectElement.html(''); - - selectElement.bind('change', function() { - scope.$apply(function() { - var optionGroup, - collection = valuesFn(scope) || [], - locals = {}, - key, value, optionElement, index, groupIndex, length, groupLength; - - if (multiple) { - value = []; - for (groupIndex = 0, groupLength = optionGroupsCache.length; - groupIndex < groupLength; - groupIndex++) { - // list of options for that group. (first item has the parent) - optionGroup = optionGroupsCache[groupIndex]; - - for(index = 1, length = optionGroup.length; index < length; index++) { - if ((optionElement = optionGroup[index].element)[0].selected) { - key = optionElement.val(); - if (keyName) locals[keyName] = key; - locals[valueName] = collection[key]; - value.push(valueFn(scope, locals)); - } - } - } - } else { - key = selectElement.val(); - if (key == '?') { - value = undefined; - } else if (key == ''){ - value = null; - } else { - locals[valueName] = collection[key]; - if (keyName) locals[keyName] = key; - value = valueFn(scope, locals); - } - } - ctrl.$setViewValue(value); - }); - }); - - ctrl.$render = render; - - // TODO(vojta): can't we optimize this ? - scope.$watch(render); - - function render() { - var optionGroups = {'':[]}, // Temporary location for the option groups before we render them - optionGroupNames = [''], - optionGroupName, - optionGroup, - option, - existingParent, existingOptions, existingOption, - modelValue = ctrl.$modelValue, - values = valuesFn(scope) || [], - keys = keyName ? sortedKeys(values) : values, - groupLength, length, - groupIndex, index, - locals = {}, - selected, - selectedSet = false, // nothing is selected yet - lastElement, - element, - label; - - if (multiple) { - selectedSet = new HashMap(modelValue); - } else if (modelValue === null || nullOption) { - // if we are not multiselect, and we are null then we have to add the nullOption - optionGroups[''].push({selected:modelValue === null, id:'', label:''}); - selectedSet = true; - } - - // We now build up the list of options we need (we merge later) - for (index = 0; length = keys.length, index < length; index++) { - locals[valueName] = values[keyName ? locals[keyName]=keys[index]:index]; - optionGroupName = groupByFn(scope, locals) || ''; - if (!(optionGroup = optionGroups[optionGroupName])) { - optionGroup = optionGroups[optionGroupName] = []; - optionGroupNames.push(optionGroupName); - } - if (multiple) { - selected = selectedSet.remove(valueFn(scope, locals)) != undefined; - } else { - selected = modelValue === valueFn(scope, locals); - selectedSet = selectedSet || selected; // see if at least one item is selected - } - label = displayFn(scope, locals); // what will be seen by the user - label = label === undefined ? '' : label; // doing displayFn(scope, locals) || '' overwrites zero values - optionGroup.push({ - id: keyName ? keys[index] : index, // either the index into array or key from object - label: label, - selected: selected // determine if we should be selected - }); - } - if (!multiple && !selectedSet) { - // nothing was selected, we have to insert the undefined item - optionGroups[''].unshift({id:'?', label:'', selected:true}); - } - - // Now we need to update the list of DOM nodes to match the optionGroups we computed above - for (groupIndex = 0, groupLength = optionGroupNames.length; - groupIndex < groupLength; - groupIndex++) { - // current option group name or '' if no group - optionGroupName = optionGroupNames[groupIndex]; - - // list of options for that group. (first item has the parent) - optionGroup = optionGroups[optionGroupName]; - - if (optionGroupsCache.length <= groupIndex) { - // we need to grow the optionGroups - existingParent = { - element: optGroupTemplate.clone().attr('label', optionGroupName), - label: optionGroup.label - }; - existingOptions = [existingParent]; - optionGroupsCache.push(existingOptions); - selectElement.append(existingParent.element); - } else { - existingOptions = optionGroupsCache[groupIndex]; - existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element - - // update the OPTGROUP label if not the same. - if (existingParent.label != optionGroupName) { - existingParent.element.attr('label', existingParent.label = optionGroupName); - } - } - - lastElement = null; // start at the beginning - for(index = 0, length = optionGroup.length; index < length; index++) { - option = optionGroup[index]; - if ((existingOption = existingOptions[index+1])) { - // reuse elements - lastElement = existingOption.element; - if (existingOption.label !== option.label) { - lastElement.text(existingOption.label = option.label); - } - if (existingOption.id !== option.id) { - lastElement.val(existingOption.id = option.id); - } - if (existingOption.element.selected !== option.selected) { - lastElement.prop('selected', (existingOption.selected = option.selected)); - } - } else { - // grow elements - - // if it's a null option - if (option.id === '' && nullOption) { - // put back the pre-compiled element - element = nullOption; - } else { - // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but - // in this version of jQuery on some browser the .text() returns a string - // rather then the element. - (element = optionTemplate.clone()) - .val(option.id) - .attr('selected', option.selected) - .text(option.label); - } - - existingOptions.push(existingOption = { - element: element, - label: option.label, - id: option.id, - selected: option.selected - }); - if (lastElement) { - lastElement.after(element); - } else { - existingParent.element.append(element); - } - lastElement = element; - } - } - // remove any excessive OPTIONs in a group - index++; // increment since the existingOptions[0] is parent element not OPTION - while(existingOptions.length > index) { - existingOptions.pop().element.remove(); - } - } - // remove any excessive OPTGROUPs from select - while(optionGroupsCache.length > groupIndex) { - optionGroupsCache.pop()[0].element.remove(); - } - } - } - } - } -}]; - -var optionDirective = ['$interpolate', function($interpolate) { - var nullSelectCtrl = { - addOption: noop, - removeOption: noop - }; - - return { - restrict: 'E', - priority: 100, - compile: function(element, attr) { - if (isUndefined(attr.value)) { - var interpolateFn = $interpolate(element.text(), true); - if (!interpolateFn) { - attr.$set('value', element.text()); - } - } - - return function (scope, element, attr) { - var selectCtrlName = '$selectController', - parent = element.parent(), - selectCtrl = parent.data(selectCtrlName) || - parent.parent().data(selectCtrlName); // in case we are in optgroup - - if (selectCtrl && selectCtrl.databound) { - // For some reason Opera defaults to true and if not overridden this messes up the repeater. - // We don't want the view to drive the initialization of the model anyway. - element.prop('selected', false); - } else { - selectCtrl = nullSelectCtrl; - } - - if (interpolateFn) { - scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) { - attr.$set('value', newVal); - if (newVal !== oldVal) selectCtrl.removeOption(oldVal); - selectCtrl.addOption(newVal); - }); - } else { - selectCtrl.addOption(attr.value); - } - - element.bind('$destroy', function() { - selectCtrl.removeOption(attr.value); - }); - }; - } - } -}]; - -var styleDirective = valueFn({ - restrict: 'E', - terminal: true -}); - //try to bind to jquery now so that one can write angular.element().read() - //but we will rebind on bootstrap again. - bindJQuery(); - - publishExternalAPI(angular); - - jqLite(document).ready(function() { - angularInit(document, bootstrap); - }); - -})(window, document); -angular.element(document).find('head').append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>');
\ No newline at end of file diff --git a/src/main/webapp/external/angular-1.0.3/angular.min.js b/src/main/webapp/external/angular-1.0.3/angular.min.js deleted file mode 100644 index 07f501b..0000000 --- a/src/main/webapp/external/angular-1.0.3/angular.min.js +++ /dev/null @@ -1,159 +0,0 @@ -/* - AngularJS v1.0.3 - (c) 2010-2012 Google, Inc. http://angularjs.org - License: MIT -*/ -(function(U,ca,p){'use strict';function m(b,a,c){var d;if(b)if(N(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(L(b)&&wa(b.length))for(d=0;d<b.length;d++)a.call(c,b[d],d);else for(d in b)b.hasOwnProperty(d)&&a.call(c,b[d],d);return b}function lb(b){var a=[],c;for(c in b)b.hasOwnProperty(c)&&a.push(c);return a.sort()}function ec(b,a,c){for(var d=lb(b),e=0;e<d.length;e++)a.call(c,b[d[e]],d[e]);return d} -function mb(b){return function(a,c){b(c,a)}}function xa(){for(var b=Z.length,a;b;){b--;a=Z[b].charCodeAt(0);if(a==57)return Z[b]="A",Z.join("");if(a==90)Z[b]="0";else return Z[b]=String.fromCharCode(a+1),Z.join("")}Z.unshift("0");return Z.join("")}function x(b){m(arguments,function(a){a!==b&&m(a,function(a,d){b[d]=a})});return b}function G(b){return parseInt(b,10)}function ya(b,a){return x(new (x(function(){},{prototype:b})),a)}function D(){}function ma(b){return b}function I(b){return function(){return b}} -function t(b){return typeof b=="undefined"}function v(b){return typeof b!="undefined"}function L(b){return b!=null&&typeof b=="object"}function F(b){return typeof b=="string"}function wa(b){return typeof b=="number"}function na(b){return Sa.apply(b)=="[object Date]"}function J(b){return Sa.apply(b)=="[object Array]"}function N(b){return typeof b=="function"}function oa(b){return b&&b.document&&b.location&&b.alert&&b.setInterval}function R(b){return F(b)?b.replace(/^\s*/,"").replace(/\s*$/,""):b}function fc(b){return b&& -(b.nodeName||b.bind&&b.find)}function Ta(b,a,c){var d=[];m(b,function(b,g,i){d.push(a.call(c,b,g,i))});return d}function gc(b,a){var c=0,d;if(J(b)||F(b))return b.length;else if(L(b))for(d in b)(!a||b.hasOwnProperty(d))&&c++;return c}function za(b,a){if(b.indexOf)return b.indexOf(a);for(var c=0;c<b.length;c++)if(a===b[c])return c;return-1}function Ua(b,a){var c=za(b,a);c>=0&&b.splice(c,1);return a}function V(b,a){if(oa(b)||b&&b.$evalAsync&&b.$watch)throw B("Can't copy Window or Scope");if(a){if(b=== -a)throw B("Can't copy equivalent objects or arrays");if(J(b)){for(;a.length;)a.pop();for(var c=0;c<b.length;c++)a.push(V(b[c]))}else for(c in m(a,function(b,c){delete a[c]}),b)a[c]=V(b[c])}else(a=b)&&(J(b)?a=V(b,[]):na(b)?a=new Date(b.getTime()):L(b)&&(a=V(b,{})));return a}function hc(b,a){var a=a||{},c;for(c in b)b.hasOwnProperty(c)&&c.substr(0,2)!=="$$"&&(a[c]=b[c]);return a}function ha(b,a){if(b===a)return!0;if(b===null||a===null)return!1;if(b!==b&&a!==a)return!0;var c=typeof b,d;if(c==typeof a&& -c=="object")if(J(b)){if((c=b.length)==a.length){for(d=0;d<c;d++)if(!ha(b[d],a[d]))return!1;return!0}}else if(na(b))return na(a)&&b.getTime()==a.getTime();else{if(b&&b.$evalAsync&&b.$watch||a&&a.$evalAsync&&a.$watch||oa(b)||oa(a))return!1;c={};for(d in b){if(d.charAt(0)!=="$"&&!N(b[d])&&!ha(b[d],a[d]))return!1;c[d]=!0}for(d in a)if(!c[d]&&d.charAt(0)!=="$"&&!N(a[d]))return!1;return!0}return!1}function Va(b,a){var c=arguments.length>2?ia.call(arguments,2):[];return N(a)&&!(a instanceof RegExp)?c.length? -function(){return arguments.length?a.apply(b,c.concat(ia.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=p:oa(a)?c="$WINDOW":a&&ca===a?c="$DOCUMENT":a&&a.$evalAsync&&a.$watch&&(c="$SCOPE");return c}function da(b,a){return JSON.stringify(b,ic,a?" ":null)}function nb(b){return F(b)?JSON.parse(b):b}function Wa(b){b&&b.length!==0?(b=E(""+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){}return u("<div>").append(b).html().match(/^(<[^>]+>)/)[1].replace(/^<([\w\-]+)/,function(a,b){return"<"+E(b)})}function Xa(b){var a={},c,d;m((b||"").split("&"),function(b){b&&(c=b.split("="),d=decodeURIComponent(c[0]),a[d]=v(c[1])?decodeURIComponent(c[1]):!0)});return a}function ob(b){var a=[];m(b,function(b,d){a.push(Ya(d,!0)+(b===!0?"":"="+Ya(b,!0)))});return a.length?a.join("&"):""}function Za(b){return Ya(b,!0).replace(/%26/gi,"&").replace(/%3D/gi, -"=").replace(/%2B/gi,"+")}function Ya(b,a){return encodeURIComponent(b).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(a?null:/%20/g,"+")}function jc(b,a){function c(a){a&&d.push(a)}var d=[b],e,g,i=["ng:app","ng-app","x-ng-app","data-ng-app"],f=/\sng[:\-]app(:\s*([\w\d_]+);?)?\s/;m(i,function(a){i[a]=!0;c(ca.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&&i[b.name])e=a,g=b.value})}});e&&a(e,g?[g]:[])}function pb(b,a){b=u(b);a=a||[];a.unshift(["$provide",function(a){a.value("$rootElement",b)}]);a.unshift("ng");var c=qb(a);c.invoke(["$rootScope","$rootElement","$compile","$injector",function(a,b,c,i){a.$apply(function(){b.data("$injector",i);c(b)(a)})}]);return c}function $a(b,a){a=a||"_";return b.replace(kc, -function(b,d){return(d?a:"")+b.toLowerCase()})}function qa(b,a,c){if(!b)throw new B("Argument '"+(a||"?")+"' is "+(c||"required"));return b}function ra(b,a,c){c&&J(b)&&(b=b[b.length-1]);qa(N(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 j}}if(!e)throw B("No module: "+d);var b=[],c=[],k=a("$injector","invoke"),j={_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:k,run:function(a){c.push(a); -return this}};g&&k(g);return j})}})}function rb(b){return b.replace(mc,function(a,b,d,e){return e?d.toUpperCase():d}).replace(nc,"Moz$1")}function ab(b,a){function c(){var e;for(var b=[this],c=a,i,f,h,k,j,l;b.length;){i=b.shift();f=0;for(h=i.length;f<h;f++){k=u(i[f]);c?k.triggerHandler("$destroy"):c=!c;j=0;for(e=(l=k.children()).length,k=e;j<k;j++)b.push(ja(l[j]))}}return d.apply(this,arguments)}var d=ja.fn[b],d=d.$original||d;c.$original=d;ja.fn[b]=c}function Q(b){if(b instanceof Q)return b;if(!(this instanceof -Q)){if(F(b)&&b.charAt(0)!="<")throw B("selectors not implemented");return new Q(b)}if(F(b)){var a=ca.createElement("div");a.innerHTML="<div> </div>"+b;a.removeChild(a.firstChild);bb(this,a.childNodes);this.remove()}else bb(this,b)}function cb(b){return b.cloneNode(!0)}function sa(b){sb(b);for(var a=0,b=b.childNodes||[];a<b.length;a++)sa(b[a])}function tb(b,a,c){var d=$(b,"events");$(b,"handle")&&(t(a)?m(d,function(a,c){db(b,c,a);delete d[c]}):t(c)?(db(b,a,d[a]),delete d[a]):Ua(d[a],c))}function sb(b){var a= -b[Aa],c=Ba[a];c&&(c.handle&&(c.events.$destroy&&c.handle({},"$destroy"),tb(b)),delete Ba[a],b[Aa]=p)}function $(b,a,c){var d=b[Aa],d=Ba[d||-1];if(v(c))d||(b[Aa]=d=++oc,d=Ba[d]={}),d[a]=c;else return d&&d[a]}function ub(b,a,c){var d=$(b,"data"),e=v(c),g=!e&&v(a),i=g&&!L(a);!d&&!i&&$(b,"data",d={});if(e)d[a]=c;else if(g)if(i)return d&&d[a];else x(d,a);else return d}function Ca(b,a){return(" "+b.className+" ").replace(/[\n\t]/g," ").indexOf(" "+a+" ")>-1}function vb(b,a){a&&m(a.split(" "),function(a){b.className= -R((" "+b.className+" ").replace(/[\n\t]/g," ").replace(" "+R(a)+" "," "))})}function wb(b,a){a&&m(a.split(" "),function(a){if(!Ca(b,a))b.className=R(b.className+" "+R(a))})}function bb(b,a){if(a)for(var a=!a.nodeName&&v(a.length)&&!oa(a)?a:[a],c=0;c<a.length;c++)b.push(a[c])}function xb(b,a){return Da(b,"$"+(a||"ngController")+"Controller")}function Da(b,a,c){b=u(b);for(b[0].nodeType==9&&(b=b.find("html"));b.length;){if(c=b.data(a))return c;b=b.parent()}}function yb(b,a){var c=Ea[a.toLowerCase()]; -return c&&zb[b.nodeName]&&c}function pc(b,a){var c=function(c,e){if(!c.preventDefault)c.preventDefault=function(){c.returnValue=!1};if(!c.stopPropagation)c.stopPropagation=function(){c.cancelBubble=!0};if(!c.target)c.target=c.srcElement||ca;if(t(c.defaultPrevented)){var g=c.preventDefault;c.preventDefault=function(){c.defaultPrevented=!0;g.call(c)};c.defaultPrevented=!1}c.isDefaultPrevented=function(){return c.defaultPrevented};m(a[e||c.type],function(a){a.call(b,c)});aa<=8?(c.preventDefault=null, -c.stopPropagation=null,c.isDefaultPrevented=null):(delete c.preventDefault,delete c.stopPropagation,delete c.isDefaultPrevented)};c.elem=b;return c}function ga(b){var a=typeof b,c;if(a=="object"&&b!==null)if(typeof(c=b.$$hashKey)=="function")c=b.$$hashKey();else{if(c===p)c=b.$$hashKey=xa()}else c=b;return a+":"+c}function Fa(b){m(b,this.put,this)}function eb(){}function Ab(b){var a,c;if(typeof b=="function"){if(!(a=b.$inject))a=[],c=b.toString().replace(qc,""),c=c.match(rc),m(c[1].split(sc),function(b){b.replace(tc, -function(b,c,d){a.push(d)})}),b.$inject=a}else J(b)?(c=b.length-1,ra(b[c],"fn"),a=b.slice(0,c)):ra(b,"fn",!0);return a}function qb(b){function a(a){return function(b,c){if(L(b))m(b,mb(a));else return a(b,c)}}function c(a,b){N(b)&&(b=l.instantiate(b));if(!b.$get)throw B("Provider "+a+" must define $get factory method.");return j[a+f]=b}function d(a,b){return c(a,{$get:b})}function e(a){var b=[];m(a,function(a){if(!k.get(a))if(k.put(a,!0),F(a)){var c=ta(a);b=b.concat(e(c.requires)).concat(c._runBlocks); -try{for(var d=c._invokeQueue,c=0,f=d.length;c<f;c++){var h=d[c],g=h[0]=="$injector"?l:l.get(h[0]);g[h[1]].apply(g,h[2])}}catch(n){throw n.message&&(n.message+=" from "+a),n;}}else if(N(a))try{b.push(l.invoke(a))}catch(i){throw i.message&&(i.message+=" from "+a),i;}else if(J(a))try{b.push(l.invoke(a))}catch(j){throw j.message&&(j.message+=" from "+String(a[a.length-1])),j;}else ra(a,"module")});return b}function g(a,b){function c(d){if(typeof d!=="string")throw B("Service name expected");if(a.hasOwnProperty(d)){if(a[d]=== -i)throw B("Circular dependency: "+h.join(" <- "));return a[d]}else try{return h.unshift(d),a[d]=i,a[d]=b(d)}finally{h.shift()}}function d(a,b,e){var f=[],k=Ab(a),g,n,i;n=0;for(g=k.length;n<g;n++)i=k[n],f.push(e&&e.hasOwnProperty(i)?e[i]:c(i,h));a.$inject||(a=a[g]);switch(b?-1:f.length){case 0:return a();case 1:return a(f[0]);case 2:return a(f[0],f[1]);case 3:return a(f[0],f[1],f[2]);case 4:return a(f[0],f[1],f[2],f[3]);case 5:return a(f[0],f[1],f[2],f[3],f[4]);case 6:return a(f[0],f[1],f[2],f[3], -f[4],f[5]);case 7:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6]);case 8:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7]);case 9:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8]);case 10:return a(f[0],f[1],f[2],f[3],f[4],f[5],f[6],f[7],f[8],f[9]);default:return a.apply(b,f)}}return{invoke:d,instantiate:function(a,b){var c=function(){},e;c.prototype=(J(a)?a[a.length-1]:a).prototype;c=new c;e=d(a,c,b);return L(e)?e:c},get:c,annotate:Ab}}var i={},f="Provider",h=[],k=new Fa,j={$provide:{provider:a(c), -factory:a(d),service:a(function(a,b){return d(a,["$injector",function(a){return a.instantiate(b)}])}),value:a(function(a,b){return d(a,I(b))}),constant:a(function(a,b){j[a]=b;o[a]=b}),decorator:function(a,b){var c=l.get(a+f),d=c.$get;c.$get=function(){var a=r.invoke(d,c);return r.invoke(b,null,{$delegate:a})}}}},l=g(j,function(){throw B("Unknown provider: "+h.join(" <- "));}),o={},r=o.$injector=g(o,function(a){a=l.get(a+f);return r.invoke(a.$get,a)});m(e(b),function(a){r.invoke(a||D)});return r}function uc(){var b= -!0;this.disableAutoScrolling=function(){b=!1};this.$get=["$window","$location","$rootScope",function(a,c,d){function e(a){var b=null;m(a,function(a){!b&&E(a.nodeName)==="a"&&(b=a)});return b}function g(){var b=c.hash(),d;b?(d=i.getElementById(b))?d.scrollIntoView():(d=e(i.getElementsByName(b)))?d.scrollIntoView():b==="top"&&a.scrollTo(0,0):a.scrollTo(0,0)}var i=a.document;b&&d.$watch(function(){return c.hash()},function(){d.$evalAsync(g)});return g}]}function vc(b,a,c,d){function e(a){try{a.apply(null, -ia.call(arguments,1))}finally{if(n--,n===0)for(;w.length;)try{w.pop()()}catch(b){c.error(b)}}}function g(a,b){(function ea(){m(q,function(a){a()});s=b(ea,a)})()}function i(){O!=f.url()&&(O=f.url(),m(A,function(a){a(f.url())}))}var f=this,h=a[0],k=b.location,j=b.history,l=b.setTimeout,o=b.clearTimeout,r={};f.isMock=!1;var n=0,w=[];f.$$completeOutstandingRequest=e;f.$$incOutstandingRequestCount=function(){n++};f.notifyWhenNoOutstandingRequests=function(a){m(q,function(a){a()});n===0?a():w.push(a)}; -var q=[],s;f.addPollFn=function(a){t(s)&&g(100,l);q.push(a);return a};var O=k.href,C=a.find("base");f.url=function(a,b){if(a){if(O!=a)return O=a,d.history?b?j.replaceState(null,"",a):(j.pushState(null,"",a),C.attr("href",C.attr("href"))):b?k.replace(a):k.href=a,f}else return k.href.replace(/%27/g,"'")};var A=[],K=!1;f.onUrlChange=function(a){K||(d.history&&u(b).bind("popstate",i),d.hashchange?u(b).bind("hashchange",i):f.addPollFn(i),K=!0);A.push(a);return a};f.baseHref=function(){var a=C.attr("href"); -return a?a.replace(/^https?\:\/\/[^\/]*/,""):a};var W={},y="",M=f.baseHref();f.cookies=function(a,b){var d,e,f,k;if(a)if(b===p)h.cookie=escape(a)+"=;path="+M+";expires=Thu, 01 Jan 1970 00:00:00 GMT";else{if(F(b))d=(h.cookie=escape(a)+"="+escape(b)+";path="+M).length+1,d>4096&&c.warn("Cookie '"+a+"' possibly not set or overflowed because it was too large ("+d+" > 4096 bytes)!"),W.length>20&&c.warn("Cookie '"+a+"' possibly not set or overflowed because too many cookies were already set ("+W.length+ -" > 20 )")}else{if(h.cookie!==y){y=h.cookie;d=y.split("; ");W={};for(f=0;f<d.length;f++)e=d[f],k=e.indexOf("="),k>0&&(W[unescape(e.substring(0,k))]=unescape(e.substring(k+1)))}return W}};f.defer=function(a,b){var c;n++;c=l(function(){delete r[c];e(a)},b||0);r[c]=!0;return c};f.defer.cancel=function(a){return r[a]?(delete r[a],o(a),e(D),!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(o){if(o==a)o=a.n}else o=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 B("cacheId "+b+" taken");var i=0,f=x({},d,{id:b}),h={},k=d&&d.capacity||Number.MAX_VALUE,j={},l=null,o=null;return a[b]={put:function(a,b){var c=j[a]||(j[a]={key:a});e(c);t(b)||(a in h||i++,h[a]=b,i>k&&this.remove(o.key))},get:function(a){var b=j[a];if(b)return e(b),h[a]},remove:function(a){var b=j[a];if(b){if(b==l)l=b.p;if(b==o)o=b.n;g(b.n,b.p);delete j[a]; -delete h[a];i--}},removeAll:function(){h={};i=0;j={};l=o=null},destroy:function(){j=f=h=null;delete a[b]},info:function(){return x({},f,{size:i})}}}var a={};b.info=function(){var b={};m(a,function(a,e){b[e]=a.info()});return b};b.get=function(b){return a[b]};return b}}function yc(){this.$get=["$cacheFactory",function(b){return b("templates")}]}function Bb(b){var a={},c="Directive",d=/^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,e=/(([\d\w\-_]+)(?:\:([^;]+))?;?)/,g="Template must have exactly one root element. was: "; -this.directive=function f(d,e){F(d)?(qa(e,"directive"),a.hasOwnProperty(d)||(a[d]=[],b.factory(d+c,["$injector","$exceptionHandler",function(b,c){var e=[];m(a[d],function(a){try{var f=b.invoke(a);if(N(f))f={compile:I(f)};else if(!f.compile&&f.link)f.compile=I(f.link);f.priority=f.priority||0;f.name=f.name||d;f.require=f.require||f.controller&&f.name;f.restrict=f.restrict||"A";e.push(f)}catch(k){c(k)}});return e}])),a[d].push(e)):m(d,mb(f));return this};this.$get=["$injector","$interpolate","$exceptionHandler", -"$http","$templateCache","$parse","$controller","$rootScope",function(b,h,k,j,l,o,r,n){function w(a,b,c){a instanceof u||(a=u(a));m(a,function(b,c){b.nodeType==3&&(a[c]=u(b).wrap("<span></span>").parent()[0])});var d=s(a,b,a,c);return function(b,c){qa(b,"scope");var e=c?ua.clone.call(a):a;e.data("$scope",b);q(e,"ng-scope");c&&c(e,b);d&&d(b,e,e);return e}}function q(a,b){try{a.addClass(b)}catch(c){}}function s(a,b,c,d){function e(a,c,d,k){for(var g,h,j,n,o,l=0,r=0,q=f.length;l<q;r++)j=c[r],g=f[l++], -h=f[l++],g?(g.scope?(n=a.$new(L(g.scope)),u(j).data("$scope",n)):n=a,(o=g.transclude)||!k&&b?g(h,n,j,d,function(b){return function(c){var d=a.$new();return b(d,c).bind("$destroy",Va(d,d.$destroy))}}(o||b)):g(h,n,j,p,k)):h&&h(a,j.childNodes,p,k)}for(var f=[],k,g,h,j=0;j<a.length;j++)g=new ea,k=O(a[j],[],g,d),g=(k=k.length?C(k,a[j],g,b,c):null)&&k.terminal||!a[j].childNodes.length?null:s(a[j].childNodes,k?k.transclude:b),f.push(k),f.push(g),h=h||k||g;return h?e:null}function O(a,b,c,f){var k=c.$attr, -g;switch(a.nodeType){case 1:A(b,fa(Cb(a).toLowerCase()),"E",f);var h,j,n;g=a.attributes;for(var o=0,l=g&&g.length;o<l;o++)if(h=g[o],h.specified)j=h.name,n=fa(j.toLowerCase()),k[n]=j,c[n]=h=R(aa&&j=="href"?decodeURIComponent(a.getAttribute(j,2)):h.value),yb(a,n)&&(c[n]=!0),X(a,b,h,n),A(b,n,"A",f);a=a.className;if(F(a)&&a!=="")for(;g=e.exec(a);)n=fa(g[2]),A(b,n,"C",f)&&(c[n]=R(g[3])),a=a.substr(g.index+g[0].length);break;case 3:H(b,a.nodeValue);break;case 8:try{if(g=d.exec(a.nodeValue))n=fa(g[1]),A(b, -n,"M",f)&&(c[n]=R(g[2]))}catch(r){}}b.sort(y);return b}function C(a,b,c,d,e){function f(a,b){if(a)a.require=z.require,l.push(a);if(b)b.require=z.require,ba.push(b)}function h(a,b){var c,d="data",e=!1;if(F(a)){for(;(c=a.charAt(0))=="^"||c=="?";)a=a.substr(1),c=="^"&&(d="inheritedData"),e=e||c=="?";c=b[d]("$"+a+"Controller");if(!c&&!e)throw B("No controller: "+a);}else J(a)&&(c=[],m(a,function(a){c.push(h(a,b))}));return c}function j(a,d,e,f,g){var n,q,w,K,s;n=b===e?c:hc(c,new ea(u(e),c.$attr));q=n.$$element; -if(C){var zc=/^\s*([@=&])\s*(\w*)\s*$/,O=d.$parent||d;m(C.scope,function(a,b){var c=a.match(zc)||[],e=c[2]||b,f,g,k;switch(c[1]){case "@":n.$observe(e,function(a){d[b]=a});n.$$observers[e].$$scope=O;break;case "=":g=o(n[e]);k=g.assign||function(){f=d[b]=g(O);throw B(Db+n[e]+" (directive: "+C.name+")");};f=d[b]=g(O);d.$watch(function(){var a=g(O);a!==d[b]&&(a!==f?f=d[b]=a:k(O,a=f=d[b]));return a});break;case "&":g=o(n[e]);d[b]=function(a){return g(O,a)};break;default:throw B("Invalid isolate scope definition for directive "+ -C.name+": "+a);}})}t&&m(t,function(a){var b={$scope:d,$element:q,$attrs:n,$transclude:g};s=a.controller;s=="@"&&(s=n[a.name]);q.data("$"+a.name+"Controller",r(s,b))});f=0;for(w=l.length;f<w;f++)try{K=l[f],K(d,q,n,K.require&&h(K.require,q))}catch(y){k(y,pa(q))}a&&a(d,e.childNodes,p,g);f=0;for(w=ba.length;f<w;f++)try{K=ba[f],K(d,q,n,K.require&&h(K.require,q))}catch(Ha){k(Ha,pa(q))}}for(var n=-Number.MAX_VALUE,l=[],ba=[],s=null,C=null,A=null,y=c.$$element=u(b),z,H,X,D,v=d,t,x,Y,E=0,G=a.length;E<G;E++){z= -a[E];X=p;if(n>z.priority)break;if(Y=z.scope)M("isolated scope",C,z,y),L(Y)&&(q(y,"ng-isolate-scope"),C=z),q(y,"ng-scope"),s=s||z;H=z.name;if(Y=z.controller)t=t||{},M("'"+H+"' controller",t[H],z,y),t[H]=z;if(Y=z.transclude)M("transclusion",D,z,y),D=z,n=z.priority,Y=="element"?(X=u(b),y=c.$$element=u("<\!-- "+H+": "+c[H]+" --\>"),b=y[0],Ga(e,u(X[0]),b),v=w(X,d,n)):(X=u(cb(b)).contents(),y.html(""),v=w(X,d));if(Y=z.template)if(M("template",A,z,y),A=z,Y=Ha(Y),z.replace){X=u("<div>"+R(Y)+"</div>").contents(); -b=X[0];if(X.length!=1||b.nodeType!==1)throw new B(g+Y);Ga(e,y,b);H={$attr:{}};a=a.concat(O(b,a.splice(E+1,a.length-(E+1)),H));K(c,H);G=a.length}else y.html(Y);if(z.templateUrl)M("template",A,z,y),A=z,j=W(a.splice(E,a.length-E),j,y,c,e,z.replace,v),G=a.length;else if(z.compile)try{x=z.compile(y,c,v),N(x)?f(null,x):x&&f(x.pre,x.post)}catch(I){k(I,pa(y))}if(z.terminal)j.terminal=!0,n=Math.max(n,z.priority)}j.scope=s&&s.scope;j.transclude=D&&v;return j}function A(d,e,g,h){var j=!1;if(a.hasOwnProperty(e))for(var n, -e=b.get(e+c),o=0,l=e.length;o<l;o++)try{if(n=e[o],(h===p||h>n.priority)&&n.restrict.indexOf(g)!=-1)d.push(n),j=!0}catch(r){k(r)}return j}function K(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,function(b,f){f=="class"?(q(e,b),a["class"]=(a["class"]?a["class"]+" ":"")+b):f=="style"?e.attr("style",e.attr("style")+";"+b):f.charAt(0)!="$"&&!a.hasOwnProperty(f)&&(a[f]=b,d[f]=c[f])})}function W(a,b,c,d,e, -f,k){var h=[],n,o,r=c[0],q=a.shift(),w=x({},q,{controller:null,templateUrl:null,transclude:null,scope:null});c.html("");j.get(q.templateUrl,{cache:l}).success(function(j){var l,q,j=Ha(j);if(f){q=u("<div>"+R(j)+"</div>").contents();l=q[0];if(q.length!=1||l.nodeType!==1)throw new B(g+j);j={$attr:{}};Ga(e,c,l);O(l,a,j);K(d,j)}else l=r,c.html(j);a.unshift(w);n=C(a,c,d,k);for(o=s(c.contents(),k);h.length;){var ba=h.pop(),j=h.pop();q=h.pop();var y=h.pop(),m=l;q!==r&&(m=cb(l),Ga(j,u(q),m));n(function(){b(o, -y,m,e,ba)},y,m,e,ba)}h=null}).error(function(a,b,c,d){throw B("Failed to load template: "+d.url);});return function(a,c,d,e,f){h?(h.push(c),h.push(d),h.push(e),h.push(f)):n(function(){b(o,c,d,e,f)},c,d,e,f)}}function y(a,b){return b.priority-a.priority}function M(a,b,c,d){if(b)throw B("Multiple directives ["+b.name+", "+c.name+"] asking for "+a+" on: "+pa(d));}function H(a,b){var c=h(b,!0);c&&a.push({priority:0,compile:I(function(a,b){var d=b.parent(),e=d.data("$binding")||[];e.push(c);q(d.data("$binding", -e),"ng-binding");a.$watch(c,function(a){b[0].nodeValue=a})})})}function X(a,b,c,d){var e=h(c,!0);e&&b.push({priority:100,compile:I(function(a,b,c){b=c.$$observers||(c.$$observers={});d==="class"&&(e=h(c[d],!0));c[d]=p;(b[d]||(b[d]=[])).$$inter=!0;(c.$$observers&&c.$$observers[d].$$scope||a).$watch(e,function(a){c.$set(d,a)})})})}function Ga(a,b,c){var d=b[0],e=d.parentNode,f,g;if(a){f=0;for(g=a.length;f<g;f++)if(a[f]==d){a[f]=c;break}}e&&e.replaceChild(c,d);c[u.expando]=d[u.expando];b[0]=c}var ea= -function(a,b){this.$$element=a;this.$attr=b||{}};ea.prototype={$normalize:fa,$set:function(a,b,c,d){var e=yb(this.$$element[0],a),f=this.$$observers;e&&(this.$$element.prop(a,b),d=e);this[a]=b;d?this.$attr[a]=d:(d=this.$attr[a])||(this.$attr[a]=d=$a(a,"-"));c!==!1&&(b===null||b===p?this.$$element.removeAttr(d):this.$$element.attr(d,b));f&&m(f[a],function(a){try{a(b)}catch(c){k(c)}})},$observe:function(a,b){var c=this,d=c.$$observers||(c.$$observers={}),e=d[a]||(d[a]=[]);e.push(b);n.$evalAsync(function(){e.$$inter|| -b(c[a])});return b}};var D=h.startSymbol(),ba=h.endSymbol(),Ha=D=="{{"||ba=="}}"?ma:function(a){return a.replace(/\{\{/g,D).replace(/}}/g,ba)};return w}]}function fa(b){return rb(b.replace(Ac,""))}function Bc(){var b={};this.register=function(a,c){L(a)?x(b,a):b[a]=c};this.$get=["$injector","$window",function(a,c){return function(d,e){if(F(d)){var g=d,d=b.hasOwnProperty(g)?b[g]:fb(e.$scope,g,!0)||fb(c,g,!0);ra(d,g,!0)}return a.instantiate(d,e)}}]}function Cc(){this.$get=["$window",function(b){return u(b.document)}]} -function Dc(){this.$get=["$log",function(b){return function(a,c){b.error.apply(b,arguments)}}]}function Ec(){var b="{{",a="}}";this.startSymbol=function(a){return a?(b=a,this):b};this.endSymbol=function(b){return b?(a=b,this):a};this.$get=["$parse",function(c){function d(d,f){for(var h,k,j=0,l=[],o=d.length,r=!1,n=[];j<o;)(h=d.indexOf(b,j))!=-1&&(k=d.indexOf(a,h+e))!=-1?(j!=h&&l.push(d.substring(j,h)),l.push(j=c(r=d.substring(h+e,k))),j.exp=r,j=k+g,r=!0):(j!=o&&l.push(d.substring(j)),j=o);if(!(o= -l.length))l.push(""),o=1;if(!f||r)return n.length=o,j=function(a){for(var b=0,c=o,d;b<c;b++){if(typeof(d=l[b])=="function")d=d(a),d==null||d==p?d="":typeof d!="string"&&(d=da(d));n[b]=d}return n.join("")},j.exp=d,j.parts=l,j}var e=b.length,g=a.length;d.startSymbol=function(){return b};d.endSymbol=function(){return a};return d}]}function Eb(b){for(var b=b.split("/"),a=b.length;a--;)b[a]=Za(b[a]);return b.join("/")}function va(b,a){var c=Fb.exec(b),c={protocol:c[1],host:c[3],port:G(c[5])||Gb[c[1]]|| -null,path:c[6]||"/",search:c[8],hash:c[10]};if(a)a.$$protocol=c.protocol,a.$$host=c.host,a.$$port=c.port;return c}function ka(b,a,c){return b+"://"+a+(c==Gb[b]?"":":"+c)}function Fc(b,a,c){var d=va(b);return decodeURIComponent(d.path)!=a||t(d.hash)||d.hash.indexOf(c)!==0?b:ka(d.protocol,d.host,d.port)+a.substr(0,a.lastIndexOf("/"))+d.hash.substr(c.length)}function Gc(b,a,c){var d=va(b);if(decodeURIComponent(d.path)==a)return b;else{var e=d.search&&"?"+d.search||"",g=d.hash&&"#"+d.hash||"",i=a.substr(0, -a.lastIndexOf("/")),f=d.path.substr(i.length);if(d.path.indexOf(i)!==0)throw B('Invalid url "'+b+'", missing path prefix "'+i+'" !');return ka(d.protocol,d.host,d.port)+a+"#"+c+f+e+g}}function gb(b,a,c){a=a||"";this.$$parse=function(b){var c=va(b,this);if(c.path.indexOf(a)!==0)throw B('Invalid url "'+b+'", missing path prefix "'+a+'" !');this.$$path=decodeURIComponent(c.path.substr(a.length));this.$$search=Xa(c.search);this.$$hash=c.hash&&decodeURIComponent(c.hash)||"";this.$$compose()};this.$$compose= -function(){var b=ob(this.$$search),c=this.$$hash?"#"+Za(this.$$hash):"";this.$$url=Eb(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=ka(this.$$protocol,this.$$host,this.$$port)+a+this.$$url};this.$$rewriteAppUrl=function(a){if(a.indexOf(c)==0)return a};this.$$parse(b)}function Ia(b,a,c){var d;this.$$parse=function(b){var c=va(b,this);if(c.hash&&c.hash.indexOf(a)!==0)throw B('Invalid url "'+b+'", missing hash prefix "'+a+'" !');d=c.path+(c.search?"?"+c.search:"");c=Hc.exec((c.hash||"").substr(a.length)); -this.$$path=c[1]?(c[1].charAt(0)=="/"?"":"/")+decodeURIComponent(c[1]):"";this.$$search=Xa(c[3]);this.$$hash=c[5]&&decodeURIComponent(c[5])||"";this.$$compose()};this.$$compose=function(){var b=ob(this.$$search),c=this.$$hash?"#"+Za(this.$$hash):"";this.$$url=Eb(this.$$path)+(b?"?"+b:"")+c;this.$$absUrl=ka(this.$$protocol,this.$$host,this.$$port)+d+(this.$$url?"#"+a+this.$$url:"")};this.$$rewriteAppUrl=function(a){if(a.indexOf(c)==0)return a};this.$$parse(b)}function Hb(b,a,c,d){Ia.apply(this,arguments); -this.$$rewriteAppUrl=function(b){if(b.indexOf(c)==0)return c+d+"#"+a+b.substr(c.length)}}function Ja(b){return function(){return this[b]}}function Ib(b,a){return function(c){if(t(c))return this[b];this[b]=a(c);this.$$compose();return this}}function Ic(){var b="",a=!1;this.hashPrefix=function(a){return v(a)?(b=a,this):b};this.html5Mode=function(b){return v(b)?(a=b,this):a};this.$get=["$rootScope","$browser","$sniffer","$rootElement",function(c,d,e,g){function i(a){c.$broadcast("$locationChangeSuccess", -f.absUrl(),a)}var f,h,k,j=d.url(),l=va(j);a?(h=d.baseHref()||"/",k=h.substr(0,h.lastIndexOf("/")),l=ka(l.protocol,l.host,l.port)+k+"/",f=e.history?new gb(Fc(j,h,b),k,l):new Hb(Gc(j,h,b),b,l,h.substr(k.length+1))):(l=ka(l.protocol,l.host,l.port)+(l.path||"")+(l.search?"?"+l.search:"")+"#"+b+"/",f=new Ia(j,b,l));g.bind("click",function(a){if(!a.ctrlKey&&!(a.metaKey||a.which==2)){for(var b=u(a.target);E(b[0].nodeName)!=="a";)if(b[0]===g[0]||!(b=b.parent())[0])return;var d=b.prop("href"),e=f.$$rewriteAppUrl(d); -d&&!b.attr("target")&&e&&(f.$$parse(e),c.$apply(),a.preventDefault(),U.angular["ff-684208-preventDefault"]=!0)}});f.absUrl()!=j&&d.url(f.absUrl(),!0);d.onUrlChange(function(a){f.absUrl()!=a&&(c.$evalAsync(function(){var b=f.absUrl();f.$$parse(a);i(b)}),c.$$phase||c.$digest())});var o=0;c.$watch(function(){var a=d.url(),b=f.$$replace;if(!o||a!=f.absUrl())o++,c.$evalAsync(function(){c.$broadcast("$locationChangeStart",f.absUrl(),a).defaultPrevented?f.$$parse(a):(d.url(f.absUrl(),b),i(a))});f.$$replace= -!1;return o});return f}]}function Jc(){this.$get=["$window",function(b){function a(a){a instanceof B&&(a.stack?a=a.message&&a.stack.indexOf(a.message)===-1?"Error: "+a.message+"\n"+a.stack:a.stack:a.sourceURL&&(a=a.message+"\n"+a.sourceURL+":"+a.line));return a}function c(c){var e=b.console||{},g=e[c]||e.log||D;return g.apply?function(){var b=[];m(arguments,function(c){b.push(a(c))});return g.apply(e,b)}:function(a,b){g(a,b)}}return{log:c("log"),warn:c("warn"),info:c("info"),error:c("error")}}]}function Kc(b, -a){function c(a){return a.indexOf(q)!=-1}function d(){return n+1<b.length?b.charAt(n+1):!1}function e(a){return"0"<=a&&a<="9"}function g(a){return a==" "||a=="\r"||a=="\t"||a=="\n"||a=="\u000b"||a=="\u00a0"}function i(a){return"a"<=a&&a<="z"||"A"<=a&&a<="Z"||"_"==a||a=="$"}function f(a){return a=="-"||a=="+"||e(a)}function h(a,c,d){d=d||n;throw B("Lexer Error: "+a+" at column"+(v(c)?"s "+c+"-"+n+" ["+b.substring(c,d)+"]":" "+d)+" in expression ["+b+"].");}function k(){for(var a="",c=n;n<b.length;){var k= -E(b.charAt(n));if(k=="."||e(k))a+=k;else{var g=d();if(k=="e"&&f(g))a+=k;else if(f(k)&&g&&e(g)&&a.charAt(a.length-1)=="e")a+=k;else if(f(k)&&(!g||!e(g))&&a.charAt(a.length-1)=="e")h("Invalid exponent");else break}n++}a*=1;o.push({index:c,text:a,json:!0,fn:function(){return a}})}function j(){for(var c="",d=n,f,k,h;n<b.length;){var j=b.charAt(n);if(j=="."||i(j)||e(j))j=="."&&(f=n),c+=j;else break;n++}if(f)for(k=n;k<b.length;){j=b.charAt(k);if(j=="("){h=c.substr(f-d+1);c=c.substr(0,f-d);n=k;break}if(g(j))k++; -else break}d={index:d,text:c};if(Ka.hasOwnProperty(c))d.fn=d.json=Ka[c];else{var l=Jb(c,a);d.fn=x(function(a,b){return l(a,b)},{assign:function(a,b){return Kb(a,c,b)}})}o.push(d);h&&(o.push({index:f,text:".",json:!1}),o.push({index:f+1,text:h,json:!1}))}function l(a){var c=n;n++;for(var d="",e=a,f=!1;n<b.length;){var k=b.charAt(n);e+=k;if(f)k=="u"?(k=b.substring(n+1,n+5),k.match(/[\da-f]{4}/i)||h("Invalid unicode escape [\\u"+k+"]"),n+=4,d+=String.fromCharCode(parseInt(k,16))):(f=Lc[k],d+=f?f:k), -f=!1;else if(k=="\\")f=!0;else if(k==a){n++;o.push({index:c,text:e,string:d,json:!0,fn:function(){return d}});return}else d+=k;n++}h("Unterminated quote",c)}for(var o=[],r,n=0,w=[],q,s=":";n<b.length;){q=b.charAt(n);if(c("\"'"))l(q);else if(e(q)||c(".")&&e(d()))k();else if(i(q)){if(j(),"{,".indexOf(s)!=-1&&w[0]=="{"&&(r=o[o.length-1]))r.json=r.text.indexOf(".")==-1}else if(c("(){}[].,;:"))o.push({index:n,text:q,json:":[,".indexOf(s)!=-1&&c("{[")||c("}]:,")}),c("{[")&&w.unshift(q),c("}]")&&w.shift(), -n++;else if(g(q)){n++;continue}else{var m=q+d(),C=Ka[q],A=Ka[m];A?(o.push({index:n,text:m,fn:A}),n+=2):C?(o.push({index:n,text:q,fn:C,json:"[,:".indexOf(s)!=-1&&c("+-")}),n+=1):h("Unexpected next character ",n,n+1)}s=q}return o}function Mc(b,a,c,d){function e(a,c){throw B("Syntax Error: Token '"+c.text+"' "+a+" at column "+(c.index+1)+" of the expression ["+b+"] starting at ["+b.substring(c.index)+"].");}function g(){if(M.length===0)throw B("Unexpected end of expression: "+b);return M[0]}function i(a, -b,c,d){if(M.length>0){var e=M[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=i(b,c,d,f))?(a&&!b.json&&e("is not valid json",b),M.shift(),b):!1}function h(a){f(a)||e("is unexpected, expecting ["+a+"]",i())}function k(a,b){return function(c,d){return a(c,d,b)}}function j(a,b,c){return function(d,e){return b(d,e,a,c)}}function l(){for(var a=[];;)if(M.length>0&&!i("}",")",";","]")&&a.push(v()),!f(";"))return a.length==1?a[0]:function(b,c){for(var d, -e=0;e<a.length;e++){var f=a[e];f&&(d=f(b,c))}return d}}function o(){for(var a=f(),b=c(a.text),d=[];;)if(a=f(":"))d.push(H());else{var e=function(a,c,e){for(var e=[e],f=0;f<d.length;f++)e.push(d[f](a,c));return b.apply(a,e)};return function(){return e}}}function r(){for(var a=n(),b;;)if(b=f("||"))a=j(a,b.fn,n());else return a}function n(){var a=w(),b;if(b=f("&&"))a=j(a,b.fn,n());return a}function w(){var a=q(),b;if(b=f("==","!="))a=j(a,b.fn,w());return a}function q(){var a;a=s();for(var b;b=f("+", -"-");)a=j(a,b.fn,s());if(b=f("<",">","<=",">="))a=j(a,b.fn,q());return a}function s(){for(var a=m(),b;b=f("*","/","%");)a=j(a,b.fn,m());return a}function m(){var a;return f("+")?C():(a=f("-"))?j(W,a.fn,m()):(a=f("!"))?k(a.fn,m()):C()}function C(){var a;if(f("("))a=v(),h(")");else if(f("["))a=A();else if(f("{"))a=K();else{var b=f();(a=b.fn)||e("not a primary expression",b)}for(var c;b=f("(","[",".");)b.text==="("?(a=u(a,c),c=null):b.text==="["?(c=a,a=ea(a)):b.text==="."?(c=a,a=t(a)):e("IMPOSSIBLE"); -return a}function A(){var a=[];if(g().text!="]"){do a.push(H());while(f(","))}h("]");return function(b,c){for(var d=[],e=0;e<a.length;e++)d.push(a[e](b,c));return d}}function K(){var a=[];if(g().text!="}"){do{var b=f(),b=b.string||b.text;h(":");var c=H();a.push({key:b,value:c})}while(f(","))}h("}");return function(b,c){for(var d={},e=0;e<a.length;e++){var f=a[e],k=f.value(b,c);d[f.key]=k}return d}}var W=I(0),y,M=Kc(b,d),H=function(){var a=r(),c,d;return(d=f("="))?(a.assign||e("implies assignment but ["+ -b.substring(0,d.index)+"] can not be assigned to",d),c=r(),function(b,d){return a.assign(b,c(b,d),d)}):a},u=function(a,b){var c=[];if(g().text!=")"){do c.push(H());while(f(","))}h(")");return function(d,e){for(var f=[],k=b?b(d,e):d,h=0;h<c.length;h++)f.push(c[h](d,e));h=a(d,e)||D;return h.apply?h.apply(k,f):h(f[0],f[1],f[2],f[3],f[4])}},t=function(a){var b=f().text,c=Jb(b,d);return x(function(b,d){return c(a(b,d),d)},{assign:function(c,d,e){return Kb(a(c,e),b,d)}})},ea=function(a){var b=H();h("]"); -return x(function(c,d){var e=a(c,d),f=b(c,d),k;if(!e)return p;if((e=e[f])&&e.then){k=e;if(!("$$v"in e))k.$$v=p,k.then(function(a){k.$$v=a});e=e.$$v}return e},{assign:function(c,d,e){return a(c,e)[b(c,e)]=d}})},v=function(){for(var a=H(),b;;)if(b=f("|"))a=j(a,b.fn,o());else return a};a?(H=r,u=t=ea=v=function(){e("is not valid json",{text:b,index:0})},y=C()):y=l();M.length!==0&&e("is an unexpected token",M[0]);return y}function Kb(b,a,c){for(var a=a.split("."),d=0;a.length>1;d++){var e=a.shift(),g= -b[e];g||(g={},b[e]=g);b=g}return b[a.shift()]=c}function fb(b,a,c){if(!a)return b;for(var a=a.split("."),d,e=b,g=a.length,i=0;i<g;i++)d=a[i],b&&(b=(e=b)[d]);return!c&&N(b)?Va(e,b):b}function Lb(b,a,c,d,e){return function(g,i){var f=i&&i.hasOwnProperty(b)?i:g,h;if(f===null||f===p)return f;if((f=f[b])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!a||f===null||f===p)return f;if((f=f[a])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!c||f=== -null||f===p)return f;if((f=f[c])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!d||f===null||f===p)return f;if((f=f[d])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}if(!e||f===null||f===p)return f;if((f=f[e])&&f.then){if(!("$$v"in f))h=f,h.$$v=p,h.then(function(a){h.$$v=a});f=f.$$v}return f}}function Jb(b,a){if(hb.hasOwnProperty(b))return hb[b];var c=b.split("."),d=c.length,e;if(a)e=d<6?Lb(c[0],c[1],c[2],c[3],c[4]):function(a,b){var e=0, -k;do k=Lb(c[e++],c[e++],c[e++],c[e++],c[e++])(a,b),b=p,a=k;while(e<d);return k};else{var g="var l, fn, p;\n";m(c,function(a,b){g+="if(s === null || s === undefined) return s;\nl=s;\ns="+(b?"s":'((k&&k.hasOwnProperty("'+a+'"))?k:s)')+'["'+a+'"];\nif (s && s.then) {\n if (!("$$v" in s)) {\n p=s;\n p.$$v = undefined;\n p.then(function(v) {p.$$v=v;});\n}\n s=s.$$v\n}\n'});g+="return s;";e=Function("s","k",g);e.toString=function(){return g}}return hb[b]=e}function Nc(){var b={};this.$get=["$filter","$sniffer", -function(a,c){return function(d){switch(typeof d){case "string":return b.hasOwnProperty(d)?b[d]:b[d]=Mc(d,!1,a,c.csp);case "function":return d;default:return D}}}]}function Oc(){this.$get=["$rootScope","$exceptionHandler",function(b,a){return Pc(function(a){b.$evalAsync(a)},a)}]}function Pc(b,a){function c(a){return a}function d(a){return i(a)}var e=function(){var f=[],h,k;return k={resolve:function(a){if(f){var c=f;f=p;h=g(a);c.length&&b(function(){for(var a,b=0,d=c.length;b<d;b++)a=c[b],h.then(a[0], -a[1])})}},reject:function(a){k.resolve(i(a))},promise:{then:function(b,k){var g=e(),i=function(d){try{g.resolve((b||c)(d))}catch(e){a(e),g.reject(e)}},n=function(b){try{g.resolve((k||d)(b))}catch(c){a(c),g.reject(c)}};f?f.push([i,n]):h.then(i,n);return g.promise}}}},g=function(a){return a&&a.then?a:{then:function(c){var d=e();b(function(){d.resolve(c(a))});return d.promise}}},i=function(a){return{then:function(c,k){var g=e();b(function(){g.resolve((k||d)(a))});return g.promise}}};return{defer:e,reject:i, -when:function(f,h,k){var j=e(),l,o=function(b){try{return(h||c)(b)}catch(d){return a(d),i(d)}},r=function(b){try{return(k||d)(b)}catch(c){return a(c),i(c)}};b(function(){g(f).then(function(a){l||(l=!0,j.resolve(g(a).then(o,r)))},function(a){l||(l=!0,j.resolve(r(a)))})});return j.promise},all:function(a){var b=e(),c=a.length,d=[];c?m(a,function(a,e){g(a).then(function(a){e in d||(d[e]=a,--c||b.resolve(d))},function(a){e in d||b.reject(a)})}):b.resolve(d);return b.promise}}}function Qc(){var b={};this.when= -function(a,c){b[a]=x({reloadOnSearch:!0},c);if(a){var d=a[a.length-1]=="/"?a.substr(0,a.length-1):a+"/";b[d]={redirectTo:a}}return this};this.otherwise=function(a){this.when(null,a);return this};this.$get=["$rootScope","$location","$routeParams","$q","$injector","$http","$templateCache",function(a,c,d,e,g,i,f){function h(){var b=k(),h=r.current;if(b&&h&&b.$route===h.$route&&ha(b.pathParams,h.pathParams)&&!b.reloadOnSearch&&!o)h.params=b.params,V(h.params,d),a.$broadcast("$routeUpdate",h);else if(b|| -h)o=!1,a.$broadcast("$routeChangeStart",b,h),(r.current=b)&&b.redirectTo&&(F(b.redirectTo)?c.path(j(b.redirectTo,b.params)).search(b.params).replace():c.url(b.redirectTo(b.pathParams,c.path(),c.search())).replace()),e.when(b).then(function(){if(b){var a=[],c=[],d;m(b.resolve||{},function(b,d){a.push(d);c.push(F(b)?g.get(b):g.invoke(b))});if(!v(d=b.template))if(v(d=b.templateUrl))d=i.get(d,{cache:f}).then(function(a){return a.data});v(d)&&(a.push("$template"),c.push(d));return e.all(c).then(function(b){var c= -{};m(b,function(b,d){c[a[d]]=b});return c})}}).then(function(c){if(b==r.current){if(b)b.locals=c,V(b.params,d);a.$broadcast("$routeChangeSuccess",b,h)}},function(c){b==r.current&&a.$broadcast("$routeChangeError",b,h,c)})}function k(){var a,d;m(b,function(b,e){if(!d&&(a=l(c.path(),e)))d=ya(b,{params:x({},c.search(),a),pathParams:a}),d.$route=b});return d||b[null]&&ya(b[null],{params:{},pathParams:{}})}function j(a,b){var c=[];m((a||"").split(":"),function(a,d){if(d==0)c.push(a);else{var e=a.match(/(\w+)(.*)/), -f=e[1];c.push(b[f]);c.push(e[2]||"");delete b[f]}});return c.join("")}var l=function(a,b){var c="^"+b.replace(/([\.\\\(\)\^\$])/g,"\\$1")+"$",d=[],e={};m(b.split(/\W/),function(a){if(a){var b=RegExp(":"+a+"([\\W])");c.match(b)&&(c=c.replace(b,"([^\\/]*)$1"),d.push(a))}});var f=a.match(RegExp(c));f&&m(d,function(a,b){e[a]=f[b+1]});return f?e:null},o=!1,r={routes:b,reload:function(){o=!0;a.$evalAsync(h)}};a.$on("$locationChangeSuccess",h);return r}]}function Rc(){this.$get=I({})}function Sc(){var b= -10;this.digestTtl=function(a){arguments.length&&(b=a);return b};this.$get=["$injector","$exceptionHandler","$parse",function(a,c,d){function e(){this.$id=xa();this.$$phase=this.$parent=this.$$watchers=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null;this["this"]=this.$root=this;this.$$asyncQueue=[];this.$$listeners={}}function g(a){if(h.$$phase)throw B(h.$$phase+" already in progress");h.$$phase=a}function i(a,b){var c=d(a);ra(c,b);return c}function f(){}e.prototype={$new:function(a){if(N(a))throw B("API-CHANGE: Use $controller to instantiate controllers."); -a?(a=new e,a.$root=this.$root):(a=function(){},a.prototype=this,a=new a,a.$id=xa());a["this"]=a;a.$$listeners={};a.$parent=this;a.$$asyncQueue=[];a.$$watchers=a.$$nextSibling=a.$$childHead=a.$$childTail=null;a.$$prevSibling=this.$$childTail;this.$$childHead?this.$$childTail=this.$$childTail.$$nextSibling=a:this.$$childHead=this.$$childTail=a;return a},$watch:function(a,b,c){var d=i(a,"watch"),e=this.$$watchers,g={fn:b,last:f,get:d,exp:a,eq:!!c};if(!N(b)){var h=i(b||D,"listener");g.fn=function(a,b, -c){h(c)}}if(!e)e=this.$$watchers=[];e.unshift(g);return function(){Ua(e,g)}},$digest:function(){var a,d,e,i,r,n,m,q=b,s,p=[],C,A;g("$digest");do{m=!1;s=this;do{for(r=s.$$asyncQueue;r.length;)try{s.$eval(r.shift())}catch(K){c(K)}if(i=s.$$watchers)for(n=i.length;n--;)try{if(a=i[n],(d=a.get(s))!==(e=a.last)&&!(a.eq?ha(d,e):typeof d=="number"&&typeof e=="number"&&isNaN(d)&&isNaN(e)))m=!0,a.last=a.eq?V(d):d,a.fn(d,e===f?d:e,s),q<5&&(C=4-q,p[C]||(p[C]=[]),A=N(a.exp)?"fn: "+(a.exp.name||a.exp.toString()): -a.exp,A+="; newVal: "+da(d)+"; oldVal: "+da(e),p[C].push(A))}catch(W){c(W)}if(!(i=s.$$childHead||s!==this&&s.$$nextSibling))for(;s!==this&&!(i=s.$$nextSibling);)s=s.$parent}while(s=i);if(m&&!q--)throw h.$$phase=null,B(b+" $digest() iterations reached. Aborting!\nWatchers fired in the last 5 iterations: "+da(p));}while(m||r.length);h.$$phase=null},$destroy:function(){if(h!=this){var a=this.$parent;this.$broadcast("$destroy");if(a.$$childHead==this)a.$$childHead=this.$$nextSibling;if(a.$$childTail== -this)a.$$childTail=this.$$prevSibling;if(this.$$prevSibling)this.$$prevSibling.$$nextSibling=this.$$nextSibling;if(this.$$nextSibling)this.$$nextSibling.$$prevSibling=this.$$prevSibling;this.$parent=this.$$nextSibling=this.$$prevSibling=this.$$childHead=this.$$childTail=null}},$eval:function(a,b){return d(a)(this,b)},$evalAsync:function(a){this.$$asyncQueue.push(a)},$apply:function(a){try{return g("$apply"),this.$eval(a)}catch(b){c(b)}finally{h.$$phase=null;try{h.$digest()}catch(d){throw c(d),d;}}}, -$on:function(a,b){var c=this.$$listeners[a];c||(this.$$listeners[a]=c=[]);c.push(b);return function(){c[za(c,b)]=null}},$emit:function(a,b){var d=[],e,f=this,g=!1,h={name:a,targetScope:f,stopPropagation:function(){g=!0},preventDefault:function(){h.defaultPrevented=!0},defaultPrevented:!1},i=[h].concat(ia.call(arguments,1)),m,p;do{e=f.$$listeners[a]||d;h.currentScope=f;m=0;for(p=e.length;m<p;m++)if(e[m])try{if(e[m].apply(null,i),g)return h}catch(C){c(C)}else e.splice(m,1),m--,p--;f=f.$parent}while(f); -return h},$broadcast:function(a,b){var d=this,e=this,f={name:a,targetScope:this,preventDefault:function(){f.defaultPrevented=!0},defaultPrevented:!1},g=[f].concat(ia.call(arguments,1)),h,i;do{d=e;f.currentScope=d;e=d.$$listeners[a]||[];h=0;for(i=e.length;h<i;h++)if(e[h])try{e[h].apply(null,g)}catch(m){c(m)}else e.splice(h,1),h--,i--;if(!(e=d.$$childHead||d!==this&&d.$$nextSibling))for(;d!==this&&!(e=d.$$nextSibling);)d=d.$parent}while(d=e);return f}};var h=new e;return h}]}function Tc(){this.$get= -["$window",function(b){var a={},c=G((/android (\d+)/.exec(E(b.navigator.userAgent))||[])[1]);return{history:!(!b.history||!b.history.pushState||c<4),hashchange:"onhashchange"in b&&(!b.document.documentMode||b.document.documentMode>7),hasEvent:function(c){if(c=="input"&&aa==9)return!1;if(t(a[c])){var e=b.document.createElement("div");a[c]="on"+c in e}return a[c]},csp:!1}}]}function Uc(){this.$get=I(U)}function Mb(b){var a={},c,d,e;if(!b)return a;m(b.split("\n"),function(b){e=b.indexOf(":");c=E(R(b.substr(0, -e)));d=R(b.substr(e+1));c&&(a[c]?a[c]+=", "+d:a[c]=d)});return a}function Nb(b){var a=L(b)?b:p;return function(c){a||(a=Mb(b));return c?a[E(c)]||null:a}}function Ob(b,a,c){if(N(c))return c(b,a);m(c,function(c){b=c(b,a)});return b}function Vc(){var b=/^\s*(\[|\{[^\{])/,a=/[\}\]]\s*$/,c=/^\)\]\}',?\n/,d=this.defaults={transformResponse:[function(d){F(d)&&(d=d.replace(c,""),b.test(d)&&a.test(d)&&(d=nb(d,!0)));return d}],transformRequest:[function(a){return L(a)&&Sa.apply(a)!=="[object File]"?da(a):a}], -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,h,k,j){function l(a){function c(a){var b=x({},a,{data:Ob(a.data,a.headers,f)});return 200<=a.status&&a.status<300?b:k.reject(b)}a.method=la(a.method);var e=a.transformRequest|| -d.transformRequest,f=a.transformResponse||d.transformResponse,h=d.headers,h=x({"X-XSRF-TOKEN":b.cookies()["XSRF-TOKEN"]},h.common,h[E(a.method)],a.headers),e=Ob(a.data,Nb(h),e),g;t(a.data)&&delete h["Content-Type"];g=o(a,e,h);g=g.then(c,c);m(w,function(a){g=a(g)});g.success=function(b){g.then(function(c){b(c.data,c.status,c.headers,a)});return g};g.error=function(b){g.then(null,function(c){b(c.data,c.status,c.headers,a)});return g};return g}function o(b,c,d){function e(a,b,c){m&&(200<=a&&a<300?m.put(w, -[a,b,Mb(c)]):m.remove(w));f(b,a,c);h.$apply()}function f(a,c,d){c=Math.max(c,0);(200<=c&&c<300?j.resolve:j.reject)({data:a,status:c,headers:Nb(d),config:b})}function i(){var a=za(l.pendingRequests,b);a!==-1&&l.pendingRequests.splice(a,1)}var j=k.defer(),o=j.promise,m,p,w=r(b.url,b.params);l.pendingRequests.push(b);o.then(i,i);b.cache&&b.method=="GET"&&(m=L(b.cache)?b.cache:n);if(m)if(p=m.get(w))if(p.then)return p.then(i,i),p;else J(p)?f(p[1],p[0],V(p[2])):f(p,200,{});else m.put(w,o);p||a(b.method, -w,c,e,d,b.timeout,b.withCredentials);return o}function r(a,b){if(!b)return a;var c=[];ec(b,function(a,b){a==null||a==p||(L(a)&&(a=da(a)),c.push(encodeURIComponent(b)+"="+encodeURIComponent(a)))});return a+(a.indexOf("?")==-1?"?":"&")+c.join("&")}var n=c("$http"),w=[];m(e,function(a){w.push(F(a)?j.get(a):j.invoke(a))});l.pendingRequests=[];(function(a){m(arguments,function(a){l[a]=function(b,c){return l(x(c||{},{method:a,url:b}))}})})("get","delete","head","jsonp");(function(a){m(arguments,function(a){l[a]= -function(b,c,d){return l(x(d||{},{method:a,url:b,data:c}))}})})("post","put");l.defaults=d;return l}]}function Wc(){this.$get=["$browser","$window","$document",function(b,a,c){return Xc(b,Yc,b.defer,a.angular.callbacks,c[0],a.location.protocol.replace(":",""))}]}function Xc(b,a,c,d,e,g){function i(a,b){var c=e.createElement("script"),d=function(){e.body.removeChild(c);b&&b()};c.type="text/javascript";c.src=a;aa?c.onreadystatechange=function(){/loaded|complete/.test(c.readyState)&&d()}:c.onload=c.onerror= -d;e.body.appendChild(c)}return function(e,h,k,j,l,o,r){function n(a,c,d,e){c=(h.match(Fb)||["",g])[1]=="file"?d?200:404:c;a(c==1223?204:c,d,e);b.$$completeOutstandingRequest(D)}b.$$incOutstandingRequestCount();h=h||b.url();if(E(e)=="jsonp"){var p="_"+(d.counter++).toString(36);d[p]=function(a){d[p].data=a};i(h.replace("JSON_CALLBACK","angular.callbacks."+p),function(){d[p].data?n(j,200,d[p].data):n(j,-2);delete d[p]})}else{var q=new a;q.open(e,h,!0);m(l,function(a,b){a&&q.setRequestHeader(b,a)}); -var s;q.onreadystatechange=function(){q.readyState==4&&n(j,s||q.status,q.responseText,q.getAllResponseHeaders())};if(r)q.withCredentials=!0;q.send(k||"");o>0&&c(function(){s=-1;q.abort()},o)}}}function Zc(){this.$get=function(){return{id:"en-us",NUMBER_FORMATS:{DECIMAL_SEP:".",GROUP_SEP:",",PATTERNS:[{minInt:1,minFrac:0,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(","),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 $c(){this.$get=["$rootScope","$browser","$q","$exceptionHandler",function(b,a,c,d){function e(e,f,h){var k=c.defer(),j=k.promise,l=v(h)&&!h,f=a.defer(function(){try{k.resolve(e())}catch(a){k.reject(a),d(a)}l||b.$apply()},f),h=function(){delete g[j.$$timeoutId]};j.$$timeoutId=f;g[f]=k;j.then(h,h);return j}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 Pb(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",Qb);a("date",Rb);a("filter",ad);a("json",bd);a("limitTo",cd);a("lowercase",dd);a("number",Sb);a("orderBy",Tb);a("uppercase",ed)}function ad(){return function(b,a){if(!(b instanceof Array))return b;var c=[];c.check=function(a){for(var b=0;b<c.length;b++)if(!c[b](a))return!1;return!0};var d=function(a,b){if(b.charAt(0)=== -"!")return!d(a,b.substr(1));switch(typeof a){case "boolean":case "number":case "string":return(""+a).toLowerCase().indexOf(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;c<a.length;c++)if(d(a[c],b))return!0;return!1;default:return!1}};switch(typeof a){case "boolean":case "number":case "string":a={$:a};case "object":for(var e in a)e=="$"?function(){var b=(""+a[e]).toLowerCase();b&&c.push(function(a){return d(a,b)})}():function(){var b=e,f= -(""+a[e]).toLowerCase();f&&c.push(function(a){return d(fb(a,b),f)})}();break;case "function":c.push(a);break;default:return b}for(var g=[],i=0;i<b.length;i++){var f=b[i];c.check(f)&&g.push(f)}return g}}function Qb(b){var a=b.NUMBER_FORMATS;return function(b,d){if(t(d))d=a.CURRENCY_SYM;return Ub(b,a.PATTERNS[1],a.GROUP_SEP,a.DECIMAL_SEP,2).replace(/\u00A4/g,d)}}function Sb(b){var a=b.NUMBER_FORMATS;return function(b,d){return Ub(b,a.PATTERNS[0],a.GROUP_SEP,a.DECIMAL_SEP,d)}}function Ub(b,a,c,d,e){if(isNaN(b)|| -!isFinite(b))return"";var g=b<0,b=Math.abs(b),i=b+"",f="",h=[],k=!1;if(i.indexOf("e")!==-1){var j=i.match(/([\d\.]+)e(-?)(\d+)/);j&&j[2]=="-"&&j[3]>e+1?i="0":(f=i,k=!0)}if(!k){i=(i.split(Vb)[1]||"").length;t(e)&&(e=Math.min(Math.max(a.minFrac,i),a.maxFrac));var i=Math.pow(10,e),b=Math.round(b*i)/i,b=(""+b).split(Vb),i=b[0],b=b[1]||"",k=0,j=a.lgSize,l=a.gSize;if(i.length>=j+l)for(var k=i.length-j,o=0;o<k;o++)(k-o)%l===0&&o!==0&&(f+=c),f+=i.charAt(o);for(o=k;o<i.length;o++)(i.length-o)%j===0&&o!==0&& -(f+=c),f+=i.charAt(o);for(;b.length<e;)b+="0";e&&(f+=d+b.substr(0,e))}h.push(g?a.negPre:a.posPre);h.push(f);h.push(g?a.negSuf:a.posSuf);return h.join("")}function ib(b,a,c){var d="";b<0&&(d="-",b=-b);for(b=""+b;b.length<a;)b="0"+b;c&&(b=b.substr(b.length-a));return d+b}function P(b,a,c,d){return function(e){e=e["get"+b]();if(c>0||e>-c)e+=c;e===0&&c==-12&&(e=12);return ib(e,a,d)}}function La(b,a){return function(c,d){var e=c["get"+b](),g=la(a?"SHORT"+b:b);return d[g][e]}}function Rb(b){function a(a){var b; -if(b=a.match(c)){var a=new Date(0),g=0,i=0;b[9]&&(g=G(b[9]+b[10]),i=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)-i,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,e){var g="",i=[],f,h,e=e||"mediumDate",e=b.DATETIME_FORMATS[e]||e;F(c)&&(c=fd.test(c)?G(c):a(c));wa(c)&&(c=new Date(c));if(!na(c))return c;for(;e;)(h=gd.exec(e))?(i=i.concat(ia.call(h, -1)),e=i.pop()):(i.push(e),e=null);m(i,function(a){f=hd[a];g+=f?f(c,b.DATETIME_FORMATS):a.replace(/(^'|'$)/g,"").replace(/''/g,"'")});return g}}function bd(){return function(b){return da(b,!0)}}function cd(){return function(b,a){if(!(b instanceof Array))return b;var a=G(a),c=[],d,e;if(!b||!(b instanceof Array))return c;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(;d<e;d++)c.push(b[d]);return c}}function Tb(b){return function(a,c,d){function e(a,b){return Wa(b)? -function(b,c){return a(c,b)}:a}if(!(a instanceof Array))return a;if(!c)return a;for(var c=J(c)?c:[c],c=Ta(c,function(a){var c=!1,d=a||ma;if(F(a)){if(a.charAt(0)=="+"||a.charAt(0)=="-")c=a.charAt(0)=="-",a=a.substring(1);d=b(a)}return e(function(a,b){var c;c=d(a);var e=d(b),f=typeof c,g=typeof e;f==g?(f=="string"&&(c=c.toLowerCase()),f=="string"&&(e=e.toLowerCase()),c=c===e?0:c<e?-1:1):c=f<g?-1:1;return c},c)}),g=[],i=0;i<a.length;i++)g.push(a[i]);return g.sort(e(function(a,b){for(var d=0;d<c.length;d++){var e= -c[d](a,b);if(e!==0)return e}return 0},d))}}function S(b){N(b)&&(b={link:b});b.restrict=b.restrict||"AC";return I(b)}function Wb(b,a){function c(a,c){c=c?"-"+$a(c,"-"):"";b.removeClass((a?Ma:Na)+c).addClass((a?Na:Ma)+c)}var d=this,e=b.parent().controller("form")||Oa,g=0,i=d.$error={};d.$name=a.name;d.$dirty=!1;d.$pristine=!0;d.$valid=!0;d.$invalid=!1;e.$addControl(d);b.addClass(Pa);c(!0);d.$addControl=function(a){a.$name&&!d.hasOwnProperty(a.$name)&&(d[a.$name]=a)};d.$removeControl=function(a){a.$name&& -d[a.$name]===a&&delete d[a.$name];m(i,function(b,c){d.$setValidity(c,!0,a)})};d.$setValidity=function(a,b,k){var j=i[a];if(b){if(j&&(Ua(j,k),!j.length)){g--;if(!g)c(b),d.$valid=!0,d.$invalid=!1;i[a]=!1;c(!0,a);e.$setValidity(a,!0,d)}}else{g||c(b);if(j){if(za(j,k)!=-1)return}else i[a]=j=[],g++,c(!1,a),e.$setValidity(a,!1,d);j.push(k);d.$valid=!1;d.$invalid=!0}};d.$setDirty=function(){b.removeClass(Pa).addClass(Xb);d.$dirty=!0;d.$pristine=!1;e.$setDirty()}}function T(b){return t(b)||b===""||b===null|| -b!==b}function Qa(b,a,c,d,e,g){var i=function(){var c=R(a.val());d.$viewValue!==c&&b.$apply(function(){d.$setViewValue(c)})};if(e.hasEvent("input"))a.bind("input",i);else{var f;a.bind("keydown",function(a){a=a.keyCode;a===91||15<a&&a<19||37<=a&&a<=40||f||(f=g.defer(function(){i();f=null}))});a.bind("change",i)}d.$render=function(){a.val(T(d.$viewValue)?"":d.$viewValue)};var h=c.ngPattern,k=function(a,b){return T(b)||a.test(b)?(d.$setValidity("pattern",!0),b):(d.$setValidity("pattern",!1),p)};h&&(h.match(/^\/(.*)\/$/)? -(h=RegExp(h.substr(1,h.length-2)),e=function(a){return k(h,a)}):e=function(a){var c=b.$eval(h);if(!c||!c.test)throw new B("Expected "+h+" to be a RegExp but was "+c);return k(c,a)},d.$formatters.push(e),d.$parsers.push(e));if(c.ngMinlength){var j=G(c.ngMinlength),e=function(a){return!T(a)&&a.length<j?(d.$setValidity("minlength",!1),p):(d.$setValidity("minlength",!0),a)};d.$parsers.push(e);d.$formatters.push(e)}if(c.ngMaxlength){var l=G(c.ngMaxlength),c=function(a){return!T(a)&&a.length>l?(d.$setValidity("maxlength", -!1),p):(d.$setValidity("maxlength",!0),a)};d.$parsers.push(c);d.$formatters.push(c)}}function jb(b,a){b="ngClass"+b;return S(function(c,d,e){function g(b,d){if(a===!0||c.$index%2===a)d&&b!==d&&i(d),f(b)}function i(a){L(a)&&!J(a)&&(a=Ta(a,function(a,b){if(a)return b}));d.removeClass(J(a)?a.join(" "):a)}function f(a){L(a)&&!J(a)&&(a=Ta(a,function(a,b){if(a)return b}));a&&d.addClass(J(a)?a.join(" "):a)}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 j=d%2;j!==g%2&&(j==a?f(c.$eval(e[b])):i(c.$eval(e[b])))})})}var E=function(b){return F(b)?b.toLowerCase():b},la=function(b){return F(b)?b.toUpperCase():b},B=U.Error,aa=G((/msie (\d+)/.exec(E(navigator.userAgent))||[])[1]),u,ja,ia=[].slice,Ra=[].push,Sa=Object.prototype.toString,Yb=U.angular||(U.angular={}),ta,Cb,Z=["0","0","0"];D.$inject=[];ma.$inject=[];Cb=aa<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,id={full:"1.0.3",major:1,minor:0,dot:3,codeName:"bouncy-thunder"},Ba=Q.cache={},Aa=Q.expando="ng-"+(new Date).getTime(),oc=1,Zb=U.document.addEventListener?function(b,a,c){b.addEventListener(a,c,!1)}:function(b,a,c){b.attachEvent("on"+a,c)},db=U.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=Q.prototype={ready:function(b){function a(){c||(c=!0,b())} -var c=!1;this.bind("DOMContentLoaded",a);Q(U).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+b])},length:0,push:Ra,sort:[].sort,splice:[].splice},Ea={};m("multiple,selected,checked,disabled,readOnly,required".split(","),function(b){Ea[E(b)]=b});var zb={};m("input,select,option,textarea,button,form".split(","),function(b){zb[la(b)]=!0});m({data:ub,inheritedData:Da,scope:function(b){return Da(b, -"$scope")},controller:xb,injector:function(b){return Da(b,"$injector")},removeAttr:function(b,a){b.removeAttribute(a)},hasClass:Ca,css:function(b,a,c){a=rb(a);if(v(c))b.style[a]=c;else{var d;aa<=8&&(d=b.currentStyle&&b.currentStyle[a],d===""&&(d="auto"));d=d||b.style[a];aa<=8&&(d=d===""?p:d);return d}},attr:function(b,a,c){var d=E(a);if(Ea[d])if(v(c))c?(b[a]=!0,b.setAttribute(a,d)):(b[a]=!1,b.removeAttribute(d));else return b[a]||(b.attributes.getNamedItem(a)||D).specified?d:p;else if(v(c))b.setAttribute(a, -c);else if(b.getAttribute)return b=b.getAttribute(a,2),b===null?p:b},prop:function(b,a,c){if(v(c))b[a]=c;else return b[a]},text:x(aa<9?function(b,a){if(b.nodeType==1){if(t(a))return b.innerText;b.innerText=a}else{if(t(a))return b.nodeValue;b.nodeValue=a}}:function(b,a){if(t(a))return b.textContent;b.textContent=a},{$dv:""}),val:function(b,a){if(t(a))return b.value;b.value=a},html:function(b,a){if(t(a))return b.innerHTML;for(var c=0,d=b.childNodes;c<d.length;c++)sa(d[c]);b.innerHTML=a}},function(b, -a){Q.prototype[a]=function(a,d){var e,g;if((b.length==2&&b!==Ca&&b!==xb?a:d)===p)if(L(a)){for(e=0;e<this.length;e++)if(b===ub)b(this[e],a);else for(g in a)b(this[e],g,a[g]);return this}else{if(this.length)return b(this[0],a,d)}else{for(e=0;e<this.length;e++)b(this[e],a,d);return this}return b.$dv}});m({removeData:sb,dealoc:sa,bind:function a(c,d,e){var g=$(c,"events"),i=$(c,"handle");g||$(c,"events",g={});i||$(c,"handle",i=pc(c,g));m(d.split(" "),function(d){var h=g[d];if(!h){if(d=="mouseenter"|| -d=="mouseleave"){var k=0;g.mouseenter=[];g.mouseleave=[];a(c,"mouseover",function(a){k++;k==1&&i(a,"mouseenter")});a(c,"mouseout",function(a){k--;k==0&&i(a,"mouseleave")})}else Zb(c,d,i),g[d]=[];h=g[d]}h.push(e)})},unbind:tb,replaceWith:function(a,c){var d,e=a.parentNode;sa(a);m(new Q(c),function(c){d?e.insertBefore(c,d.nextSibling):e.replaceChild(c,a);d=c})},children:function(a){var c=[];m(a.childNodes,function(a){a.nodeName!="#text"&&c.push(a)});return c},contents:function(a){return a.childNodes}, -append:function(a,c){m(new Q(c),function(c){a.nodeType===1&&a.appendChild(c)})},prepend:function(a,c){if(a.nodeType===1){var d=a.firstChild;m(new Q(c),function(c){d?a.insertBefore(c,d):(a.appendChild(c),d=c)})}},wrap:function(a,c){var c=u(c)[0],d=a.parentNode;d&&d.replaceChild(c,a);c.appendChild(a)},remove:function(a){sa(a);var c=a.parentNode;c&&c.removeChild(a)},after:function(a,c){var d=a,e=a.parentNode;m(new Q(c),function(a){e.insertBefore(a,d.nextSibling);d=a})},addClass:wb,removeClass:vb,toggleClass:function(a, -c,d){t(d)&&(d=!Ca(a,c));(d?wb:vb)(a,c)},parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},next:function(a){return a.nextSibling},find:function(a,c){return a.getElementsByTagName(c)},clone:cb,triggerHandler:function(a,c){var d=($(a,"events")||{})[c];m(d,function(c){c.call(a,null)})}},function(a,c){Q.prototype[c]=function(c,e){for(var g,i=0;i<this.length;i++)g==p?(g=a(this[i],c,e),g!==p&&(g=u(g))):bb(g,a(this[i],c,e));return g==p?this:g}});Fa.prototype={put:function(a,c){this[ga(a)]= -c},get:function(a){return this[ga(a)]},remove:function(a){var c=this[a=ga(a)];delete this[a];return c}};eb.prototype={push:function(a,c){var d=this[a=ga(a)];d?d.push(c):this[a]=[c]},shift:function(a){var c=this[a=ga(a)];if(c)return c.length==1?(delete this[a],c[0]):c.shift()},peek:function(a){if(a=this[ga(a)])return a[0]}};var rc=/^function\s*[^\(]*\(\s*([^\)]*)\)/m,sc=/,/,tc=/^\s*(_?)(\S+?)\1\s*$/,qc=/((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg,Db="Non-assignable model expression: ";Bb.$inject=["$provide"]; -var Ac=/^(x[\:\-_]|data[\:\-_])/i,Fb=/^([^:]+):\/\/(\w+:{0,1}\w*@)?([\w\.-]*)(:([0-9]+))?(\/[^\?#]*)?(\?([^#]*))?(#(.*))?$/,$b=/^([^\?#]*)?(\?([^#]*))?(#(.*))?$/,Hc=$b,Gb={http:80,https:443,ftp:21};gb.prototype={$$replace:!1,absUrl:Ja("$$absUrl"),url:function(a,c){if(t(a))return this.$$url;var d=$b.exec(a);d[1]&&this.path(decodeURIComponent(d[1]));if(d[2]||d[1])this.search(d[3]||"");this.hash(d[5]||"",c);return this},protocol:Ja("$$protocol"),host:Ja("$$host"),port:Ja("$$port"),path:Ib("$$path",function(a){return a.charAt(0)== -"/"?a:"/"+a}),search:function(a,c){if(t(a))return this.$$search;v(c)?c===null?delete this.$$search[a]:this.$$search[a]=c:this.$$search=F(a)?Xa(a):a;this.$$compose();return this},hash:Ib("$$hash",ma),replace:function(){this.$$replace=!0;return this}};Ia.prototype=ya(gb.prototype);Hb.prototype=ya(Ia.prototype);var Ka={"null":function(){return null},"true":function(){return!0},"false":function(){return!1},undefined:D,"+":function(a,c,d,e){d=d(a,c);e=e(a,c);return v(d)?v(e)?d+e:d:v(e)?e:p},"-":function(a, -c,d,e){d=d(a,c);e=e(a,c);return(v(d)?d:0)-(v(e)?e:0)},"*":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)},"=":D,"==":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 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)}},Lc={n:"\n",f:"\u000c",r:"\r",t:"\t",v:"\u000b","'":"'",'"':'"'},hb={},Yc=U.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 new B("This browser does not support XMLHttpRequest."); -};Pb.$inject=["$provide"];Qb.$inject=["$locale"];Sb.$inject=["$locale"];var Vb=".",hd={yyyy:P("FullYear",4),yy:P("FullYear",2,0,!0),y:P("FullYear",1),MMMM:La("Month"),MMM:La("Month",!0),MM:P("Month",2,1),M:P("Month",1,1),dd:P("Date",2),d:P("Date",1),HH:P("Hours",2),H:P("Hours",1),hh:P("Hours",2,-12),h:P("Hours",1,-12),mm:P("Minutes",2),m:P("Minutes",1),ss:P("Seconds",2),s:P("Seconds",1),EEEE:La("Day"),EEE:La("Day",!0),a:function(a,c){return a.getHours()<12?c.AMPMS[0]:c.AMPMS[1]},Z:function(a){a=a.getTimezoneOffset(); -return ib(a/60,2)+ib(Math.abs(a%60),2)}},gd=/((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/,fd=/^\d+$/;Rb.$inject=["$locale"];var dd=I(E),ed=I(la);Tb.$inject=["$parse"];var jd=I({restrict:"E",compile:function(a,c){c.href||c.$set("href","");return function(a,c){c.bind("click",function(a){if(!c.attr("href"))return a.preventDefault(),!1})}}}),kb={};m(Ea,function(a,c){var d=fa("ng-"+c);kb[d]=function(){return{priority:100,compile:function(){return function(a,g,i){a.$watch(i[d], -function(a){i.$set(c,!!a)})}}}}});m(["src","href"],function(a){var c=fa("ng-"+a);kb[c]=function(){return{priority:99,link:function(d,e,g){g.$observe(c,function(c){c&&(g.$set(a,c),aa&&e.prop(a,c))})}}}});var Oa={$addControl:D,$removeControl:D,$setValidity:D,$setDirty:D};Wb.$inject=["$element","$attrs","$scope"];var Ra=function(a){return["$timeout",function(c){var d={name:"form",restrict:"E",controller:Wb,compile:function(){return{pre:function(a,d,i,f){if(!i.action){var h=function(a){a.preventDefault? -a.preventDefault():a.returnValue=!1};Zb(d[0],"submit",h);d.bind("$destroy",function(){c(function(){db(d[0],"submit",h)},0,!1)})}var k=d.parent().controller("form"),j=i.name||i.ngForm;j&&(a[j]=f);k&&d.bind("$destroy",function(){k.$removeControl(f);j&&(a[j]=p);x(f,Oa)})}}}};return a?x(V(d),{restrict:"EAC"}):d}]},kd=Ra(),ld=Ra(!0),md=/^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/,nd=/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4}$/,od=/^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/, -ac={text:Qa,number:function(a,c,d,e,g,i){Qa(a,c,d,e,g,i);e.$parsers.push(function(a){var c=T(a);return c||od.test(a)?(e.$setValidity("number",!0),a===""?null:c?a:parseFloat(a)):(e.$setValidity("number",!1),p)});e.$formatters.push(function(a){return T(a)?"":""+a});if(d.min){var f=parseFloat(d.min),a=function(a){return!T(a)&&a<f?(e.$setValidity("min",!1),p):(e.$setValidity("min",!0),a)};e.$parsers.push(a);e.$formatters.push(a)}if(d.max){var h=parseFloat(d.max),d=function(a){return!T(a)&&a>h?(e.$setValidity("max", -!1),p):(e.$setValidity("max",!0),a)};e.$parsers.push(d);e.$formatters.push(d)}e.$formatters.push(function(a){return T(a)||wa(a)?(e.$setValidity("number",!0),a):(e.$setValidity("number",!1),p)})},url:function(a,c,d,e,g,i){Qa(a,c,d,e,g,i);a=function(a){return T(a)||md.test(a)?(e.$setValidity("url",!0),a):(e.$setValidity("url",!1),p)};e.$formatters.push(a);e.$parsers.push(a)},email:function(a,c,d,e,g,i){Qa(a,c,d,e,g,i);a=function(a){return T(a)||nd.test(a)?(e.$setValidity("email",!0),a):(e.$setValidity("email", -!1),p)};e.$formatters.push(a);e.$parsers.push(a)},radio:function(a,c,d,e){t(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,i=d.ngFalseValue;F(g)||(g=!0);F(i)||(i=!1);c.bind("click",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:i})},hidden:D,button:D,submit:D,reset:D},bc=["$browser","$sniffer",function(a,c){return{restrict:"E",require:"?ngModel",link:function(d,e,g,i){i&&(ac[E(g.type)]||ac.text)(d,e,g,i,c,a)}}}],Na="ng-valid",Ma="ng-invalid",Pa="ng-pristine",Xb="ng-dirty",pd=["$scope","$exceptionHandler","$attrs","$element","$parse",function(a,c,d,e,g){function i(a,c){c=c?"-"+$a(c,"-"):"";e.removeClass((a?Ma:Na)+c).addClass((a?Na:Ma)+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),h=f.assign;if(!h)throw B(Db+d.ngModel+" ("+pa(e)+")");this.$render=D;var k=e.inheritedData("$formController")||Oa,j=0,l=this.$error={};e.addClass(Pa);i(!0);this.$setValidity=function(a,c){if(l[a]!==!c){if(c){if(l[a]&&j--,!j)i(!0),this.$valid=!0,this.$invalid=!1}else i(!1),this.$invalid=!0,this.$valid=!1,j++;l[a]=!c;i(c,a);k.$setValidity(a, -c,this)}};this.$setViewValue=function(d){this.$viewValue=d;if(this.$pristine)this.$dirty=!0,this.$pristine=!1,e.removeClass(Pa).addClass(Xb),k.$setDirty();m(this.$parsers,function(a){d=a(d)});if(this.$modelValue!==d)this.$modelValue=d,h(a,d),m(this.$viewChangeListeners,function(a){try{a()}catch(d){c(d)}})};var o=this;a.$watch(function(){var c=f(a);if(o.$modelValue!==c){var d=o.$formatters,e=d.length;for(o.$modelValue=c;e--;)c=d[e](c);if(o.$viewValue!==c)o.$viewValue=c,o.$render()}})}],qd=function(){return{require:["ngModel", -"^?form"],controller:pd,link:function(a,c,d,e){var g=e[0],i=e[1]||Oa;i.$addControl(g);c.bind("$destroy",function(){i.$removeControl(g)})}}},rd=I({require:"ngModel",link:function(a,c,d,e){e.$viewChangeListeners.push(function(){a.$eval(d.ngChange)})}}),cc=function(){return{require:"?ngModel",link:function(a,c,d,e){if(e){d.required=!0;var g=function(a){if(d.required&&(T(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)})}}}},sd=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(R(a))});return c});e.$formatters.push(function(a){return J(a)?a.join(", "):p})}}},td=/^(true|false|\d+)$/,ud=function(){return{priority:100,compile:function(a,c){return td.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)})}}}},vd=S(function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBind);a.$watch(d.ngBind,function(a){c.text(a==p?"":a)})}),wd=["$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",function(a){d.text(a)})}}],xd=[function(){return function(a,c,d){c.addClass("ng-binding").data("$binding",d.ngBindHtmlUnsafe);a.$watch(d.ngBindHtmlUnsafe, -function(a){c.html(a||"")})}}],yd=jb("",!0),zd=jb("Odd",0),Ad=jb("Even",1),Bd=S({compile:function(a,c){c.$set("ngCloak",p);a.removeClass("ng-cloak")}}),Cd=[function(){return{scope:!0,controller:"@"}}],Dd=["$sniffer",function(a){return{priority:1E3,compile:function(){a.csp=!0}}}],dc={};m("click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave".split(" "),function(a){var c=fa("ng-"+a);dc[c]=["$parse",function(d){return function(e,g,i){var f=d(i[c]);g.bind(E(a),function(a){e.$apply(function(){f(e, -{$event:a})})})}}]});var Ed=S(function(a,c,d){c.bind("submit",function(){a.$apply(d.ngSubmit)})}),Fd=["$http","$templateCache","$anchorScroll","$compile",function(a,c,d,e){return{restrict:"ECA",terminal:!0,compile:function(g,i){var f=i.ngInclude||i.src,h=i.onload||"",k=i.autoscroll;return function(g,i){var o=0,m,n=function(){m&&(m.$destroy(),m=null);i.html("")};g.$watch(f,function(f){var p=++o;f?a.get(f,{cache:c}).success(function(a){p===o&&(m&&m.$destroy(),m=g.$new(),i.html(a),e(i.contents())(m), -v(k)&&(!k||g.$eval(k))&&d(),m.$emit("$includeContentLoaded"),g.$eval(h))}).error(function(){p===o&&n()}):n()})}}}}],Gd=S({compile:function(){return{pre:function(a,c,d){a.$eval(d.ngInit)}}}}),Hd=S({terminal:!0,priority:1E3}),Id=["$locale","$interpolate",function(a,c){var d=/{}/g;return{restrict:"EA",link:function(e,g,i){var f=i.count,h=g.attr(i.$attr.when),k=i.offset||0,j=e.$eval(h),l={},o=c.startSymbol(),r=c.endSymbol();m(j,function(a,e){l[e]=c(a.replace(d,o+f+"-"+k+r))});e.$watch(function(){var c= -parseFloat(e.$eval(f));return isNaN(c)?"":(j[c]||(c=a.pluralCat(c-k)),l[c](e,g,!0))},function(a){g.text(a)})}}}],Jd=S({transclude:"element",priority:1E3,terminal:!0,compile:function(a,c,d){return function(a,c,i){var f=i.ngRepeat,i=f.match(/^\s*(.+)\s+in\s+(.*)\s*$/),h,k,j;if(!i)throw B("Expected ngRepeat in form of '_item_ in _collection_' but got '"+f+"'.");f=i[1];h=i[2];i=f.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);if(!i)throw B("'item' in 'item in collection' should be identifier or (key, value) but got '"+ -f+"'.");k=i[3]||i[1];j=i[2];var l=new eb;a.$watch(function(a){var e,f,i=a.$eval(h),m=gc(i,!0),p,u=new eb,C,A,v,t,y=c;if(J(i))v=i||[];else{v=[];for(C in i)i.hasOwnProperty(C)&&C.charAt(0)!="$"&&v.push(C);v.sort()}e=0;for(f=v.length;e<f;e++){C=i===v?e:v[e];A=i[C];if(t=l.shift(A)){p=t.scope;u.push(A,t);if(e!==t.index)t.index=e,y.after(t.element);y=t.element}else p=a.$new();p[k]=A;j&&(p[j]=C);p.$index=e;p.$first=e===0;p.$last=e===m-1;p.$middle=!(p.$first||p.$last);t||d(p,function(a){y.after(a);t={scope:p, -element:y=a,index:e};u.push(A,t)})}for(C in l)if(l.hasOwnProperty(C))for(v=l[C];v.length;)A=v.pop(),A.element.remove(),A.scope.$destroy();l=u})}}}),Kd=S(function(a,c,d){a.$watch(d.ngShow,function(a){c.css("display",Wa(a)?"":"none")})}),Ld=S(function(a,c,d){a.$watch(d.ngHide,function(a){c.css("display",Wa(a)?"none":"")})}),Md=S(function(a,c,d){a.$watch(d.ngStyle,function(a,d){d&&a!==d&&m(d,function(a,d){c.css(d,"")});a&&c.css(a)},!0)}),Nd=I({restrict:"EA",compile:function(a,c){var d=c.ngSwitch||c.on, -e={};a.data("ng-switch",e);return function(a,i){var f,h,k;a.$watch(d,function(d){h&&(k.$destroy(),h.remove(),h=k=null);if(f=e["!"+d]||e["?"])a.$eval(c.change),k=a.$new(),f(k,function(a){h=a;i.append(a)})})}}}),Od=S({transclude:"element",priority:500,compile:function(a,c,d){a=a.inheritedData("ng-switch");qa(a);a["!"+c.ngSwitchWhen]=d}}),Pd=S({transclude:"element",priority:500,compile:function(a,c,d){a=a.inheritedData("ng-switch");qa(a);a["?"]=d}}),Qd=S({controller:["$transclude","$element",function(a, -c){a(function(a){c.append(a)})}]}),Rd=["$http","$templateCache","$route","$anchorScroll","$compile","$controller",function(a,c,d,e,g,i){return{restrict:"ECA",terminal:!0,link:function(a,c,k){function j(){var j=d.current&&d.current.locals,k=j&&j.$template;if(k){c.html(k);l&&(l.$destroy(),l=null);var k=g(c.contents()),p=d.current;l=p.scope=a.$new();if(p.controller)j.$scope=l,j=i(p.controller,j),c.contents().data("$ngControllerController",j);k(l);l.$emit("$viewContentLoaded");l.$eval(m);e()}else c.html(""), -l&&(l.$destroy(),l=null)}var l,m=k.onload||"";a.$on("$routeChangeSuccess",j);j()}}}],Sd=["$templateCache",function(a){return{restrict:"E",terminal:!0,compile:function(c,d){d.type=="text/ng-template"&&a.put(d.id,c[0].text)}}}],Td=I({terminal:!0}),Ud=["$compile","$parse",function(a,c){var d=/^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w\d]*)|(?:\(\s*([\$\w][\$\w\d]*)\s*,\s*([\$\w][\$\w\d]*)\s*\)))\s+in\s+(.*)$/,e={$setViewValue:D};return{restrict:"E",require:["select", -"?ngModel"],controller:["$element","$scope","$attrs",function(a,c,d){var h=this,k={},j=e,l;h.databound=d.ngModel;h.init=function(a,c,d){j=a;l=d};h.addOption=function(c){k[c]=!0;j.$viewValue==c&&(a.val(c),l.parent()&&l.remove())};h.removeOption=function(a){this.hasOption(a)&&(delete k[a],j.$viewValue==a&&this.renderUnknownOption(a))};h.renderUnknownOption=function(c){c="? "+ga(c)+" ?";l.val(c);a.prepend(l);a.val(c);l.prop("selected",!0)};h.hasOption=function(a){return k.hasOwnProperty(a)};c.$on("$destroy", -function(){h.renderUnknownOption=D})}],link:function(e,i,f,h){function k(a,c,d,e){d.$render=function(){var a=d.$viewValue;e.hasOption(a)?(A.parent()&&A.remove(),c.val(a),a===""&&s.prop("selected",!0)):t(a)&&s?c.val(""):e.renderUnknownOption(a)};c.bind("change",function(){a.$apply(function(){A.parent()&&A.remove();d.$setViewValue(c.val())})})}function j(a,c,d){var e;d.$render=function(){var a=new Fa(d.$viewValue);m(c.children(),function(c){c.selected=v(a.get(c.value))})};a.$watch(function(){ha(e,d.$viewValue)|| -(e=V(d.$viewValue),d.$render())});c.bind("change",function(){a.$apply(function(){var a=[];m(c.children(),function(c){c.selected&&a.push(c.value)});d.$setViewValue(a)})})}function l(e,f,g){function h(){var a={"":[]},c=[""],d,i,s,t,u;s=g.$modelValue;t=r(e)||[];var y=l?lb(t):t,A,w,x;w={};u=!1;var z,B;if(n)u=new Fa(s);else if(s===null||q)a[""].push({selected:s===null,id:"",label:""}),u=!0;for(x=0;A=y.length,x<A;x++){w[k]=t[l?w[l]=y[x]:x];d=m(e,w)||"";if(!(i=a[d]))i=a[d]=[],c.push(d);n?d=u.remove(o(e, -w))!=p:(d=s===o(e,w),u=u||d);z=j(e,w);z=z===p?"":z;i.push({id:l?y[x]:x,label:z,selected:d})}!n&&!u&&a[""].unshift({id:"?",label:"",selected:!0});w=0;for(y=c.length;w<y;w++){d=c[w];i=a[d];if(v.length<=w)s={element:C.clone().attr("label",d),label:i.label},t=[s],v.push(t),f.append(s.element);else if(t=v[w],s=t[0],s.label!=d)s.element.attr("label",s.label=d);z=null;x=0;for(A=i.length;x<A;x++)if(d=i[x],u=t[x+1]){z=u.element;if(u.label!==d.label)z.text(u.label=d.label);if(u.id!==d.id)z.val(u.id=d.id);if(u.element.selected!== -d.selected)z.prop("selected",u.selected=d.selected)}else d.id===""&&q?B=q:(B=D.clone()).val(d.id).attr("selected",d.selected).text(d.label),t.push({element:B,label:d.label,id:d.id,selected:d.selected}),z?z.after(B):s.element.append(B),z=B;for(x++;t.length>x;)t.pop().element.remove()}for(;v.length>w;)v.pop()[0].element.remove()}var i;if(!(i=w.match(d)))throw B("Expected ngOptions in form of '_select_ (as _label_)? for (_key_,)?_value_ in _collection_' but got '"+w+"'.");var j=c(i[2]||i[1]),k=i[4]|| -i[6],l=i[5],m=c(i[3]||""),o=c(i[2]?i[1]:k),r=c(i[7]),v=[[{element:f,label:""}]];q&&(a(q)(e),q.removeClass("ng-scope"),q.remove());f.html("");f.bind("change",function(){e.$apply(function(){var a,c=r(e)||[],d={},h,i,j,m,q,s;if(n){i=[];m=0;for(s=v.length;m<s;m++){a=v[m];j=1;for(q=a.length;j<q;j++)if((h=a[j].element)[0].selected)h=h.val(),l&&(d[l]=h),d[k]=c[h],i.push(o(e,d))}}else h=f.val(),h=="?"?i=p:h==""?i=null:(d[k]=c[h],l&&(d[l]=h),i=o(e,d));g.$setViewValue(i)})});g.$render=h;e.$watch(h)}if(h[1]){for(var o= -h[0],r=h[1],n=f.multiple,w=f.ngOptions,q=!1,s,D=u(ca.createElement("option")),C=u(ca.createElement("optgroup")),A=D.clone(),h=0,x=i.children(),E=x.length;h<E;h++)if(x[h].value==""){s=q=x.eq(h);break}o.init(r,q,A);if(n&&(f.required||f.ngRequired)){var y=function(a){r.$setValidity("required",!f.required||a&&a.length);return a};r.$parsers.push(y);r.$formatters.unshift(y);f.$observe("required",function(){y(r.$viewValue)})}w?l(e,i,r):n?j(e,i,r):k(e,i,r,o)}}}}],Vd=["$interpolate",function(a){var c={addOption:D, -removeOption:D};return{restrict:"E",priority:100,compile:function(d,e){if(t(e.value)){var g=a(d.text(),!0);g||e.$set("value",d.text())}return function(a,d,e){var k=d.parent(),j=k.data("$selectController")||k.parent().data("$selectController");j&&j.databound?d.prop("selected",!1):j=c;g?a.$watch(g,function(a,c){e.$set("value",a);a!==c&&j.removeOption(c);j.addOption(a)}):j.addOption(e.value);d.bind("$destroy",function(){j.removeOption(e.value)})}}}}],Wd=I({restrict:"E",terminal:!0});(ja=U.jQuery)?(u= -ja,x(ja.fn,{scope:ua.scope,controller:ua.controller,injector:ua.injector,inheritedData:ua.inheritedData}),ab("remove",!0),ab("empty"),ab("html")):u=Q;Yb.element=u;(function(a){x(a,{bootstrap:pb,copy:V,extend:x,equals:ha,element:u,forEach:m,injector:qb,noop:D,bind:Va,toJson:da,fromJson:nb,identity:ma,isUndefined:t,isDefined:v,isString:F,isFunction:N,isObject:L,isNumber:wa,isElement:fc,isArray:J,version:id,isDate:na,lowercase:E,uppercase:la,callbacks:{counter:0}});ta=lc(U);try{ta("ngLocale")}catch(c){ta("ngLocale", -[]).provider("$locale",Zc)}ta("ng",["ngLocale"],["$provide",function(a){a.provider("$compile",Bb).directive({a:jd,input:bc,textarea:bc,form:kd,script:Sd,select:Ud,style:Wd,option:Vd,ngBind:vd,ngBindHtmlUnsafe:xd,ngBindTemplate:wd,ngClass:yd,ngClassEven:Ad,ngClassOdd:zd,ngCsp:Dd,ngCloak:Bd,ngController:Cd,ngForm:ld,ngHide:Ld,ngInclude:Fd,ngInit:Gd,ngNonBindable:Hd,ngPluralize:Id,ngRepeat:Jd,ngShow:Kd,ngSubmit:Ed,ngStyle:Md,ngSwitch:Nd,ngSwitchWhen:Od,ngSwitchDefault:Pd,ngOptions:Td,ngView:Rd,ngTransclude:Qd, -ngModel:qd,ngList:sd,ngChange:rd,required:cc,ngRequired:cc,ngValue:ud}).directive(kb).directive(dc);a.provider({$anchorScroll:uc,$browser:wc,$cacheFactory:xc,$controller:Bc,$document:Cc,$exceptionHandler:Dc,$filter:Pb,$interpolate:Ec,$http:Vc,$httpBackend:Wc,$location:Ic,$log:Jc,$parse:Nc,$route:Qc,$routeParams:Rc,$rootScope:Sc,$q:Oc,$sniffer:Tc,$templateCache:yc,$timeout:$c,$window:Uc})}])})(Yb);u(ca).ready(function(){jc(ca,pb)})})(window,document);angular.element(document).find("head").append('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak{display:none;}ng\\:form{display:block;}</style>'); |