blob: 754f9aaf60a4f6ca4733d41711083e39091ff1b3 [file] [log] [blame]
Simon Hunt30cc4562014-12-08 12:38:12 -08001/**
2 * @license AngularJS v1.3.5
3 * (c) 2010-2014 Google, Inc. http://angularjs.org
4 * License: MIT
5 */
6(function(window, document, undefined) {'use strict';
7
8/**
9 * @description
10 *
11 * This object provides a utility for producing rich Error messages within
12 * Angular. It can be called as follows:
13 *
14 * var exampleMinErr = minErr('example');
15 * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
16 *
17 * The above creates an instance of minErr in the example namespace. The
18 * resulting error will have a namespaced error code of example.one. The
19 * resulting error will replace {0} with the value of foo, and {1} with the
20 * value of bar. The object is not restricted in the number of arguments it can
21 * take.
22 *
23 * If fewer arguments are specified than necessary for interpolation, the extra
24 * interpolation markers will be preserved in the final string.
25 *
26 * Since data will be parsed statically during a build step, some restrictions
27 * are applied with respect to how minErr instances are created and called.
28 * Instances should have names of the form namespaceMinErr for a minErr created
29 * using minErr('namespace') . Error codes, namespaces and template strings
30 * should all be static strings, not variables or general expressions.
31 *
32 * @param {string} module The namespace to use for the new minErr instance.
33 * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
34 * error from returned function, for cases when a particular type of error is useful.
35 * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
36 */
37
38function minErr(module, ErrorConstructor) {
39 ErrorConstructor = ErrorConstructor || Error;
40 return function() {
41 var code = arguments[0],
42 prefix = '[' + (module ? module + ':' : '') + code + '] ',
43 template = arguments[1],
44 templateArgs = arguments,
45
46 message, i;
47
48 message = prefix + template.replace(/\{\d+\}/g, function(match) {
49 var index = +match.slice(1, -1), arg;
50
51 if (index + 2 < templateArgs.length) {
52 return toDebugString(templateArgs[index + 2]);
53 }
54 return match;
55 });
56
57 message = message + '\nhttp://errors.angularjs.org/1.3.5/' +
58 (module ? module + '/' : '') + code;
59 for (i = 2; i < arguments.length; i++) {
60 message = message + (i == 2 ? '?' : '&') + 'p' + (i - 2) + '=' +
61 encodeURIComponent(toDebugString(arguments[i]));
62 }
63 return new ErrorConstructor(message);
64 };
65}
66
67/* We need to tell jshint what variables are being exported */
68/* global angular: true,
69 msie: true,
70 jqLite: true,
71 jQuery: true,
72 slice: true,
73 splice: true,
74 push: true,
75 toString: true,
76 ngMinErr: true,
77 angularModule: true,
78 uid: true,
79 REGEX_STRING_REGEXP: true,
80 VALIDITY_STATE_PROPERTY: true,
81
82 lowercase: true,
83 uppercase: true,
84 manualLowercase: true,
85 manualUppercase: true,
86 nodeName_: true,
87 isArrayLike: true,
88 forEach: true,
89 sortedKeys: true,
90 forEachSorted: true,
91 reverseParams: true,
92 nextUid: true,
93 setHashKey: true,
94 extend: true,
95 int: true,
96 inherit: true,
97 noop: true,
98 identity: true,
99 valueFn: true,
100 isUndefined: true,
101 isDefined: true,
102 isObject: true,
103 isString: true,
104 isNumber: true,
105 isDate: true,
106 isArray: true,
107 isFunction: true,
108 isRegExp: true,
109 isWindow: true,
110 isScope: true,
111 isFile: true,
112 isBlob: true,
113 isBoolean: true,
114 isPromiseLike: true,
115 trim: true,
116 escapeForRegexp: true,
117 isElement: true,
118 makeMap: true,
119 includes: true,
120 arrayRemove: true,
121 copy: true,
122 shallowCopy: true,
123 equals: true,
124 csp: true,
125 concat: true,
126 sliceArgs: true,
127 bind: true,
128 toJsonReplacer: true,
129 toJson: true,
130 fromJson: true,
131 startingTag: true,
132 tryDecodeURIComponent: true,
133 parseKeyValue: true,
134 toKeyValue: true,
135 encodeUriSegment: true,
136 encodeUriQuery: true,
137 angularInit: true,
138 bootstrap: true,
139 getTestability: true,
140 snake_case: true,
141 bindJQuery: true,
142 assertArg: true,
143 assertArgFn: true,
144 assertNotHasOwnProperty: true,
145 getter: true,
146 getBlockNodes: true,
147 hasOwnProperty: true,
148 createMap: true,
149
150 NODE_TYPE_ELEMENT: true,
151 NODE_TYPE_TEXT: true,
152 NODE_TYPE_COMMENT: true,
153 NODE_TYPE_DOCUMENT: true,
154 NODE_TYPE_DOCUMENT_FRAGMENT: true,
155*/
156
157////////////////////////////////////
158
159/**
160 * @ngdoc module
161 * @name ng
162 * @module ng
163 * @description
164 *
165 * # ng (core module)
166 * The ng module is loaded by default when an AngularJS application is started. The module itself
167 * contains the essential components for an AngularJS application to function. The table below
168 * lists a high level breakdown of each of the services/factories, filters, directives and testing
169 * components available within this core module.
170 *
171 * <div doc-module-components="ng"></div>
172 */
173
174var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
175
176// The name of a form control's ValidityState property.
177// This is used so that it's possible for internal tests to create mock ValidityStates.
178var VALIDITY_STATE_PROPERTY = 'validity';
179
180/**
181 * @ngdoc function
182 * @name angular.lowercase
183 * @module ng
184 * @kind function
185 *
186 * @description Converts the specified string to lowercase.
187 * @param {string} string String to be converted to lowercase.
188 * @returns {string} Lowercased string.
189 */
190var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
191var hasOwnProperty = Object.prototype.hasOwnProperty;
192
193/**
194 * @ngdoc function
195 * @name angular.uppercase
196 * @module ng
197 * @kind function
198 *
199 * @description Converts the specified string to uppercase.
200 * @param {string} string String to be converted to uppercase.
201 * @returns {string} Uppercased string.
202 */
203var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
204
205
206var manualLowercase = function(s) {
207 /* jshint bitwise: false */
208 return isString(s)
209 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
210 : s;
211};
212var manualUppercase = function(s) {
213 /* jshint bitwise: false */
214 return isString(s)
215 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
216 : s;
217};
218
219
220// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
221// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
222// with correct but slower alternatives.
223if ('i' !== 'I'.toLowerCase()) {
224 lowercase = manualLowercase;
225 uppercase = manualUppercase;
226}
227
228
229var
230 msie, // holds major version number for IE, or NaN if UA is not IE.
231 jqLite, // delay binding since jQuery could be loaded after us.
232 jQuery, // delay binding
233 slice = [].slice,
234 splice = [].splice,
235 push = [].push,
236 toString = Object.prototype.toString,
237 ngMinErr = minErr('ng'),
238
239 /** @name angular */
240 angular = window.angular || (window.angular = {}),
241 angularModule,
242 uid = 0;
243
244/**
245 * documentMode is an IE-only property
246 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
247 */
248msie = document.documentMode;
249
250
251/**
252 * @private
253 * @param {*} obj
254 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
255 * String ...)
256 */
257function isArrayLike(obj) {
258 if (obj == null || isWindow(obj)) {
259 return false;
260 }
261
262 var length = obj.length;
263
264 if (obj.nodeType === NODE_TYPE_ELEMENT && length) {
265 return true;
266 }
267
268 return isString(obj) || isArray(obj) || length === 0 ||
269 typeof length === 'number' && length > 0 && (length - 1) in obj;
270}
271
272/**
273 * @ngdoc function
274 * @name angular.forEach
275 * @module ng
276 * @kind function
277 *
278 * @description
279 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
280 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
281 * is the value of an object property or an array element, `key` is the object property key or
282 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
283 *
284 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
285 * using the `hasOwnProperty` method.
286 *
287 * Unlike ES262's
288 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
289 * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
290 * return the value provided.
291 *
292 ```js
293 var values = {name: 'misko', gender: 'male'};
294 var log = [];
295 angular.forEach(values, function(value, key) {
296 this.push(key + ': ' + value);
297 }, log);
298 expect(log).toEqual(['name: misko', 'gender: male']);
299 ```
300 *
301 * @param {Object|Array} obj Object to iterate over.
302 * @param {Function} iterator Iterator function.
303 * @param {Object=} context Object to become context (`this`) for the iterator function.
304 * @returns {Object|Array} Reference to `obj`.
305 */
306
307function forEach(obj, iterator, context) {
308 var key, length;
309 if (obj) {
310 if (isFunction(obj)) {
311 for (key in obj) {
312 // Need to check if hasOwnProperty exists,
313 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
314 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
315 iterator.call(context, obj[key], key, obj);
316 }
317 }
318 } else if (isArray(obj) || isArrayLike(obj)) {
319 var isPrimitive = typeof obj !== 'object';
320 for (key = 0, length = obj.length; key < length; key++) {
321 if (isPrimitive || key in obj) {
322 iterator.call(context, obj[key], key, obj);
323 }
324 }
325 } else if (obj.forEach && obj.forEach !== forEach) {
326 obj.forEach(iterator, context, obj);
327 } else {
328 for (key in obj) {
329 if (obj.hasOwnProperty(key)) {
330 iterator.call(context, obj[key], key, obj);
331 }
332 }
333 }
334 }
335 return obj;
336}
337
338function sortedKeys(obj) {
339 return Object.keys(obj).sort();
340}
341
342function forEachSorted(obj, iterator, context) {
343 var keys = sortedKeys(obj);
344 for (var i = 0; i < keys.length; i++) {
345 iterator.call(context, obj[keys[i]], keys[i]);
346 }
347 return keys;
348}
349
350
351/**
352 * when using forEach the params are value, key, but it is often useful to have key, value.
353 * @param {function(string, *)} iteratorFn
354 * @returns {function(*, string)}
355 */
356function reverseParams(iteratorFn) {
357 return function(value, key) { iteratorFn(key, value); };
358}
359
360/**
361 * A consistent way of creating unique IDs in angular.
362 *
363 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
364 * we hit number precision issues in JavaScript.
365 *
366 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
367 *
368 * @returns {number} an unique alpha-numeric string
369 */
370function nextUid() {
371 return ++uid;
372}
373
374
375/**
376 * Set or clear the hashkey for an object.
377 * @param obj object
378 * @param h the hashkey (!truthy to delete the hashkey)
379 */
380function setHashKey(obj, h) {
381 if (h) {
382 obj.$$hashKey = h;
383 }
384 else {
385 delete obj.$$hashKey;
386 }
387}
388
389/**
390 * @ngdoc function
391 * @name angular.extend
392 * @module ng
393 * @kind function
394 *
395 * @description
396 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
397 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
398 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
399 * Note: Keep in mind that `angular.extend` does not support recursive merge (deep copy).
400 *
401 * @param {Object} dst Destination object.
402 * @param {...Object} src Source object(s).
403 * @returns {Object} Reference to `dst`.
404 */
405function extend(dst) {
406 var h = dst.$$hashKey;
407
408 for (var i = 1, ii = arguments.length; i < ii; i++) {
409 var obj = arguments[i];
410 if (obj) {
411 var keys = Object.keys(obj);
412 for (var j = 0, jj = keys.length; j < jj; j++) {
413 var key = keys[j];
414 dst[key] = obj[key];
415 }
416 }
417 }
418
419 setHashKey(dst, h);
420 return dst;
421}
422
423function int(str) {
424 return parseInt(str, 10);
425}
426
427
428function inherit(parent, extra) {
429 return extend(Object.create(parent), extra);
430}
431
432/**
433 * @ngdoc function
434 * @name angular.noop
435 * @module ng
436 * @kind function
437 *
438 * @description
439 * A function that performs no operations. This function can be useful when writing code in the
440 * functional style.
441 ```js
442 function foo(callback) {
443 var result = calculateResult();
444 (callback || angular.noop)(result);
445 }
446 ```
447 */
448function noop() {}
449noop.$inject = [];
450
451
452/**
453 * @ngdoc function
454 * @name angular.identity
455 * @module ng
456 * @kind function
457 *
458 * @description
459 * A function that returns its first argument. This function is useful when writing code in the
460 * functional style.
461 *
462 ```js
463 function transformer(transformationFn, value) {
464 return (transformationFn || angular.identity)(value);
465 };
466 ```
467 */
468function identity($) {return $;}
469identity.$inject = [];
470
471
472function valueFn(value) {return function() {return value;};}
473
474/**
475 * @ngdoc function
476 * @name angular.isUndefined
477 * @module ng
478 * @kind function
479 *
480 * @description
481 * Determines if a reference is undefined.
482 *
483 * @param {*} value Reference to check.
484 * @returns {boolean} True if `value` is undefined.
485 */
486function isUndefined(value) {return typeof value === 'undefined';}
487
488
489/**
490 * @ngdoc function
491 * @name angular.isDefined
492 * @module ng
493 * @kind function
494 *
495 * @description
496 * Determines if a reference is defined.
497 *
498 * @param {*} value Reference to check.
499 * @returns {boolean} True if `value` is defined.
500 */
501function isDefined(value) {return typeof value !== 'undefined';}
502
503
504/**
505 * @ngdoc function
506 * @name angular.isObject
507 * @module ng
508 * @kind function
509 *
510 * @description
511 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
512 * considered to be objects. Note that JavaScript arrays are objects.
513 *
514 * @param {*} value Reference to check.
515 * @returns {boolean} True if `value` is an `Object` but not `null`.
516 */
517function isObject(value) {
518 // http://jsperf.com/isobject4
519 return value !== null && typeof value === 'object';
520}
521
522
523/**
524 * @ngdoc function
525 * @name angular.isString
526 * @module ng
527 * @kind function
528 *
529 * @description
530 * Determines if a reference is a `String`.
531 *
532 * @param {*} value Reference to check.
533 * @returns {boolean} True if `value` is a `String`.
534 */
535function isString(value) {return typeof value === 'string';}
536
537
538/**
539 * @ngdoc function
540 * @name angular.isNumber
541 * @module ng
542 * @kind function
543 *
544 * @description
545 * Determines if a reference is a `Number`.
546 *
547 * @param {*} value Reference to check.
548 * @returns {boolean} True if `value` is a `Number`.
549 */
550function isNumber(value) {return typeof value === 'number';}
551
552
553/**
554 * @ngdoc function
555 * @name angular.isDate
556 * @module ng
557 * @kind function
558 *
559 * @description
560 * Determines if a value is a date.
561 *
562 * @param {*} value Reference to check.
563 * @returns {boolean} True if `value` is a `Date`.
564 */
565function isDate(value) {
566 return toString.call(value) === '[object Date]';
567}
568
569
570/**
571 * @ngdoc function
572 * @name angular.isArray
573 * @module ng
574 * @kind function
575 *
576 * @description
577 * Determines if a reference is an `Array`.
578 *
579 * @param {*} value Reference to check.
580 * @returns {boolean} True if `value` is an `Array`.
581 */
582var isArray = Array.isArray;
583
584/**
585 * @ngdoc function
586 * @name angular.isFunction
587 * @module ng
588 * @kind function
589 *
590 * @description
591 * Determines if a reference is a `Function`.
592 *
593 * @param {*} value Reference to check.
594 * @returns {boolean} True if `value` is a `Function`.
595 */
596function isFunction(value) {return typeof value === 'function';}
597
598
599/**
600 * Determines if a value is a regular expression object.
601 *
602 * @private
603 * @param {*} value Reference to check.
604 * @returns {boolean} True if `value` is a `RegExp`.
605 */
606function isRegExp(value) {
607 return toString.call(value) === '[object RegExp]';
608}
609
610
611/**
612 * Checks if `obj` is a window object.
613 *
614 * @private
615 * @param {*} obj Object to check
616 * @returns {boolean} True if `obj` is a window obj.
617 */
618function isWindow(obj) {
619 return obj && obj.window === obj;
620}
621
622
623function isScope(obj) {
624 return obj && obj.$evalAsync && obj.$watch;
625}
626
627
628function isFile(obj) {
629 return toString.call(obj) === '[object File]';
630}
631
632
633function isBlob(obj) {
634 return toString.call(obj) === '[object Blob]';
635}
636
637
638function isBoolean(value) {
639 return typeof value === 'boolean';
640}
641
642
643function isPromiseLike(obj) {
644 return obj && isFunction(obj.then);
645}
646
647
648var trim = function(value) {
649 return isString(value) ? value.trim() : value;
650};
651
652// Copied from:
653// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
654// Prereq: s is a string.
655var escapeForRegexp = function(s) {
656 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
657 replace(/\x08/g, '\\x08');
658};
659
660
661/**
662 * @ngdoc function
663 * @name angular.isElement
664 * @module ng
665 * @kind function
666 *
667 * @description
668 * Determines if a reference is a DOM element (or wrapped jQuery element).
669 *
670 * @param {*} value Reference to check.
671 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
672 */
673function isElement(node) {
674 return !!(node &&
675 (node.nodeName // we are a direct element
676 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
677}
678
679/**
680 * @param str 'key1,key2,...'
681 * @returns {object} in the form of {key1:true, key2:true, ...}
682 */
683function makeMap(str) {
684 var obj = {}, items = str.split(","), i;
685 for (i = 0; i < items.length; i++)
686 obj[ items[i] ] = true;
687 return obj;
688}
689
690
691function nodeName_(element) {
692 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
693}
694
695function includes(array, obj) {
696 return Array.prototype.indexOf.call(array, obj) != -1;
697}
698
699function arrayRemove(array, value) {
700 var index = array.indexOf(value);
701 if (index >= 0)
702 array.splice(index, 1);
703 return value;
704}
705
706/**
707 * @ngdoc function
708 * @name angular.copy
709 * @module ng
710 * @kind function
711 *
712 * @description
713 * Creates a deep copy of `source`, which should be an object or an array.
714 *
715 * * If no destination is supplied, a copy of the object or array is created.
716 * * If a destination is provided, all of its elements (for array) or properties (for objects)
717 * are deleted and then all elements/properties from the source are copied to it.
718 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
719 * * If `source` is identical to 'destination' an exception will be thrown.
720 *
721 * @param {*} source The source that will be used to make a copy.
722 * Can be any type, including primitives, `null`, and `undefined`.
723 * @param {(Object|Array)=} destination Destination into which the source is copied. If
724 * provided, must be of the same type as `source`.
725 * @returns {*} The copy or updated `destination`, if `destination` was specified.
726 *
727 * @example
728 <example module="copyExample">
729 <file name="index.html">
730 <div ng-controller="ExampleController">
731 <form novalidate class="simple-form">
732 Name: <input type="text" ng-model="user.name" /><br />
733 E-mail: <input type="email" ng-model="user.email" /><br />
734 Gender: <input type="radio" ng-model="user.gender" value="male" />male
735 <input type="radio" ng-model="user.gender" value="female" />female<br />
736 <button ng-click="reset()">RESET</button>
737 <button ng-click="update(user)">SAVE</button>
738 </form>
739 <pre>form = {{user | json}}</pre>
740 <pre>master = {{master | json}}</pre>
741 </div>
742
743 <script>
744 angular.module('copyExample', [])
745 .controller('ExampleController', ['$scope', function($scope) {
746 $scope.master= {};
747
748 $scope.update = function(user) {
749 // Example with 1 argument
750 $scope.master= angular.copy(user);
751 };
752
753 $scope.reset = function() {
754 // Example with 2 arguments
755 angular.copy($scope.master, $scope.user);
756 };
757
758 $scope.reset();
759 }]);
760 </script>
761 </file>
762 </example>
763 */
764function copy(source, destination, stackSource, stackDest) {
765 if (isWindow(source) || isScope(source)) {
766 throw ngMinErr('cpws',
767 "Can't copy! Making copies of Window or Scope instances is not supported.");
768 }
769
770 if (!destination) {
771 destination = source;
772 if (source) {
773 if (isArray(source)) {
774 destination = copy(source, [], stackSource, stackDest);
775 } else if (isDate(source)) {
776 destination = new Date(source.getTime());
777 } else if (isRegExp(source)) {
778 destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
779 destination.lastIndex = source.lastIndex;
780 } else if (isObject(source)) {
781 var emptyObject = Object.create(Object.getPrototypeOf(source));
782 destination = copy(source, emptyObject, stackSource, stackDest);
783 }
784 }
785 } else {
786 if (source === destination) throw ngMinErr('cpi',
787 "Can't copy! Source and destination are identical.");
788
789 stackSource = stackSource || [];
790 stackDest = stackDest || [];
791
792 if (isObject(source)) {
793 var index = stackSource.indexOf(source);
794 if (index !== -1) return stackDest[index];
795
796 stackSource.push(source);
797 stackDest.push(destination);
798 }
799
800 var result;
801 if (isArray(source)) {
802 destination.length = 0;
803 for (var i = 0; i < source.length; i++) {
804 result = copy(source[i], null, stackSource, stackDest);
805 if (isObject(source[i])) {
806 stackSource.push(source[i]);
807 stackDest.push(result);
808 }
809 destination.push(result);
810 }
811 } else {
812 var h = destination.$$hashKey;
813 if (isArray(destination)) {
814 destination.length = 0;
815 } else {
816 forEach(destination, function(value, key) {
817 delete destination[key];
818 });
819 }
820 for (var key in source) {
821 if (source.hasOwnProperty(key)) {
822 result = copy(source[key], null, stackSource, stackDest);
823 if (isObject(source[key])) {
824 stackSource.push(source[key]);
825 stackDest.push(result);
826 }
827 destination[key] = result;
828 }
829 }
830 setHashKey(destination,h);
831 }
832
833 }
834 return destination;
835}
836
837/**
838 * Creates a shallow copy of an object, an array or a primitive.
839 *
840 * Assumes that there are no proto properties for objects.
841 */
842function shallowCopy(src, dst) {
843 if (isArray(src)) {
844 dst = dst || [];
845
846 for (var i = 0, ii = src.length; i < ii; i++) {
847 dst[i] = src[i];
848 }
849 } else if (isObject(src)) {
850 dst = dst || {};
851
852 for (var key in src) {
853 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
854 dst[key] = src[key];
855 }
856 }
857 }
858
859 return dst || src;
860}
861
862
863/**
864 * @ngdoc function
865 * @name angular.equals
866 * @module ng
867 * @kind function
868 *
869 * @description
870 * Determines if two objects or two values are equivalent. Supports value types, regular
871 * expressions, arrays and objects.
872 *
873 * Two objects or values are considered equivalent if at least one of the following is true:
874 *
875 * * Both objects or values pass `===` comparison.
876 * * Both objects or values are of the same type and all of their properties are equal by
877 * comparing them with `angular.equals`.
878 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
879 * * Both values represent the same regular expression (In JavaScript,
880 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
881 * representation matches).
882 *
883 * During a property comparison, properties of `function` type and properties with names
884 * that begin with `$` are ignored.
885 *
886 * Scope and DOMWindow objects are being compared only by identify (`===`).
887 *
888 * @param {*} o1 Object or value to compare.
889 * @param {*} o2 Object or value to compare.
890 * @returns {boolean} True if arguments are equal.
891 */
892function equals(o1, o2) {
893 if (o1 === o2) return true;
894 if (o1 === null || o2 === null) return false;
895 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
896 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
897 if (t1 == t2) {
898 if (t1 == 'object') {
899 if (isArray(o1)) {
900 if (!isArray(o2)) return false;
901 if ((length = o1.length) == o2.length) {
902 for (key = 0; key < length; key++) {
903 if (!equals(o1[key], o2[key])) return false;
904 }
905 return true;
906 }
907 } else if (isDate(o1)) {
908 if (!isDate(o2)) return false;
909 return equals(o1.getTime(), o2.getTime());
910 } else if (isRegExp(o1) && isRegExp(o2)) {
911 return o1.toString() == o2.toString();
912 } else {
913 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) || isArray(o2)) return false;
914 keySet = {};
915 for (key in o1) {
916 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
917 if (!equals(o1[key], o2[key])) return false;
918 keySet[key] = true;
919 }
920 for (key in o2) {
921 if (!keySet.hasOwnProperty(key) &&
922 key.charAt(0) !== '$' &&
923 o2[key] !== undefined &&
924 !isFunction(o2[key])) return false;
925 }
926 return true;
927 }
928 }
929 }
930 return false;
931}
932
933var csp = function() {
934 if (isDefined(csp.isActive_)) return csp.isActive_;
935
936 var active = !!(document.querySelector('[ng-csp]') ||
937 document.querySelector('[data-ng-csp]'));
938
939 if (!active) {
940 try {
941 /* jshint -W031, -W054 */
942 new Function('');
943 /* jshint +W031, +W054 */
944 } catch (e) {
945 active = true;
946 }
947 }
948
949 return (csp.isActive_ = active);
950};
951
952
953
954function concat(array1, array2, index) {
955 return array1.concat(slice.call(array2, index));
956}
957
958function sliceArgs(args, startIndex) {
959 return slice.call(args, startIndex || 0);
960}
961
962
963/* jshint -W101 */
964/**
965 * @ngdoc function
966 * @name angular.bind
967 * @module ng
968 * @kind function
969 *
970 * @description
971 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
972 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
973 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
974 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
975 *
976 * @param {Object} self Context which `fn` should be evaluated in.
977 * @param {function()} fn Function to be bound.
978 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
979 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
980 */
981/* jshint +W101 */
982function bind(self, fn) {
983 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
984 if (isFunction(fn) && !(fn instanceof RegExp)) {
985 return curryArgs.length
986 ? function() {
987 return arguments.length
988 ? fn.apply(self, concat(curryArgs, arguments, 0))
989 : fn.apply(self, curryArgs);
990 }
991 : function() {
992 return arguments.length
993 ? fn.apply(self, arguments)
994 : fn.call(self);
995 };
996 } else {
997 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
998 return fn;
999 }
1000}
1001
1002
1003function toJsonReplacer(key, value) {
1004 var val = value;
1005
1006 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1007 val = undefined;
1008 } else if (isWindow(value)) {
1009 val = '$WINDOW';
1010 } else if (value && document === value) {
1011 val = '$DOCUMENT';
1012 } else if (isScope(value)) {
1013 val = '$SCOPE';
1014 }
1015
1016 return val;
1017}
1018
1019
1020/**
1021 * @ngdoc function
1022 * @name angular.toJson
1023 * @module ng
1024 * @kind function
1025 *
1026 * @description
1027 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1028 * stripped since angular uses this notation internally.
1029 *
1030 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1031 * @param {boolean=} pretty If set to true, the JSON output will contain newlines and whitespace.
1032 * @returns {string|undefined} JSON-ified string representing `obj`.
1033 */
1034function toJson(obj, pretty) {
1035 if (typeof obj === 'undefined') return undefined;
1036 return JSON.stringify(obj, toJsonReplacer, pretty ? ' ' : null);
1037}
1038
1039
1040/**
1041 * @ngdoc function
1042 * @name angular.fromJson
1043 * @module ng
1044 * @kind function
1045 *
1046 * @description
1047 * Deserializes a JSON string.
1048 *
1049 * @param {string} json JSON string to deserialize.
1050 * @returns {Object|Array|string|number} Deserialized thingy.
1051 */
1052function fromJson(json) {
1053 return isString(json)
1054 ? JSON.parse(json)
1055 : json;
1056}
1057
1058
1059/**
1060 * @returns {string} Returns the string representation of the element.
1061 */
1062function startingTag(element) {
1063 element = jqLite(element).clone();
1064 try {
1065 // turns out IE does not let you set .html() on elements which
1066 // are not allowed to have children. So we just ignore it.
1067 element.empty();
1068 } catch (e) {}
1069 var elemHtml = jqLite('<div>').append(element).html();
1070 try {
1071 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1072 elemHtml.
1073 match(/^(<[^>]+>)/)[1].
1074 replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1075 } catch (e) {
1076 return lowercase(elemHtml);
1077 }
1078
1079}
1080
1081
1082/////////////////////////////////////////////////
1083
1084/**
1085 * Tries to decode the URI component without throwing an exception.
1086 *
1087 * @private
1088 * @param str value potential URI component to check.
1089 * @returns {boolean} True if `value` can be decoded
1090 * with the decodeURIComponent function.
1091 */
1092function tryDecodeURIComponent(value) {
1093 try {
1094 return decodeURIComponent(value);
1095 } catch (e) {
1096 // Ignore any invalid uri component
1097 }
1098}
1099
1100
1101/**
1102 * Parses an escaped url query string into key-value pairs.
1103 * @returns {Object.<string,boolean|Array>}
1104 */
1105function parseKeyValue(/**string*/keyValue) {
1106 var obj = {}, key_value, key;
1107 forEach((keyValue || "").split('&'), function(keyValue) {
1108 if (keyValue) {
1109 key_value = keyValue.replace(/\+/g,'%20').split('=');
1110 key = tryDecodeURIComponent(key_value[0]);
1111 if (isDefined(key)) {
1112 var val = isDefined(key_value[1]) ? tryDecodeURIComponent(key_value[1]) : true;
1113 if (!hasOwnProperty.call(obj, key)) {
1114 obj[key] = val;
1115 } else if (isArray(obj[key])) {
1116 obj[key].push(val);
1117 } else {
1118 obj[key] = [obj[key],val];
1119 }
1120 }
1121 }
1122 });
1123 return obj;
1124}
1125
1126function toKeyValue(obj) {
1127 var parts = [];
1128 forEach(obj, function(value, key) {
1129 if (isArray(value)) {
1130 forEach(value, function(arrayValue) {
1131 parts.push(encodeUriQuery(key, true) +
1132 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1133 });
1134 } else {
1135 parts.push(encodeUriQuery(key, true) +
1136 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1137 }
1138 });
1139 return parts.length ? parts.join('&') : '';
1140}
1141
1142
1143/**
1144 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1145 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1146 * segments:
1147 * segment = *pchar
1148 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1149 * pct-encoded = "%" HEXDIG HEXDIG
1150 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1151 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1152 * / "*" / "+" / "," / ";" / "="
1153 */
1154function encodeUriSegment(val) {
1155 return encodeUriQuery(val, true).
1156 replace(/%26/gi, '&').
1157 replace(/%3D/gi, '=').
1158 replace(/%2B/gi, '+');
1159}
1160
1161
1162/**
1163 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1164 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1165 * encoded per http://tools.ietf.org/html/rfc3986:
1166 * query = *( pchar / "/" / "?" )
1167 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1168 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1169 * pct-encoded = "%" HEXDIG HEXDIG
1170 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1171 * / "*" / "+" / "," / ";" / "="
1172 */
1173function encodeUriQuery(val, pctEncodeSpaces) {
1174 return encodeURIComponent(val).
1175 replace(/%40/gi, '@').
1176 replace(/%3A/gi, ':').
1177 replace(/%24/g, '$').
1178 replace(/%2C/gi, ',').
1179 replace(/%3B/gi, ';').
1180 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1181}
1182
1183var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1184
1185function getNgAttribute(element, ngAttr) {
1186 var attr, i, ii = ngAttrPrefixes.length;
1187 element = jqLite(element);
1188 for (i = 0; i < ii; ++i) {
1189 attr = ngAttrPrefixes[i] + ngAttr;
1190 if (isString(attr = element.attr(attr))) {
1191 return attr;
1192 }
1193 }
1194 return null;
1195}
1196
1197/**
1198 * @ngdoc directive
1199 * @name ngApp
1200 * @module ng
1201 *
1202 * @element ANY
1203 * @param {angular.Module} ngApp an optional application
1204 * {@link angular.module module} name to load.
1205 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1206 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1207 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1208 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1209 * tracking down the root of these bugs.
1210 *
1211 * @description
1212 *
1213 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1214 * designates the **root element** of the application and is typically placed near the root element
1215 * of the page - e.g. on the `<body>` or `<html>` tags.
1216 *
1217 * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1218 * found in the document will be used to define the root element to auto-bootstrap as an
1219 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1220 * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1221 *
1222 * You can specify an **AngularJS module** to be used as the root module for the application. This
1223 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped and
1224 * should contain the application code needed or have dependencies on other modules that will
1225 * contain the code. See {@link angular.module} for more information.
1226 *
1227 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1228 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1229 * would not be resolved to `3`.
1230 *
1231 * `ngApp` is the easiest, and most common, way to bootstrap an application.
1232 *
1233 <example module="ngAppDemo">
1234 <file name="index.html">
1235 <div ng-controller="ngAppDemoController">
1236 I can add: {{a}} + {{b}} = {{ a+b }}
1237 </div>
1238 </file>
1239 <file name="script.js">
1240 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1241 $scope.a = 1;
1242 $scope.b = 2;
1243 });
1244 </file>
1245 </example>
1246 *
1247 * Using `ngStrictDi`, you would see something like this:
1248 *
1249 <example ng-app-included="true">
1250 <file name="index.html">
1251 <div ng-app="ngAppStrictDemo" ng-strict-di>
1252 <div ng-controller="GoodController1">
1253 I can add: {{a}} + {{b}} = {{ a+b }}
1254
1255 <p>This renders because the controller does not fail to
1256 instantiate, by using explicit annotation style (see
1257 script.js for details)
1258 </p>
1259 </div>
1260
1261 <div ng-controller="GoodController2">
1262 Name: <input ng-model="name"><br />
1263 Hello, {{name}}!
1264
1265 <p>This renders because the controller does not fail to
1266 instantiate, by using explicit annotation style
1267 (see script.js for details)
1268 </p>
1269 </div>
1270
1271 <div ng-controller="BadController">
1272 I can add: {{a}} + {{b}} = {{ a+b }}
1273
1274 <p>The controller could not be instantiated, due to relying
1275 on automatic function annotations (which are disabled in
1276 strict mode). As such, the content of this section is not
1277 interpolated, and there should be an error in your web console.
1278 </p>
1279 </div>
1280 </div>
1281 </file>
1282 <file name="script.js">
1283 angular.module('ngAppStrictDemo', [])
1284 // BadController will fail to instantiate, due to relying on automatic function annotation,
1285 // rather than an explicit annotation
1286 .controller('BadController', function($scope) {
1287 $scope.a = 1;
1288 $scope.b = 2;
1289 })
1290 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1291 // due to using explicit annotations using the array style and $inject property, respectively.
1292 .controller('GoodController1', ['$scope', function($scope) {
1293 $scope.a = 1;
1294 $scope.b = 2;
1295 }])
1296 .controller('GoodController2', GoodController2);
1297 function GoodController2($scope) {
1298 $scope.name = "World";
1299 }
1300 GoodController2.$inject = ['$scope'];
1301 </file>
1302 <file name="style.css">
1303 div[ng-controller] {
1304 margin-bottom: 1em;
1305 -webkit-border-radius: 4px;
1306 border-radius: 4px;
1307 border: 1px solid;
1308 padding: .5em;
1309 }
1310 div[ng-controller^=Good] {
1311 border-color: #d6e9c6;
1312 background-color: #dff0d8;
1313 color: #3c763d;
1314 }
1315 div[ng-controller^=Bad] {
1316 border-color: #ebccd1;
1317 background-color: #f2dede;
1318 color: #a94442;
1319 margin-bottom: 0;
1320 }
1321 </file>
1322 </example>
1323 */
1324function angularInit(element, bootstrap) {
1325 var appElement,
1326 module,
1327 config = {};
1328
1329 // The element `element` has priority over any other element
1330 forEach(ngAttrPrefixes, function(prefix) {
1331 var name = prefix + 'app';
1332
1333 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1334 appElement = element;
1335 module = element.getAttribute(name);
1336 }
1337 });
1338 forEach(ngAttrPrefixes, function(prefix) {
1339 var name = prefix + 'app';
1340 var candidate;
1341
1342 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1343 appElement = candidate;
1344 module = candidate.getAttribute(name);
1345 }
1346 });
1347 if (appElement) {
1348 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1349 bootstrap(appElement, module ? [module] : [], config);
1350 }
1351}
1352
1353/**
1354 * @ngdoc function
1355 * @name angular.bootstrap
1356 * @module ng
1357 * @description
1358 * Use this function to manually start up angular application.
1359 *
1360 * See: {@link guide/bootstrap Bootstrap}
1361 *
1362 * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1363 * They must use {@link ng.directive:ngApp ngApp}.
1364 *
1365 * Angular will detect if it has been loaded into the browser more than once and only allow the
1366 * first loaded script to be bootstrapped and will report a warning to the browser console for
1367 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1368 * multiple instances of Angular try to work on the DOM.
1369 *
1370 * ```html
1371 * <!doctype html>
1372 * <html>
1373 * <body>
1374 * <div ng-controller="WelcomeController">
1375 * {{greeting}}
1376 * </div>
1377 *
1378 * <script src="angular.js"></script>
1379 * <script>
1380 * var app = angular.module('demo', [])
1381 * .controller('WelcomeController', function($scope) {
1382 * $scope.greeting = 'Welcome!';
1383 * });
1384 * angular.bootstrap(document, ['demo']);
1385 * </script>
1386 * </body>
1387 * </html>
1388 * ```
1389 *
1390 * @param {DOMElement} element DOM element which is the root of angular application.
1391 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1392 * Each item in the array should be the name of a predefined module or a (DI annotated)
1393 * function that will be invoked by the injector as a run block.
1394 * See: {@link angular.module modules}
1395 * @param {Object=} config an object for defining configuration options for the application. The
1396 * following keys are supported:
1397 *
1398 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1399 * assist in finding bugs which break minified code. Defaults to `false`.
1400 *
1401 * @returns {auto.$injector} Returns the newly created injector for this app.
1402 */
1403function bootstrap(element, modules, config) {
1404 if (!isObject(config)) config = {};
1405 var defaultConfig = {
1406 strictDi: false
1407 };
1408 config = extend(defaultConfig, config);
1409 var doBootstrap = function() {
1410 element = jqLite(element);
1411
1412 if (element.injector()) {
1413 var tag = (element[0] === document) ? 'document' : startingTag(element);
1414 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1415 throw ngMinErr(
1416 'btstrpd',
1417 "App Already Bootstrapped with this Element '{0}'",
1418 tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1419 }
1420
1421 modules = modules || [];
1422 modules.unshift(['$provide', function($provide) {
1423 $provide.value('$rootElement', element);
1424 }]);
1425
1426 if (config.debugInfoEnabled) {
1427 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1428 modules.push(['$compileProvider', function($compileProvider) {
1429 $compileProvider.debugInfoEnabled(true);
1430 }]);
1431 }
1432
1433 modules.unshift('ng');
1434 var injector = createInjector(modules, config.strictDi);
1435 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1436 function bootstrapApply(scope, element, compile, injector) {
1437 scope.$apply(function() {
1438 element.data('$injector', injector);
1439 compile(element)(scope);
1440 });
1441 }]
1442 );
1443 return injector;
1444 };
1445
1446 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1447 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1448
1449 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1450 config.debugInfoEnabled = true;
1451 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1452 }
1453
1454 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1455 return doBootstrap();
1456 }
1457
1458 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1459 angular.resumeBootstrap = function(extraModules) {
1460 forEach(extraModules, function(module) {
1461 modules.push(module);
1462 });
1463 doBootstrap();
1464 };
1465}
1466
1467/**
1468 * @ngdoc function
1469 * @name angular.reloadWithDebugInfo
1470 * @module ng
1471 * @description
1472 * Use this function to reload the current application with debug information turned on.
1473 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1474 *
1475 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1476 */
1477function reloadWithDebugInfo() {
1478 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1479 window.location.reload();
1480}
1481
1482/**
1483 * @name angular.getTestability
1484 * @module ng
1485 * @description
1486 * Get the testability service for the instance of Angular on the given
1487 * element.
1488 * @param {DOMElement} element DOM element which is the root of angular application.
1489 */
1490function getTestability(rootElement) {
1491 return angular.element(rootElement).injector().get('$$testability');
1492}
1493
1494var SNAKE_CASE_REGEXP = /[A-Z]/g;
1495function snake_case(name, separator) {
1496 separator = separator || '_';
1497 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1498 return (pos ? separator : '') + letter.toLowerCase();
1499 });
1500}
1501
1502var bindJQueryFired = false;
1503var skipDestroyOnNextJQueryCleanData;
1504function bindJQuery() {
1505 var originalCleanData;
1506
1507 if (bindJQueryFired) {
1508 return;
1509 }
1510
1511 // bind to jQuery if present;
1512 jQuery = window.jQuery;
1513 // Use jQuery if it exists with proper functionality, otherwise default to us.
1514 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1515 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1516 // versions. It will not work for sure with jQuery <1.7, though.
1517 if (jQuery && jQuery.fn.on) {
1518 jqLite = jQuery;
1519 extend(jQuery.fn, {
1520 scope: JQLitePrototype.scope,
1521 isolateScope: JQLitePrototype.isolateScope,
1522 controller: JQLitePrototype.controller,
1523 injector: JQLitePrototype.injector,
1524 inheritedData: JQLitePrototype.inheritedData
1525 });
1526
1527 // All nodes removed from the DOM via various jQuery APIs like .remove()
1528 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1529 // the $destroy event on all removed nodes.
1530 originalCleanData = jQuery.cleanData;
1531 jQuery.cleanData = function(elems) {
1532 var events;
1533 if (!skipDestroyOnNextJQueryCleanData) {
1534 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1535 events = jQuery._data(elem, "events");
1536 if (events && events.$destroy) {
1537 jQuery(elem).triggerHandler('$destroy');
1538 }
1539 }
1540 } else {
1541 skipDestroyOnNextJQueryCleanData = false;
1542 }
1543 originalCleanData(elems);
1544 };
1545 } else {
1546 jqLite = JQLite;
1547 }
1548
1549 angular.element = jqLite;
1550
1551 // Prevent double-proxying.
1552 bindJQueryFired = true;
1553}
1554
1555/**
1556 * throw error if the argument is falsy.
1557 */
1558function assertArg(arg, name, reason) {
1559 if (!arg) {
1560 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1561 }
1562 return arg;
1563}
1564
1565function assertArgFn(arg, name, acceptArrayAnnotation) {
1566 if (acceptArrayAnnotation && isArray(arg)) {
1567 arg = arg[arg.length - 1];
1568 }
1569
1570 assertArg(isFunction(arg), name, 'not a function, got ' +
1571 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1572 return arg;
1573}
1574
1575/**
1576 * throw error if the name given is hasOwnProperty
1577 * @param {String} name the name to test
1578 * @param {String} context the context in which the name is used, such as module or directive
1579 */
1580function assertNotHasOwnProperty(name, context) {
1581 if (name === 'hasOwnProperty') {
1582 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1583 }
1584}
1585
1586/**
1587 * Return the value accessible from the object by path. Any undefined traversals are ignored
1588 * @param {Object} obj starting object
1589 * @param {String} path path to traverse
1590 * @param {boolean} [bindFnToScope=true]
1591 * @returns {Object} value as accessible by path
1592 */
1593//TODO(misko): this function needs to be removed
1594function getter(obj, path, bindFnToScope) {
1595 if (!path) return obj;
1596 var keys = path.split('.');
1597 var key;
1598 var lastInstance = obj;
1599 var len = keys.length;
1600
1601 for (var i = 0; i < len; i++) {
1602 key = keys[i];
1603 if (obj) {
1604 obj = (lastInstance = obj)[key];
1605 }
1606 }
1607 if (!bindFnToScope && isFunction(obj)) {
1608 return bind(lastInstance, obj);
1609 }
1610 return obj;
1611}
1612
1613/**
1614 * Return the DOM siblings between the first and last node in the given array.
1615 * @param {Array} array like object
1616 * @returns {jqLite} jqLite collection containing the nodes
1617 */
1618function getBlockNodes(nodes) {
1619 // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
1620 // collection, otherwise update the original collection.
1621 var node = nodes[0];
1622 var endNode = nodes[nodes.length - 1];
1623 var blockNodes = [node];
1624
1625 do {
1626 node = node.nextSibling;
1627 if (!node) break;
1628 blockNodes.push(node);
1629 } while (node !== endNode);
1630
1631 return jqLite(blockNodes);
1632}
1633
1634
1635/**
1636 * Creates a new object without a prototype. This object is useful for lookup without having to
1637 * guard against prototypically inherited properties via hasOwnProperty.
1638 *
1639 * Related micro-benchmarks:
1640 * - http://jsperf.com/object-create2
1641 * - http://jsperf.com/proto-map-lookup/2
1642 * - http://jsperf.com/for-in-vs-object-keys2
1643 *
1644 * @returns {Object}
1645 */
1646function createMap() {
1647 return Object.create(null);
1648}
1649
1650var NODE_TYPE_ELEMENT = 1;
1651var NODE_TYPE_TEXT = 3;
1652var NODE_TYPE_COMMENT = 8;
1653var NODE_TYPE_DOCUMENT = 9;
1654var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1655
1656/**
1657 * @ngdoc type
1658 * @name angular.Module
1659 * @module ng
1660 * @description
1661 *
1662 * Interface for configuring angular {@link angular.module modules}.
1663 */
1664
1665function setupModuleLoader(window) {
1666
1667 var $injectorMinErr = minErr('$injector');
1668 var ngMinErr = minErr('ng');
1669
1670 function ensure(obj, name, factory) {
1671 return obj[name] || (obj[name] = factory());
1672 }
1673
1674 var angular = ensure(window, 'angular', Object);
1675
1676 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1677 angular.$$minErr = angular.$$minErr || minErr;
1678
1679 return ensure(angular, 'module', function() {
1680 /** @type {Object.<string, angular.Module>} */
1681 var modules = {};
1682
1683 /**
1684 * @ngdoc function
1685 * @name angular.module
1686 * @module ng
1687 * @description
1688 *
1689 * The `angular.module` is a global place for creating, registering and retrieving Angular
1690 * modules.
1691 * All modules (angular core or 3rd party) that should be available to an application must be
1692 * registered using this mechanism.
1693 *
1694 * When passed two or more arguments, a new module is created. If passed only one argument, an
1695 * existing module (the name passed as the first argument to `module`) is retrieved.
1696 *
1697 *
1698 * # Module
1699 *
1700 * A module is a collection of services, directives, controllers, filters, and configuration information.
1701 * `angular.module` is used to configure the {@link auto.$injector $injector}.
1702 *
1703 * ```js
1704 * // Create a new module
1705 * var myModule = angular.module('myModule', []);
1706 *
1707 * // register a new service
1708 * myModule.value('appName', 'MyCoolApp');
1709 *
1710 * // configure existing services inside initialization blocks.
1711 * myModule.config(['$locationProvider', function($locationProvider) {
1712 * // Configure existing providers
1713 * $locationProvider.hashPrefix('!');
1714 * }]);
1715 * ```
1716 *
1717 * Then you can create an injector and load your modules like this:
1718 *
1719 * ```js
1720 * var injector = angular.injector(['ng', 'myModule'])
1721 * ```
1722 *
1723 * However it's more likely that you'll just use
1724 * {@link ng.directive:ngApp ngApp} or
1725 * {@link angular.bootstrap} to simplify this process for you.
1726 *
1727 * @param {!string} name The name of the module to create or retrieve.
1728 * @param {!Array.<string>=} requires If specified then new module is being created. If
1729 * unspecified then the module is being retrieved for further configuration.
1730 * @param {Function=} configFn Optional configuration function for the module. Same as
1731 * {@link angular.Module#config Module#config()}.
1732 * @returns {module} new module with the {@link angular.Module} api.
1733 */
1734 return function module(name, requires, configFn) {
1735 var assertNotHasOwnProperty = function(name, context) {
1736 if (name === 'hasOwnProperty') {
1737 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
1738 }
1739 };
1740
1741 assertNotHasOwnProperty(name, 'module');
1742 if (requires && modules.hasOwnProperty(name)) {
1743 modules[name] = null;
1744 }
1745 return ensure(modules, name, function() {
1746 if (!requires) {
1747 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
1748 "the module name or forgot to load it. If registering a module ensure that you " +
1749 "specify the dependencies as the second argument.", name);
1750 }
1751
1752 /** @type {!Array.<Array.<*>>} */
1753 var invokeQueue = [];
1754
1755 /** @type {!Array.<Function>} */
1756 var configBlocks = [];
1757
1758 /** @type {!Array.<Function>} */
1759 var runBlocks = [];
1760
1761 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
1762
1763 /** @type {angular.Module} */
1764 var moduleInstance = {
1765 // Private state
1766 _invokeQueue: invokeQueue,
1767 _configBlocks: configBlocks,
1768 _runBlocks: runBlocks,
1769
1770 /**
1771 * @ngdoc property
1772 * @name angular.Module#requires
1773 * @module ng
1774 *
1775 * @description
1776 * Holds the list of modules which the injector will load before the current module is
1777 * loaded.
1778 */
1779 requires: requires,
1780
1781 /**
1782 * @ngdoc property
1783 * @name angular.Module#name
1784 * @module ng
1785 *
1786 * @description
1787 * Name of the module.
1788 */
1789 name: name,
1790
1791
1792 /**
1793 * @ngdoc method
1794 * @name angular.Module#provider
1795 * @module ng
1796 * @param {string} name service name
1797 * @param {Function} providerType Construction function for creating new instance of the
1798 * service.
1799 * @description
1800 * See {@link auto.$provide#provider $provide.provider()}.
1801 */
1802 provider: invokeLater('$provide', 'provider'),
1803
1804 /**
1805 * @ngdoc method
1806 * @name angular.Module#factory
1807 * @module ng
1808 * @param {string} name service name
1809 * @param {Function} providerFunction Function for creating new instance of the service.
1810 * @description
1811 * See {@link auto.$provide#factory $provide.factory()}.
1812 */
1813 factory: invokeLater('$provide', 'factory'),
1814
1815 /**
1816 * @ngdoc method
1817 * @name angular.Module#service
1818 * @module ng
1819 * @param {string} name service name
1820 * @param {Function} constructor A constructor function that will be instantiated.
1821 * @description
1822 * See {@link auto.$provide#service $provide.service()}.
1823 */
1824 service: invokeLater('$provide', 'service'),
1825
1826 /**
1827 * @ngdoc method
1828 * @name angular.Module#value
1829 * @module ng
1830 * @param {string} name service name
1831 * @param {*} object Service instance object.
1832 * @description
1833 * See {@link auto.$provide#value $provide.value()}.
1834 */
1835 value: invokeLater('$provide', 'value'),
1836
1837 /**
1838 * @ngdoc method
1839 * @name angular.Module#constant
1840 * @module ng
1841 * @param {string} name constant name
1842 * @param {*} object Constant value.
1843 * @description
1844 * Because the constant are fixed, they get applied before other provide methods.
1845 * See {@link auto.$provide#constant $provide.constant()}.
1846 */
1847 constant: invokeLater('$provide', 'constant', 'unshift'),
1848
1849 /**
1850 * @ngdoc method
1851 * @name angular.Module#animation
1852 * @module ng
1853 * @param {string} name animation name
1854 * @param {Function} animationFactory Factory function for creating new instance of an
1855 * animation.
1856 * @description
1857 *
1858 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
1859 *
1860 *
1861 * Defines an animation hook that can be later used with
1862 * {@link ngAnimate.$animate $animate} service and directives that use this service.
1863 *
1864 * ```js
1865 * module.animation('.animation-name', function($inject1, $inject2) {
1866 * return {
1867 * eventName : function(element, done) {
1868 * //code to run the animation
1869 * //once complete, then run done()
1870 * return function cancellationFunction(element) {
1871 * //code to cancel the animation
1872 * }
1873 * }
1874 * }
1875 * })
1876 * ```
1877 *
1878 * See {@link ng.$animateProvider#register $animateProvider.register()} and
1879 * {@link ngAnimate ngAnimate module} for more information.
1880 */
1881 animation: invokeLater('$animateProvider', 'register'),
1882
1883 /**
1884 * @ngdoc method
1885 * @name angular.Module#filter
1886 * @module ng
1887 * @param {string} name Filter name.
1888 * @param {Function} filterFactory Factory function for creating new instance of filter.
1889 * @description
1890 * See {@link ng.$filterProvider#register $filterProvider.register()}.
1891 */
1892 filter: invokeLater('$filterProvider', 'register'),
1893
1894 /**
1895 * @ngdoc method
1896 * @name angular.Module#controller
1897 * @module ng
1898 * @param {string|Object} name Controller name, or an object map of controllers where the
1899 * keys are the names and the values are the constructors.
1900 * @param {Function} constructor Controller constructor function.
1901 * @description
1902 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
1903 */
1904 controller: invokeLater('$controllerProvider', 'register'),
1905
1906 /**
1907 * @ngdoc method
1908 * @name angular.Module#directive
1909 * @module ng
1910 * @param {string|Object} name Directive name, or an object map of directives where the
1911 * keys are the names and the values are the factories.
1912 * @param {Function} directiveFactory Factory function for creating new instance of
1913 * directives.
1914 * @description
1915 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
1916 */
1917 directive: invokeLater('$compileProvider', 'directive'),
1918
1919 /**
1920 * @ngdoc method
1921 * @name angular.Module#config
1922 * @module ng
1923 * @param {Function} configFn Execute this function on module load. Useful for service
1924 * configuration.
1925 * @description
1926 * Use this method to register work which needs to be performed on module loading.
1927 * For more about how to configure services, see
1928 * {@link providers#provider-recipe Provider Recipe}.
1929 */
1930 config: config,
1931
1932 /**
1933 * @ngdoc method
1934 * @name angular.Module#run
1935 * @module ng
1936 * @param {Function} initializationFn Execute this function after injector creation.
1937 * Useful for application initialization.
1938 * @description
1939 * Use this method to register work which should be performed when the injector is done
1940 * loading all modules.
1941 */
1942 run: function(block) {
1943 runBlocks.push(block);
1944 return this;
1945 }
1946 };
1947
1948 if (configFn) {
1949 config(configFn);
1950 }
1951
1952 return moduleInstance;
1953
1954 /**
1955 * @param {string} provider
1956 * @param {string} method
1957 * @param {String=} insertMethod
1958 * @returns {angular.Module}
1959 */
1960 function invokeLater(provider, method, insertMethod, queue) {
1961 if (!queue) queue = invokeQueue;
1962 return function() {
1963 queue[insertMethod || 'push']([provider, method, arguments]);
1964 return moduleInstance;
1965 };
1966 }
1967 });
1968 };
1969 });
1970
1971}
1972
1973/* global: toDebugString: true */
1974
1975function serializeObject(obj) {
1976 var seen = [];
1977
1978 return JSON.stringify(obj, function(key, val) {
1979 val = toJsonReplacer(key, val);
1980 if (isObject(val)) {
1981
1982 if (seen.indexOf(val) >= 0) return '<<already seen>>';
1983
1984 seen.push(val);
1985 }
1986 return val;
1987 });
1988}
1989
1990function toDebugString(obj) {
1991 if (typeof obj === 'function') {
1992 return obj.toString().replace(/ \{[\s\S]*$/, '');
1993 } else if (typeof obj === 'undefined') {
1994 return 'undefined';
1995 } else if (typeof obj !== 'string') {
1996 return serializeObject(obj);
1997 }
1998 return obj;
1999}
2000
2001/* global angularModule: true,
2002 version: true,
2003
2004 $LocaleProvider,
2005 $CompileProvider,
2006
2007 htmlAnchorDirective,
2008 inputDirective,
2009 inputDirective,
2010 formDirective,
2011 scriptDirective,
2012 selectDirective,
2013 styleDirective,
2014 optionDirective,
2015 ngBindDirective,
2016 ngBindHtmlDirective,
2017 ngBindTemplateDirective,
2018 ngClassDirective,
2019 ngClassEvenDirective,
2020 ngClassOddDirective,
2021 ngCspDirective,
2022 ngCloakDirective,
2023 ngControllerDirective,
2024 ngFormDirective,
2025 ngHideDirective,
2026 ngIfDirective,
2027 ngIncludeDirective,
2028 ngIncludeFillContentDirective,
2029 ngInitDirective,
2030 ngNonBindableDirective,
2031 ngPluralizeDirective,
2032 ngRepeatDirective,
2033 ngShowDirective,
2034 ngStyleDirective,
2035 ngSwitchDirective,
2036 ngSwitchWhenDirective,
2037 ngSwitchDefaultDirective,
2038 ngOptionsDirective,
2039 ngTranscludeDirective,
2040 ngModelDirective,
2041 ngListDirective,
2042 ngChangeDirective,
2043 patternDirective,
2044 patternDirective,
2045 requiredDirective,
2046 requiredDirective,
2047 minlengthDirective,
2048 minlengthDirective,
2049 maxlengthDirective,
2050 maxlengthDirective,
2051 ngValueDirective,
2052 ngModelOptionsDirective,
2053 ngAttributeAliasDirectives,
2054 ngEventDirectives,
2055
2056 $AnchorScrollProvider,
2057 $AnimateProvider,
2058 $BrowserProvider,
2059 $CacheFactoryProvider,
2060 $ControllerProvider,
2061 $DocumentProvider,
2062 $ExceptionHandlerProvider,
2063 $FilterProvider,
2064 $InterpolateProvider,
2065 $IntervalProvider,
2066 $HttpProvider,
2067 $HttpBackendProvider,
2068 $LocationProvider,
2069 $LogProvider,
2070 $ParseProvider,
2071 $RootScopeProvider,
2072 $QProvider,
2073 $$QProvider,
2074 $$SanitizeUriProvider,
2075 $SceProvider,
2076 $SceDelegateProvider,
2077 $SnifferProvider,
2078 $TemplateCacheProvider,
2079 $TemplateRequestProvider,
2080 $$TestabilityProvider,
2081 $TimeoutProvider,
2082 $$RAFProvider,
2083 $$AsyncCallbackProvider,
2084 $WindowProvider
2085*/
2086
2087
2088/**
2089 * @ngdoc object
2090 * @name angular.version
2091 * @module ng
2092 * @description
2093 * An object that contains information about the current AngularJS version. This object has the
2094 * following properties:
2095 *
2096 * - `full` – `{string}` – Full version string, such as "0.9.18".
2097 * - `major` – `{number}` – Major version number, such as "0".
2098 * - `minor` – `{number}` – Minor version number, such as "9".
2099 * - `dot` – `{number}` – Dot version number, such as "18".
2100 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2101 */
2102var version = {
2103 full: '1.3.5', // all of these placeholder strings will be replaced by grunt's
2104 major: 1, // package task
2105 minor: 3,
2106 dot: 5,
2107 codeName: 'cybernetic-mercantilism'
2108};
2109
2110
2111function publishExternalAPI(angular) {
2112 extend(angular, {
2113 'bootstrap': bootstrap,
2114 'copy': copy,
2115 'extend': extend,
2116 'equals': equals,
2117 'element': jqLite,
2118 'forEach': forEach,
2119 'injector': createInjector,
2120 'noop': noop,
2121 'bind': bind,
2122 'toJson': toJson,
2123 'fromJson': fromJson,
2124 'identity': identity,
2125 'isUndefined': isUndefined,
2126 'isDefined': isDefined,
2127 'isString': isString,
2128 'isFunction': isFunction,
2129 'isObject': isObject,
2130 'isNumber': isNumber,
2131 'isElement': isElement,
2132 'isArray': isArray,
2133 'version': version,
2134 'isDate': isDate,
2135 'lowercase': lowercase,
2136 'uppercase': uppercase,
2137 'callbacks': {counter: 0},
2138 'getTestability': getTestability,
2139 '$$minErr': minErr,
2140 '$$csp': csp,
2141 'reloadWithDebugInfo': reloadWithDebugInfo
2142 });
2143
2144 angularModule = setupModuleLoader(window);
2145 try {
2146 angularModule('ngLocale');
2147 } catch (e) {
2148 angularModule('ngLocale', []).provider('$locale', $LocaleProvider);
2149 }
2150
2151 angularModule('ng', ['ngLocale'], ['$provide',
2152 function ngModule($provide) {
2153 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2154 $provide.provider({
2155 $$sanitizeUri: $$SanitizeUriProvider
2156 });
2157 $provide.provider('$compile', $CompileProvider).
2158 directive({
2159 a: htmlAnchorDirective,
2160 input: inputDirective,
2161 textarea: inputDirective,
2162 form: formDirective,
2163 script: scriptDirective,
2164 select: selectDirective,
2165 style: styleDirective,
2166 option: optionDirective,
2167 ngBind: ngBindDirective,
2168 ngBindHtml: ngBindHtmlDirective,
2169 ngBindTemplate: ngBindTemplateDirective,
2170 ngClass: ngClassDirective,
2171 ngClassEven: ngClassEvenDirective,
2172 ngClassOdd: ngClassOddDirective,
2173 ngCloak: ngCloakDirective,
2174 ngController: ngControllerDirective,
2175 ngForm: ngFormDirective,
2176 ngHide: ngHideDirective,
2177 ngIf: ngIfDirective,
2178 ngInclude: ngIncludeDirective,
2179 ngInit: ngInitDirective,
2180 ngNonBindable: ngNonBindableDirective,
2181 ngPluralize: ngPluralizeDirective,
2182 ngRepeat: ngRepeatDirective,
2183 ngShow: ngShowDirective,
2184 ngStyle: ngStyleDirective,
2185 ngSwitch: ngSwitchDirective,
2186 ngSwitchWhen: ngSwitchWhenDirective,
2187 ngSwitchDefault: ngSwitchDefaultDirective,
2188 ngOptions: ngOptionsDirective,
2189 ngTransclude: ngTranscludeDirective,
2190 ngModel: ngModelDirective,
2191 ngList: ngListDirective,
2192 ngChange: ngChangeDirective,
2193 pattern: patternDirective,
2194 ngPattern: patternDirective,
2195 required: requiredDirective,
2196 ngRequired: requiredDirective,
2197 minlength: minlengthDirective,
2198 ngMinlength: minlengthDirective,
2199 maxlength: maxlengthDirective,
2200 ngMaxlength: maxlengthDirective,
2201 ngValue: ngValueDirective,
2202 ngModelOptions: ngModelOptionsDirective
2203 }).
2204 directive({
2205 ngInclude: ngIncludeFillContentDirective
2206 }).
2207 directive(ngAttributeAliasDirectives).
2208 directive(ngEventDirectives);
2209 $provide.provider({
2210 $anchorScroll: $AnchorScrollProvider,
2211 $animate: $AnimateProvider,
2212 $browser: $BrowserProvider,
2213 $cacheFactory: $CacheFactoryProvider,
2214 $controller: $ControllerProvider,
2215 $document: $DocumentProvider,
2216 $exceptionHandler: $ExceptionHandlerProvider,
2217 $filter: $FilterProvider,
2218 $interpolate: $InterpolateProvider,
2219 $interval: $IntervalProvider,
2220 $http: $HttpProvider,
2221 $httpBackend: $HttpBackendProvider,
2222 $location: $LocationProvider,
2223 $log: $LogProvider,
2224 $parse: $ParseProvider,
2225 $rootScope: $RootScopeProvider,
2226 $q: $QProvider,
2227 $$q: $$QProvider,
2228 $sce: $SceProvider,
2229 $sceDelegate: $SceDelegateProvider,
2230 $sniffer: $SnifferProvider,
2231 $templateCache: $TemplateCacheProvider,
2232 $templateRequest: $TemplateRequestProvider,
2233 $$testability: $$TestabilityProvider,
2234 $timeout: $TimeoutProvider,
2235 $window: $WindowProvider,
2236 $$rAF: $$RAFProvider,
2237 $$asyncCallback: $$AsyncCallbackProvider
2238 });
2239 }
2240 ]);
2241}
2242
2243/* global JQLitePrototype: true,
2244 addEventListenerFn: true,
2245 removeEventListenerFn: true,
2246 BOOLEAN_ATTR: true,
2247 ALIASED_ATTR: true,
2248*/
2249
2250//////////////////////////////////
2251//JQLite
2252//////////////////////////////////
2253
2254/**
2255 * @ngdoc function
2256 * @name angular.element
2257 * @module ng
2258 * @kind function
2259 *
2260 * @description
2261 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2262 *
2263 * If jQuery is available, `angular.element` is an alias for the
2264 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2265 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2266 *
2267 * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2268 * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2269 * commonly needed functionality with the goal of having a very small footprint.</div>
2270 *
2271 * To use jQuery, simply load it before `DOMContentLoaded` event fired.
2272 *
2273 * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2274 * jqLite; they are never raw DOM references.</div>
2275 *
2276 * ## Angular's jqLite
2277 * jqLite provides only the following jQuery methods:
2278 *
2279 * - [`addClass()`](http://api.jquery.com/addClass/)
2280 * - [`after()`](http://api.jquery.com/after/)
2281 * - [`append()`](http://api.jquery.com/append/)
2282 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2283 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2284 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2285 * - [`clone()`](http://api.jquery.com/clone/)
2286 * - [`contents()`](http://api.jquery.com/contents/)
2287 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`
2288 * - [`data()`](http://api.jquery.com/data/)
2289 * - [`detach()`](http://api.jquery.com/detach/)
2290 * - [`empty()`](http://api.jquery.com/empty/)
2291 * - [`eq()`](http://api.jquery.com/eq/)
2292 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2293 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2294 * - [`html()`](http://api.jquery.com/html/)
2295 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2296 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2297 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces or selectors
2298 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2299 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2300 * - [`prepend()`](http://api.jquery.com/prepend/)
2301 * - [`prop()`](http://api.jquery.com/prop/)
2302 * - [`ready()`](http://api.jquery.com/ready/)
2303 * - [`remove()`](http://api.jquery.com/remove/)
2304 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2305 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2306 * - [`removeData()`](http://api.jquery.com/removeData/)
2307 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2308 * - [`text()`](http://api.jquery.com/text/)
2309 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2310 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2311 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces
2312 * - [`val()`](http://api.jquery.com/val/)
2313 * - [`wrap()`](http://api.jquery.com/wrap/)
2314 *
2315 * ## jQuery/jqLite Extras
2316 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2317 *
2318 * ### Events
2319 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2320 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2321 * element before it is removed.
2322 *
2323 * ### Methods
2324 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2325 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2326 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2327 * `'ngModel'`).
2328 * - `injector()` - retrieves the injector of the current element or its parent.
2329 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2330 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2331 * be enabled.
2332 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2333 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2334 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2335 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2336 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2337 * parent element is reached.
2338 *
2339 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2340 * @returns {Object} jQuery object.
2341 */
2342
2343JQLite.expando = 'ng339';
2344
2345var jqCache = JQLite.cache = {},
2346 jqId = 1,
2347 addEventListenerFn = function(element, type, fn) {
2348 element.addEventListener(type, fn, false);
2349 },
2350 removeEventListenerFn = function(element, type, fn) {
2351 element.removeEventListener(type, fn, false);
2352 };
2353
2354/*
2355 * !!! This is an undocumented "private" function !!!
2356 */
2357JQLite._data = function(node) {
2358 //jQuery always returns an object on cache miss
2359 return this.cache[node[this.expando]] || {};
2360};
2361
2362function jqNextId() { return ++jqId; }
2363
2364
2365var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2366var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2367var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2368var jqLiteMinErr = minErr('jqLite');
2369
2370/**
2371 * Converts snake_case to camelCase.
2372 * Also there is special case for Moz prefix starting with upper case letter.
2373 * @param name Name to normalize
2374 */
2375function camelCase(name) {
2376 return name.
2377 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2378 return offset ? letter.toUpperCase() : letter;
2379 }).
2380 replace(MOZ_HACK_REGEXP, 'Moz$1');
2381}
2382
2383var SINGLE_TAG_REGEXP = /^<(\w+)\s*\/?>(?:<\/\1>|)$/;
2384var HTML_REGEXP = /<|&#?\w+;/;
2385var TAG_NAME_REGEXP = /<([\w:]+)/;
2386var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi;
2387
2388var wrapMap = {
2389 'option': [1, '<select multiple="multiple">', '</select>'],
2390
2391 'thead': [1, '<table>', '</table>'],
2392 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2393 'tr': [2, '<table><tbody>', '</tbody></table>'],
2394 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2395 '_default': [0, "", ""]
2396};
2397
2398wrapMap.optgroup = wrapMap.option;
2399wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2400wrapMap.th = wrapMap.td;
2401
2402
2403function jqLiteIsTextNode(html) {
2404 return !HTML_REGEXP.test(html);
2405}
2406
2407function jqLiteAcceptsData(node) {
2408 // The window object can accept data but has no nodeType
2409 // Otherwise we are only interested in elements (1) and documents (9)
2410 var nodeType = node.nodeType;
2411 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2412}
2413
2414function jqLiteBuildFragment(html, context) {
2415 var tmp, tag, wrap,
2416 fragment = context.createDocumentFragment(),
2417 nodes = [], i;
2418
2419 if (jqLiteIsTextNode(html)) {
2420 // Convert non-html into a text node
2421 nodes.push(context.createTextNode(html));
2422 } else {
2423 // Convert html into DOM nodes
2424 tmp = tmp || fragment.appendChild(context.createElement("div"));
2425 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2426 wrap = wrapMap[tag] || wrapMap._default;
2427 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2428
2429 // Descend through wrappers to the right content
2430 i = wrap[0];
2431 while (i--) {
2432 tmp = tmp.lastChild;
2433 }
2434
2435 nodes = concat(nodes, tmp.childNodes);
2436
2437 tmp = fragment.firstChild;
2438 tmp.textContent = "";
2439 }
2440
2441 // Remove wrapper from fragment
2442 fragment.textContent = "";
2443 fragment.innerHTML = ""; // Clear inner HTML
2444 forEach(nodes, function(node) {
2445 fragment.appendChild(node);
2446 });
2447
2448 return fragment;
2449}
2450
2451function jqLiteParseHTML(html, context) {
2452 context = context || document;
2453 var parsed;
2454
2455 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2456 return [context.createElement(parsed[1])];
2457 }
2458
2459 if ((parsed = jqLiteBuildFragment(html, context))) {
2460 return parsed.childNodes;
2461 }
2462
2463 return [];
2464}
2465
2466/////////////////////////////////////////////
2467function JQLite(element) {
2468 if (element instanceof JQLite) {
2469 return element;
2470 }
2471
2472 var argIsString;
2473
2474 if (isString(element)) {
2475 element = trim(element);
2476 argIsString = true;
2477 }
2478 if (!(this instanceof JQLite)) {
2479 if (argIsString && element.charAt(0) != '<') {
2480 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2481 }
2482 return new JQLite(element);
2483 }
2484
2485 if (argIsString) {
2486 jqLiteAddNodes(this, jqLiteParseHTML(element));
2487 } else {
2488 jqLiteAddNodes(this, element);
2489 }
2490}
2491
2492function jqLiteClone(element) {
2493 return element.cloneNode(true);
2494}
2495
2496function jqLiteDealoc(element, onlyDescendants) {
2497 if (!onlyDescendants) jqLiteRemoveData(element);
2498
2499 if (element.querySelectorAll) {
2500 var descendants = element.querySelectorAll('*');
2501 for (var i = 0, l = descendants.length; i < l; i++) {
2502 jqLiteRemoveData(descendants[i]);
2503 }
2504 }
2505}
2506
2507function jqLiteOff(element, type, fn, unsupported) {
2508 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2509
2510 var expandoStore = jqLiteExpandoStore(element);
2511 var events = expandoStore && expandoStore.events;
2512 var handle = expandoStore && expandoStore.handle;
2513
2514 if (!handle) return; //no listeners registered
2515
2516 if (!type) {
2517 for (type in events) {
2518 if (type !== '$destroy') {
2519 removeEventListenerFn(element, type, handle);
2520 }
2521 delete events[type];
2522 }
2523 } else {
2524 forEach(type.split(' '), function(type) {
2525 if (isDefined(fn)) {
2526 var listenerFns = events[type];
2527 arrayRemove(listenerFns || [], fn);
2528 if (listenerFns && listenerFns.length > 0) {
2529 return;
2530 }
2531 }
2532
2533 removeEventListenerFn(element, type, handle);
2534 delete events[type];
2535 });
2536 }
2537}
2538
2539function jqLiteRemoveData(element, name) {
2540 var expandoId = element.ng339;
2541 var expandoStore = expandoId && jqCache[expandoId];
2542
2543 if (expandoStore) {
2544 if (name) {
2545 delete expandoStore.data[name];
2546 return;
2547 }
2548
2549 if (expandoStore.handle) {
2550 if (expandoStore.events.$destroy) {
2551 expandoStore.handle({}, '$destroy');
2552 }
2553 jqLiteOff(element);
2554 }
2555 delete jqCache[expandoId];
2556 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2557 }
2558}
2559
2560
2561function jqLiteExpandoStore(element, createIfNecessary) {
2562 var expandoId = element.ng339,
2563 expandoStore = expandoId && jqCache[expandoId];
2564
2565 if (createIfNecessary && !expandoStore) {
2566 element.ng339 = expandoId = jqNextId();
2567 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2568 }
2569
2570 return expandoStore;
2571}
2572
2573
2574function jqLiteData(element, key, value) {
2575 if (jqLiteAcceptsData(element)) {
2576
2577 var isSimpleSetter = isDefined(value);
2578 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2579 var massGetter = !key;
2580 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2581 var data = expandoStore && expandoStore.data;
2582
2583 if (isSimpleSetter) { // data('key', value)
2584 data[key] = value;
2585 } else {
2586 if (massGetter) { // data()
2587 return data;
2588 } else {
2589 if (isSimpleGetter) { // data('key')
2590 // don't force creation of expandoStore if it doesn't exist yet
2591 return data && data[key];
2592 } else { // mass-setter: data({key1: val1, key2: val2})
2593 extend(data, key);
2594 }
2595 }
2596 }
2597 }
2598}
2599
2600function jqLiteHasClass(element, selector) {
2601 if (!element.getAttribute) return false;
2602 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
2603 indexOf(" " + selector + " ") > -1);
2604}
2605
2606function jqLiteRemoveClass(element, cssClasses) {
2607 if (cssClasses && element.setAttribute) {
2608 forEach(cssClasses.split(' '), function(cssClass) {
2609 element.setAttribute('class', trim(
2610 (" " + (element.getAttribute('class') || '') + " ")
2611 .replace(/[\n\t]/g, " ")
2612 .replace(" " + trim(cssClass) + " ", " "))
2613 );
2614 });
2615 }
2616}
2617
2618function jqLiteAddClass(element, cssClasses) {
2619 if (cssClasses && element.setAttribute) {
2620 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
2621 .replace(/[\n\t]/g, " ");
2622
2623 forEach(cssClasses.split(' '), function(cssClass) {
2624 cssClass = trim(cssClass);
2625 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
2626 existingClasses += cssClass + ' ';
2627 }
2628 });
2629
2630 element.setAttribute('class', trim(existingClasses));
2631 }
2632}
2633
2634
2635function jqLiteAddNodes(root, elements) {
2636 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
2637
2638 if (elements) {
2639
2640 // if a Node (the most common case)
2641 if (elements.nodeType) {
2642 root[root.length++] = elements;
2643 } else {
2644 var length = elements.length;
2645
2646 // if an Array or NodeList and not a Window
2647 if (typeof length === 'number' && elements.window !== elements) {
2648 if (length) {
2649 for (var i = 0; i < length; i++) {
2650 root[root.length++] = elements[i];
2651 }
2652 }
2653 } else {
2654 root[root.length++] = elements;
2655 }
2656 }
2657 }
2658}
2659
2660
2661function jqLiteController(element, name) {
2662 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
2663}
2664
2665function jqLiteInheritedData(element, name, value) {
2666 // if element is the document object work with the html element instead
2667 // this makes $(document).scope() possible
2668 if (element.nodeType == NODE_TYPE_DOCUMENT) {
2669 element = element.documentElement;
2670 }
2671 var names = isArray(name) ? name : [name];
2672
2673 while (element) {
2674 for (var i = 0, ii = names.length; i < ii; i++) {
2675 if ((value = jqLite.data(element, names[i])) !== undefined) return value;
2676 }
2677
2678 // If dealing with a document fragment node with a host element, and no parent, use the host
2679 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
2680 // to lookup parent controllers.
2681 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
2682 }
2683}
2684
2685function jqLiteEmpty(element) {
2686 jqLiteDealoc(element, true);
2687 while (element.firstChild) {
2688 element.removeChild(element.firstChild);
2689 }
2690}
2691
2692function jqLiteRemove(element, keepData) {
2693 if (!keepData) jqLiteDealoc(element);
2694 var parent = element.parentNode;
2695 if (parent) parent.removeChild(element);
2696}
2697
2698
2699function jqLiteDocumentLoaded(action, win) {
2700 win = win || window;
2701 if (win.document.readyState === 'complete') {
2702 // Force the action to be run async for consistent behaviour
2703 // from the action's point of view
2704 // i.e. it will definitely not be in a $apply
2705 win.setTimeout(action);
2706 } else {
2707 // No need to unbind this handler as load is only ever called once
2708 jqLite(win).on('load', action);
2709 }
2710}
2711
2712//////////////////////////////////////////
2713// Functions which are declared directly.
2714//////////////////////////////////////////
2715var JQLitePrototype = JQLite.prototype = {
2716 ready: function(fn) {
2717 var fired = false;
2718
2719 function trigger() {
2720 if (fired) return;
2721 fired = true;
2722 fn();
2723 }
2724
2725 // check if document is already loaded
2726 if (document.readyState === 'complete') {
2727 setTimeout(trigger);
2728 } else {
2729 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
2730 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
2731 // jshint -W064
2732 JQLite(window).on('load', trigger); // fallback to window.onload for others
2733 // jshint +W064
2734 }
2735 },
2736 toString: function() {
2737 var value = [];
2738 forEach(this, function(e) { value.push('' + e);});
2739 return '[' + value.join(', ') + ']';
2740 },
2741
2742 eq: function(index) {
2743 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
2744 },
2745
2746 length: 0,
2747 push: push,
2748 sort: [].sort,
2749 splice: [].splice
2750};
2751
2752//////////////////////////////////////////
2753// Functions iterating getter/setters.
2754// these functions return self on setter and
2755// value on get.
2756//////////////////////////////////////////
2757var BOOLEAN_ATTR = {};
2758forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
2759 BOOLEAN_ATTR[lowercase(value)] = value;
2760});
2761var BOOLEAN_ELEMENTS = {};
2762forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
2763 BOOLEAN_ELEMENTS[value] = true;
2764});
2765var ALIASED_ATTR = {
2766 'ngMinlength': 'minlength',
2767 'ngMaxlength': 'maxlength',
2768 'ngMin': 'min',
2769 'ngMax': 'max',
2770 'ngPattern': 'pattern'
2771};
2772
2773function getBooleanAttrName(element, name) {
2774 // check dom last since we will most likely fail on name
2775 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
2776
2777 // booleanAttr is here twice to minimize DOM access
2778 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
2779}
2780
2781function getAliasedAttrName(element, name) {
2782 var nodeName = element.nodeName;
2783 return (nodeName === 'INPUT' || nodeName === 'TEXTAREA') && ALIASED_ATTR[name];
2784}
2785
2786forEach({
2787 data: jqLiteData,
2788 removeData: jqLiteRemoveData
2789}, function(fn, name) {
2790 JQLite[name] = fn;
2791});
2792
2793forEach({
2794 data: jqLiteData,
2795 inheritedData: jqLiteInheritedData,
2796
2797 scope: function(element) {
2798 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2799 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
2800 },
2801
2802 isolateScope: function(element) {
2803 // Can't use jqLiteData here directly so we stay compatible with jQuery!
2804 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
2805 },
2806
2807 controller: jqLiteController,
2808
2809 injector: function(element) {
2810 return jqLiteInheritedData(element, '$injector');
2811 },
2812
2813 removeAttr: function(element, name) {
2814 element.removeAttribute(name);
2815 },
2816
2817 hasClass: jqLiteHasClass,
2818
2819 css: function(element, name, value) {
2820 name = camelCase(name);
2821
2822 if (isDefined(value)) {
2823 element.style[name] = value;
2824 } else {
2825 return element.style[name];
2826 }
2827 },
2828
2829 attr: function(element, name, value) {
2830 var lowercasedName = lowercase(name);
2831 if (BOOLEAN_ATTR[lowercasedName]) {
2832 if (isDefined(value)) {
2833 if (!!value) {
2834 element[name] = true;
2835 element.setAttribute(name, lowercasedName);
2836 } else {
2837 element[name] = false;
2838 element.removeAttribute(lowercasedName);
2839 }
2840 } else {
2841 return (element[name] ||
2842 (element.attributes.getNamedItem(name) || noop).specified)
2843 ? lowercasedName
2844 : undefined;
2845 }
2846 } else if (isDefined(value)) {
2847 element.setAttribute(name, value);
2848 } else if (element.getAttribute) {
2849 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
2850 // some elements (e.g. Document) don't have get attribute, so return undefined
2851 var ret = element.getAttribute(name, 2);
2852 // normalize non-existing attributes to undefined (as jQuery)
2853 return ret === null ? undefined : ret;
2854 }
2855 },
2856
2857 prop: function(element, name, value) {
2858 if (isDefined(value)) {
2859 element[name] = value;
2860 } else {
2861 return element[name];
2862 }
2863 },
2864
2865 text: (function() {
2866 getText.$dv = '';
2867 return getText;
2868
2869 function getText(element, value) {
2870 if (isUndefined(value)) {
2871 var nodeType = element.nodeType;
2872 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
2873 }
2874 element.textContent = value;
2875 }
2876 })(),
2877
2878 val: function(element, value) {
2879 if (isUndefined(value)) {
2880 if (element.multiple && nodeName_(element) === 'select') {
2881 var result = [];
2882 forEach(element.options, function(option) {
2883 if (option.selected) {
2884 result.push(option.value || option.text);
2885 }
2886 });
2887 return result.length === 0 ? null : result;
2888 }
2889 return element.value;
2890 }
2891 element.value = value;
2892 },
2893
2894 html: function(element, value) {
2895 if (isUndefined(value)) {
2896 return element.innerHTML;
2897 }
2898 jqLiteDealoc(element, true);
2899 element.innerHTML = value;
2900 },
2901
2902 empty: jqLiteEmpty
2903}, function(fn, name) {
2904 /**
2905 * Properties: writes return selection, reads return first value
2906 */
2907 JQLite.prototype[name] = function(arg1, arg2) {
2908 var i, key;
2909 var nodeCount = this.length;
2910
2911 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
2912 // in a way that survives minification.
2913 // jqLiteEmpty takes no arguments but is a setter.
2914 if (fn !== jqLiteEmpty &&
2915 (((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2) === undefined)) {
2916 if (isObject(arg1)) {
2917
2918 // we are a write, but the object properties are the key/values
2919 for (i = 0; i < nodeCount; i++) {
2920 if (fn === jqLiteData) {
2921 // data() takes the whole object in jQuery
2922 fn(this[i], arg1);
2923 } else {
2924 for (key in arg1) {
2925 fn(this[i], key, arg1[key]);
2926 }
2927 }
2928 }
2929 // return self for chaining
2930 return this;
2931 } else {
2932 // we are a read, so read the first child.
2933 // TODO: do we still need this?
2934 var value = fn.$dv;
2935 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
2936 var jj = (value === undefined) ? Math.min(nodeCount, 1) : nodeCount;
2937 for (var j = 0; j < jj; j++) {
2938 var nodeValue = fn(this[j], arg1, arg2);
2939 value = value ? value + nodeValue : nodeValue;
2940 }
2941 return value;
2942 }
2943 } else {
2944 // we are a write, so apply to all children
2945 for (i = 0; i < nodeCount; i++) {
2946 fn(this[i], arg1, arg2);
2947 }
2948 // return self for chaining
2949 return this;
2950 }
2951 };
2952});
2953
2954function createEventHandler(element, events) {
2955 var eventHandler = function(event, type) {
2956 // jQuery specific api
2957 event.isDefaultPrevented = function() {
2958 return event.defaultPrevented;
2959 };
2960
2961 var eventFns = events[type || event.type];
2962 var eventFnsLength = eventFns ? eventFns.length : 0;
2963
2964 if (!eventFnsLength) return;
2965
2966 if (isUndefined(event.immediatePropagationStopped)) {
2967 var originalStopImmediatePropagation = event.stopImmediatePropagation;
2968 event.stopImmediatePropagation = function() {
2969 event.immediatePropagationStopped = true;
2970
2971 if (event.stopPropagation) {
2972 event.stopPropagation();
2973 }
2974
2975 if (originalStopImmediatePropagation) {
2976 originalStopImmediatePropagation.call(event);
2977 }
2978 };
2979 }
2980
2981 event.isImmediatePropagationStopped = function() {
2982 return event.immediatePropagationStopped === true;
2983 };
2984
2985 // Copy event handlers in case event handlers array is modified during execution.
2986 if ((eventFnsLength > 1)) {
2987 eventFns = shallowCopy(eventFns);
2988 }
2989
2990 for (var i = 0; i < eventFnsLength; i++) {
2991 if (!event.isImmediatePropagationStopped()) {
2992 eventFns[i].call(element, event);
2993 }
2994 }
2995 };
2996
2997 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
2998 // events on `element`
2999 eventHandler.elem = element;
3000 return eventHandler;
3001}
3002
3003//////////////////////////////////////////
3004// Functions iterating traversal.
3005// These functions chain results into a single
3006// selector.
3007//////////////////////////////////////////
3008forEach({
3009 removeData: jqLiteRemoveData,
3010
3011 on: function jqLiteOn(element, type, fn, unsupported) {
3012 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3013
3014 // Do not add event handlers to non-elements because they will not be cleaned up.
3015 if (!jqLiteAcceptsData(element)) {
3016 return;
3017 }
3018
3019 var expandoStore = jqLiteExpandoStore(element, true);
3020 var events = expandoStore.events;
3021 var handle = expandoStore.handle;
3022
3023 if (!handle) {
3024 handle = expandoStore.handle = createEventHandler(element, events);
3025 }
3026
3027 // http://jsperf.com/string-indexof-vs-split
3028 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3029 var i = types.length;
3030
3031 while (i--) {
3032 type = types[i];
3033 var eventFns = events[type];
3034
3035 if (!eventFns) {
3036 events[type] = [];
3037
3038 if (type === 'mouseenter' || type === 'mouseleave') {
3039 // Refer to jQuery's implementation of mouseenter & mouseleave
3040 // Read about mouseenter and mouseleave:
3041 // http://www.quirksmode.org/js/events_mouse.html#link8
3042
3043 jqLiteOn(element, MOUSE_EVENT_MAP[type], function(event) {
3044 var target = this, related = event.relatedTarget;
3045 // For mousenter/leave call the handler if related is outside the target.
3046 // NB: No relatedTarget if the mouse left/entered the browser window
3047 if (!related || (related !== target && !target.contains(related))) {
3048 handle(event, type);
3049 }
3050 });
3051
3052 } else {
3053 if (type !== '$destroy') {
3054 addEventListenerFn(element, type, handle);
3055 }
3056 }
3057 eventFns = events[type];
3058 }
3059 eventFns.push(fn);
3060 }
3061 },
3062
3063 off: jqLiteOff,
3064
3065 one: function(element, type, fn) {
3066 element = jqLite(element);
3067
3068 //add the listener twice so that when it is called
3069 //you can remove the original function and still be
3070 //able to call element.off(ev, fn) normally
3071 element.on(type, function onFn() {
3072 element.off(type, fn);
3073 element.off(type, onFn);
3074 });
3075 element.on(type, fn);
3076 },
3077
3078 replaceWith: function(element, replaceNode) {
3079 var index, parent = element.parentNode;
3080 jqLiteDealoc(element);
3081 forEach(new JQLite(replaceNode), function(node) {
3082 if (index) {
3083 parent.insertBefore(node, index.nextSibling);
3084 } else {
3085 parent.replaceChild(node, element);
3086 }
3087 index = node;
3088 });
3089 },
3090
3091 children: function(element) {
3092 var children = [];
3093 forEach(element.childNodes, function(element) {
3094 if (element.nodeType === NODE_TYPE_ELEMENT)
3095 children.push(element);
3096 });
3097 return children;
3098 },
3099
3100 contents: function(element) {
3101 return element.contentDocument || element.childNodes || [];
3102 },
3103
3104 append: function(element, node) {
3105 var nodeType = element.nodeType;
3106 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3107
3108 node = new JQLite(node);
3109
3110 for (var i = 0, ii = node.length; i < ii; i++) {
3111 var child = node[i];
3112 element.appendChild(child);
3113 }
3114 },
3115
3116 prepend: function(element, node) {
3117 if (element.nodeType === NODE_TYPE_ELEMENT) {
3118 var index = element.firstChild;
3119 forEach(new JQLite(node), function(child) {
3120 element.insertBefore(child, index);
3121 });
3122 }
3123 },
3124
3125 wrap: function(element, wrapNode) {
3126 wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3127 var parent = element.parentNode;
3128 if (parent) {
3129 parent.replaceChild(wrapNode, element);
3130 }
3131 wrapNode.appendChild(element);
3132 },
3133
3134 remove: jqLiteRemove,
3135
3136 detach: function(element) {
3137 jqLiteRemove(element, true);
3138 },
3139
3140 after: function(element, newElement) {
3141 var index = element, parent = element.parentNode;
3142 newElement = new JQLite(newElement);
3143
3144 for (var i = 0, ii = newElement.length; i < ii; i++) {
3145 var node = newElement[i];
3146 parent.insertBefore(node, index.nextSibling);
3147 index = node;
3148 }
3149 },
3150
3151 addClass: jqLiteAddClass,
3152 removeClass: jqLiteRemoveClass,
3153
3154 toggleClass: function(element, selector, condition) {
3155 if (selector) {
3156 forEach(selector.split(' '), function(className) {
3157 var classCondition = condition;
3158 if (isUndefined(classCondition)) {
3159 classCondition = !jqLiteHasClass(element, className);
3160 }
3161 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3162 });
3163 }
3164 },
3165
3166 parent: function(element) {
3167 var parent = element.parentNode;
3168 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3169 },
3170
3171 next: function(element) {
3172 return element.nextElementSibling;
3173 },
3174
3175 find: function(element, selector) {
3176 if (element.getElementsByTagName) {
3177 return element.getElementsByTagName(selector);
3178 } else {
3179 return [];
3180 }
3181 },
3182
3183 clone: jqLiteClone,
3184
3185 triggerHandler: function(element, event, extraParameters) {
3186
3187 var dummyEvent, eventFnsCopy, handlerArgs;
3188 var eventName = event.type || event;
3189 var expandoStore = jqLiteExpandoStore(element);
3190 var events = expandoStore && expandoStore.events;
3191 var eventFns = events && events[eventName];
3192
3193 if (eventFns) {
3194 // Create a dummy event to pass to the handlers
3195 dummyEvent = {
3196 preventDefault: function() { this.defaultPrevented = true; },
3197 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3198 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3199 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3200 stopPropagation: noop,
3201 type: eventName,
3202 target: element
3203 };
3204
3205 // If a custom event was provided then extend our dummy event with it
3206 if (event.type) {
3207 dummyEvent = extend(dummyEvent, event);
3208 }
3209
3210 // Copy event handlers in case event handlers array is modified during execution.
3211 eventFnsCopy = shallowCopy(eventFns);
3212 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3213
3214 forEach(eventFnsCopy, function(fn) {
3215 if (!dummyEvent.isImmediatePropagationStopped()) {
3216 fn.apply(element, handlerArgs);
3217 }
3218 });
3219 }
3220 }
3221}, function(fn, name) {
3222 /**
3223 * chaining functions
3224 */
3225 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3226 var value;
3227
3228 for (var i = 0, ii = this.length; i < ii; i++) {
3229 if (isUndefined(value)) {
3230 value = fn(this[i], arg1, arg2, arg3);
3231 if (isDefined(value)) {
3232 // any function which returns a value needs to be wrapped
3233 value = jqLite(value);
3234 }
3235 } else {
3236 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3237 }
3238 }
3239 return isDefined(value) ? value : this;
3240 };
3241
3242 // bind legacy bind/unbind to on/off
3243 JQLite.prototype.bind = JQLite.prototype.on;
3244 JQLite.prototype.unbind = JQLite.prototype.off;
3245});
3246
3247/**
3248 * Computes a hash of an 'obj'.
3249 * Hash of a:
3250 * string is string
3251 * number is number as string
3252 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3253 * that is also assigned to the $$hashKey property of the object.
3254 *
3255 * @param obj
3256 * @returns {string} hash string such that the same input will have the same hash string.
3257 * The resulting string key is in 'type:hashKey' format.
3258 */
3259function hashKey(obj, nextUidFn) {
3260 var key = obj && obj.$$hashKey;
3261
3262 if (key) {
3263 if (typeof key === 'function') {
3264 key = obj.$$hashKey();
3265 }
3266 return key;
3267 }
3268
3269 var objType = typeof obj;
3270 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3271 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3272 } else {
3273 key = objType + ':' + obj;
3274 }
3275
3276 return key;
3277}
3278
3279/**
3280 * HashMap which can use objects as keys
3281 */
3282function HashMap(array, isolatedUid) {
3283 if (isolatedUid) {
3284 var uid = 0;
3285 this.nextUid = function() {
3286 return ++uid;
3287 };
3288 }
3289 forEach(array, this.put, this);
3290}
3291HashMap.prototype = {
3292 /**
3293 * Store key value pair
3294 * @param key key to store can be any type
3295 * @param value value to store can be any type
3296 */
3297 put: function(key, value) {
3298 this[hashKey(key, this.nextUid)] = value;
3299 },
3300
3301 /**
3302 * @param key
3303 * @returns {Object} the value for the key
3304 */
3305 get: function(key) {
3306 return this[hashKey(key, this.nextUid)];
3307 },
3308
3309 /**
3310 * Remove the key/value pair
3311 * @param key
3312 */
3313 remove: function(key) {
3314 var value = this[key = hashKey(key, this.nextUid)];
3315 delete this[key];
3316 return value;
3317 }
3318};
3319
3320/**
3321 * @ngdoc function
3322 * @module ng
3323 * @name angular.injector
3324 * @kind function
3325 *
3326 * @description
3327 * Creates an injector object that can be used for retrieving services as well as for
3328 * dependency injection (see {@link guide/di dependency injection}).
3329 *
3330 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3331 * {@link angular.module}. The `ng` module must be explicitly added.
3332 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3333 * disallows argument name annotation inference.
3334 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3335 *
3336 * @example
3337 * Typical usage
3338 * ```js
3339 * // create an injector
3340 * var $injector = angular.injector(['ng']);
3341 *
3342 * // use the injector to kick off your application
3343 * // use the type inference to auto inject arguments, or use implicit injection
3344 * $injector.invoke(function($rootScope, $compile, $document) {
3345 * $compile($document)($rootScope);
3346 * $rootScope.$digest();
3347 * });
3348 * ```
3349 *
3350 * Sometimes you want to get access to the injector of a currently running Angular app
3351 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3352 * application has been bootstrapped. You can do this using the extra `injector()` added
3353 * to JQuery/jqLite elements. See {@link angular.element}.
3354 *
3355 * *This is fairly rare but could be the case if a third party library is injecting the
3356 * markup.*
3357 *
3358 * In the following example a new block of HTML containing a `ng-controller`
3359 * directive is added to the end of the document body by JQuery. We then compile and link
3360 * it into the current AngularJS scope.
3361 *
3362 * ```js
3363 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3364 * $(document.body).append($div);
3365 *
3366 * angular.element(document).injector().invoke(function($compile) {
3367 * var scope = angular.element($div).scope();
3368 * $compile($div)(scope);
3369 * });
3370 * ```
3371 */
3372
3373
3374/**
3375 * @ngdoc module
3376 * @name auto
3377 * @description
3378 *
3379 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3380 */
3381
3382var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
3383var FN_ARG_SPLIT = /,/;
3384var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3385var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3386var $injectorMinErr = minErr('$injector');
3387
3388function anonFn(fn) {
3389 // For anonymous functions, showing at the very least the function signature can help in
3390 // debugging.
3391 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3392 args = fnText.match(FN_ARGS);
3393 if (args) {
3394 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3395 }
3396 return 'fn';
3397}
3398
3399function annotate(fn, strictDi, name) {
3400 var $inject,
3401 fnText,
3402 argDecl,
3403 last;
3404
3405 if (typeof fn === 'function') {
3406 if (!($inject = fn.$inject)) {
3407 $inject = [];
3408 if (fn.length) {
3409 if (strictDi) {
3410 if (!isString(name) || !name) {
3411 name = fn.name || anonFn(fn);
3412 }
3413 throw $injectorMinErr('strictdi',
3414 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3415 }
3416 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3417 argDecl = fnText.match(FN_ARGS);
3418 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3419 arg.replace(FN_ARG, function(all, underscore, name) {
3420 $inject.push(name);
3421 });
3422 });
3423 }
3424 fn.$inject = $inject;
3425 }
3426 } else if (isArray(fn)) {
3427 last = fn.length - 1;
3428 assertArgFn(fn[last], 'fn');
3429 $inject = fn.slice(0, last);
3430 } else {
3431 assertArgFn(fn, 'fn', true);
3432 }
3433 return $inject;
3434}
3435
3436///////////////////////////////////////
3437
3438/**
3439 * @ngdoc service
3440 * @name $injector
3441 *
3442 * @description
3443 *
3444 * `$injector` is used to retrieve object instances as defined by
3445 * {@link auto.$provide provider}, instantiate types, invoke methods,
3446 * and load modules.
3447 *
3448 * The following always holds true:
3449 *
3450 * ```js
3451 * var $injector = angular.injector();
3452 * expect($injector.get('$injector')).toBe($injector);
3453 * expect($injector.invoke(function($injector) {
3454 * return $injector;
3455 * })).toBe($injector);
3456 * ```
3457 *
3458 * # Injection Function Annotation
3459 *
3460 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3461 * following are all valid ways of annotating function with injection arguments and are equivalent.
3462 *
3463 * ```js
3464 * // inferred (only works if code not minified/obfuscated)
3465 * $injector.invoke(function(serviceA){});
3466 *
3467 * // annotated
3468 * function explicit(serviceA) {};
3469 * explicit.$inject = ['serviceA'];
3470 * $injector.invoke(explicit);
3471 *
3472 * // inline
3473 * $injector.invoke(['serviceA', function(serviceA){}]);
3474 * ```
3475 *
3476 * ## Inference
3477 *
3478 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3479 * can then be parsed and the function arguments can be extracted. This method of discovering
3480 * annotations is disallowed when the injector is in strict mode.
3481 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3482 * argument names.
3483 *
3484 * ## `$inject` Annotation
3485 * By adding an `$inject` property onto a function the injection parameters can be specified.
3486 *
3487 * ## Inline
3488 * As an array of injection names, where the last item in the array is the function to call.
3489 */
3490
3491/**
3492 * @ngdoc method
3493 * @name $injector#get
3494 *
3495 * @description
3496 * Return an instance of the service.
3497 *
3498 * @param {string} name The name of the instance to retrieve.
3499 * @return {*} The instance.
3500 */
3501
3502/**
3503 * @ngdoc method
3504 * @name $injector#invoke
3505 *
3506 * @description
3507 * Invoke the method and supply the method arguments from the `$injector`.
3508 *
3509 * @param {!Function} fn The function to invoke. Function parameters are injected according to the
3510 * {@link guide/di $inject Annotation} rules.
3511 * @param {Object=} self The `this` for the invoked method.
3512 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3513 * object first, before the `$injector` is consulted.
3514 * @returns {*} the value returned by the invoked `fn` function.
3515 */
3516
3517/**
3518 * @ngdoc method
3519 * @name $injector#has
3520 *
3521 * @description
3522 * Allows the user to query if the particular service exists.
3523 *
3524 * @param {string} name Name of the service to query.
3525 * @returns {boolean} `true` if injector has given service.
3526 */
3527
3528/**
3529 * @ngdoc method
3530 * @name $injector#instantiate
3531 * @description
3532 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3533 * operator, and supplies all of the arguments to the constructor function as specified by the
3534 * constructor annotation.
3535 *
3536 * @param {Function} Type Annotated constructor function.
3537 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3538 * object first, before the `$injector` is consulted.
3539 * @returns {Object} new instance of `Type`.
3540 */
3541
3542/**
3543 * @ngdoc method
3544 * @name $injector#annotate
3545 *
3546 * @description
3547 * Returns an array of service names which the function is requesting for injection. This API is
3548 * used by the injector to determine which services need to be injected into the function when the
3549 * function is invoked. There are three ways in which the function can be annotated with the needed
3550 * dependencies.
3551 *
3552 * # Argument names
3553 *
3554 * The simplest form is to extract the dependencies from the arguments of the function. This is done
3555 * by converting the function into a string using `toString()` method and extracting the argument
3556 * names.
3557 * ```js
3558 * // Given
3559 * function MyController($scope, $route) {
3560 * // ...
3561 * }
3562 *
3563 * // Then
3564 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3565 * ```
3566 *
3567 * You can disallow this method by using strict injection mode.
3568 *
3569 * This method does not work with code minification / obfuscation. For this reason the following
3570 * annotation strategies are supported.
3571 *
3572 * # The `$inject` property
3573 *
3574 * If a function has an `$inject` property and its value is an array of strings, then the strings
3575 * represent names of services to be injected into the function.
3576 * ```js
3577 * // Given
3578 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
3579 * // ...
3580 * }
3581 * // Define function dependencies
3582 * MyController['$inject'] = ['$scope', '$route'];
3583 *
3584 * // Then
3585 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
3586 * ```
3587 *
3588 * # The array notation
3589 *
3590 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
3591 * is very inconvenient. In these situations using the array notation to specify the dependencies in
3592 * a way that survives minification is a better choice:
3593 *
3594 * ```js
3595 * // We wish to write this (not minification / obfuscation safe)
3596 * injector.invoke(function($compile, $rootScope) {
3597 * // ...
3598 * });
3599 *
3600 * // We are forced to write break inlining
3601 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
3602 * // ...
3603 * };
3604 * tmpFn.$inject = ['$compile', '$rootScope'];
3605 * injector.invoke(tmpFn);
3606 *
3607 * // To better support inline function the inline annotation is supported
3608 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
3609 * // ...
3610 * }]);
3611 *
3612 * // Therefore
3613 * expect(injector.annotate(
3614 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
3615 * ).toEqual(['$compile', '$rootScope']);
3616 * ```
3617 *
3618 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
3619 * be retrieved as described above.
3620 *
3621 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
3622 *
3623 * @returns {Array.<string>} The names of the services which the function requires.
3624 */
3625
3626
3627
3628
3629/**
3630 * @ngdoc service
3631 * @name $provide
3632 *
3633 * @description
3634 *
3635 * The {@link auto.$provide $provide} service has a number of methods for registering components
3636 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
3637 * {@link angular.Module}.
3638 *
3639 * An Angular **service** is a singleton object created by a **service factory**. These **service
3640 * factories** are functions which, in turn, are created by a **service provider**.
3641 * The **service providers** are constructor functions. When instantiated they must contain a
3642 * property called `$get`, which holds the **service factory** function.
3643 *
3644 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
3645 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
3646 * function to get the instance of the **service**.
3647 *
3648 * Often services have no configuration options and there is no need to add methods to the service
3649 * provider. The provider will be no more than a constructor function with a `$get` property. For
3650 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
3651 * services without specifying a provider.
3652 *
3653 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
3654 * {@link auto.$injector $injector}
3655 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
3656 * providers and services.
3657 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
3658 * services, not providers.
3659 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
3660 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
3661 * given factory function.
3662 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
3663 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
3664 * a new object using the given constructor function.
3665 *
3666 * See the individual methods for more information and examples.
3667 */
3668
3669/**
3670 * @ngdoc method
3671 * @name $provide#provider
3672 * @description
3673 *
3674 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
3675 * are constructor functions, whose instances are responsible for "providing" a factory for a
3676 * service.
3677 *
3678 * Service provider names start with the name of the service they provide followed by `Provider`.
3679 * For example, the {@link ng.$log $log} service has a provider called
3680 * {@link ng.$logProvider $logProvider}.
3681 *
3682 * Service provider objects can have additional methods which allow configuration of the provider
3683 * and its service. Importantly, you can configure what kind of service is created by the `$get`
3684 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
3685 * method {@link ng.$logProvider#debugEnabled debugEnabled}
3686 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
3687 * console or not.
3688 *
3689 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
3690 'Provider'` key.
3691 * @param {(Object|function())} provider If the provider is:
3692 *
3693 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
3694 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
3695 * - `Constructor`: a new instance of the provider will be created using
3696 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
3697 *
3698 * @returns {Object} registered provider instance
3699
3700 * @example
3701 *
3702 * The following example shows how to create a simple event tracking service and register it using
3703 * {@link auto.$provide#provider $provide.provider()}.
3704 *
3705 * ```js
3706 * // Define the eventTracker provider
3707 * function EventTrackerProvider() {
3708 * var trackingUrl = '/track';
3709 *
3710 * // A provider method for configuring where the tracked events should been saved
3711 * this.setTrackingUrl = function(url) {
3712 * trackingUrl = url;
3713 * };
3714 *
3715 * // The service factory function
3716 * this.$get = ['$http', function($http) {
3717 * var trackedEvents = {};
3718 * return {
3719 * // Call this to track an event
3720 * event: function(event) {
3721 * var count = trackedEvents[event] || 0;
3722 * count += 1;
3723 * trackedEvents[event] = count;
3724 * return count;
3725 * },
3726 * // Call this to save the tracked events to the trackingUrl
3727 * save: function() {
3728 * $http.post(trackingUrl, trackedEvents);
3729 * }
3730 * };
3731 * }];
3732 * }
3733 *
3734 * describe('eventTracker', function() {
3735 * var postSpy;
3736 *
3737 * beforeEach(module(function($provide) {
3738 * // Register the eventTracker provider
3739 * $provide.provider('eventTracker', EventTrackerProvider);
3740 * }));
3741 *
3742 * beforeEach(module(function(eventTrackerProvider) {
3743 * // Configure eventTracker provider
3744 * eventTrackerProvider.setTrackingUrl('/custom-track');
3745 * }));
3746 *
3747 * it('tracks events', inject(function(eventTracker) {
3748 * expect(eventTracker.event('login')).toEqual(1);
3749 * expect(eventTracker.event('login')).toEqual(2);
3750 * }));
3751 *
3752 * it('saves to the tracking url', inject(function(eventTracker, $http) {
3753 * postSpy = spyOn($http, 'post');
3754 * eventTracker.event('login');
3755 * eventTracker.save();
3756 * expect(postSpy).toHaveBeenCalled();
3757 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
3758 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
3759 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
3760 * }));
3761 * });
3762 * ```
3763 */
3764
3765/**
3766 * @ngdoc method
3767 * @name $provide#factory
3768 * @description
3769 *
3770 * Register a **service factory**, which will be called to return the service instance.
3771 * This is short for registering a service where its provider consists of only a `$get` property,
3772 * which is the given service factory function.
3773 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
3774 * configure your service in a provider.
3775 *
3776 * @param {string} name The name of the instance.
3777 * @param {function()} $getFn The $getFn for the instance creation. Internally this is a short hand
3778 * for `$provide.provider(name, {$get: $getFn})`.
3779 * @returns {Object} registered provider instance
3780 *
3781 * @example
3782 * Here is an example of registering a service
3783 * ```js
3784 * $provide.factory('ping', ['$http', function($http) {
3785 * return function ping() {
3786 * return $http.send('/ping');
3787 * };
3788 * }]);
3789 * ```
3790 * You would then inject and use this service like this:
3791 * ```js
3792 * someModule.controller('Ctrl', ['ping', function(ping) {
3793 * ping();
3794 * }]);
3795 * ```
3796 */
3797
3798
3799/**
3800 * @ngdoc method
3801 * @name $provide#service
3802 * @description
3803 *
3804 * Register a **service constructor**, which will be invoked with `new` to create the service
3805 * instance.
3806 * This is short for registering a service where its provider's `$get` property is the service
3807 * constructor function that will be used to instantiate the service instance.
3808 *
3809 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
3810 * as a type/class.
3811 *
3812 * @param {string} name The name of the instance.
3813 * @param {Function} constructor A class (constructor function) that will be instantiated.
3814 * @returns {Object} registered provider instance
3815 *
3816 * @example
3817 * Here is an example of registering a service using
3818 * {@link auto.$provide#service $provide.service(class)}.
3819 * ```js
3820 * var Ping = function($http) {
3821 * this.$http = $http;
3822 * };
3823 *
3824 * Ping.$inject = ['$http'];
3825 *
3826 * Ping.prototype.send = function() {
3827 * return this.$http.get('/ping');
3828 * };
3829 * $provide.service('ping', Ping);
3830 * ```
3831 * You would then inject and use this service like this:
3832 * ```js
3833 * someModule.controller('Ctrl', ['ping', function(ping) {
3834 * ping.send();
3835 * }]);
3836 * ```
3837 */
3838
3839
3840/**
3841 * @ngdoc method
3842 * @name $provide#value
3843 * @description
3844 *
3845 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
3846 * number, an array, an object or a function. This is short for registering a service where its
3847 * provider's `$get` property is a factory function that takes no arguments and returns the **value
3848 * service**.
3849 *
3850 * Value services are similar to constant services, except that they cannot be injected into a
3851 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
3852 * an Angular
3853 * {@link auto.$provide#decorator decorator}.
3854 *
3855 * @param {string} name The name of the instance.
3856 * @param {*} value The value.
3857 * @returns {Object} registered provider instance
3858 *
3859 * @example
3860 * Here are some examples of creating value services.
3861 * ```js
3862 * $provide.value('ADMIN_USER', 'admin');
3863 *
3864 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
3865 *
3866 * $provide.value('halfOf', function(value) {
3867 * return value / 2;
3868 * });
3869 * ```
3870 */
3871
3872
3873/**
3874 * @ngdoc method
3875 * @name $provide#constant
3876 * @description
3877 *
3878 * Register a **constant service**, such as a string, a number, an array, an object or a function,
3879 * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
3880 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
3881 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
3882 *
3883 * @param {string} name The name of the constant.
3884 * @param {*} value The constant value.
3885 * @returns {Object} registered instance
3886 *
3887 * @example
3888 * Here a some examples of creating constants:
3889 * ```js
3890 * $provide.constant('SHARD_HEIGHT', 306);
3891 *
3892 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
3893 *
3894 * $provide.constant('double', function(value) {
3895 * return value * 2;
3896 * });
3897 * ```
3898 */
3899
3900
3901/**
3902 * @ngdoc method
3903 * @name $provide#decorator
3904 * @description
3905 *
3906 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
3907 * intercepts the creation of a service, allowing it to override or modify the behaviour of the
3908 * service. The object returned by the decorator may be the original service, or a new service
3909 * object which replaces or wraps and delegates to the original service.
3910 *
3911 * @param {string} name The name of the service to decorate.
3912 * @param {function()} decorator This function will be invoked when the service needs to be
3913 * instantiated and should return the decorated service instance. The function is called using
3914 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
3915 * Local injection arguments:
3916 *
3917 * * `$delegate` - The original service instance, which can be monkey patched, configured,
3918 * decorated or delegated to.
3919 *
3920 * @example
3921 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
3922 * calls to {@link ng.$log#error $log.warn()}.
3923 * ```js
3924 * $provide.decorator('$log', ['$delegate', function($delegate) {
3925 * $delegate.warn = $delegate.error;
3926 * return $delegate;
3927 * }]);
3928 * ```
3929 */
3930
3931
3932function createInjector(modulesToLoad, strictDi) {
3933 strictDi = (strictDi === true);
3934 var INSTANTIATING = {},
3935 providerSuffix = 'Provider',
3936 path = [],
3937 loadedModules = new HashMap([], true),
3938 providerCache = {
3939 $provide: {
3940 provider: supportObject(provider),
3941 factory: supportObject(factory),
3942 service: supportObject(service),
3943 value: supportObject(value),
3944 constant: supportObject(constant),
3945 decorator: decorator
3946 }
3947 },
3948 providerInjector = (providerCache.$injector =
3949 createInternalInjector(providerCache, function() {
3950 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
3951 })),
3952 instanceCache = {},
3953 instanceInjector = (instanceCache.$injector =
3954 createInternalInjector(instanceCache, function(servicename) {
3955 var provider = providerInjector.get(servicename + providerSuffix);
3956 return instanceInjector.invoke(provider.$get, provider, undefined, servicename);
3957 }));
3958
3959
3960 forEach(loadModules(modulesToLoad), function(fn) { instanceInjector.invoke(fn || noop); });
3961
3962 return instanceInjector;
3963
3964 ////////////////////////////////////
3965 // $provider
3966 ////////////////////////////////////
3967
3968 function supportObject(delegate) {
3969 return function(key, value) {
3970 if (isObject(key)) {
3971 forEach(key, reverseParams(delegate));
3972 } else {
3973 return delegate(key, value);
3974 }
3975 };
3976 }
3977
3978 function provider(name, provider_) {
3979 assertNotHasOwnProperty(name, 'service');
3980 if (isFunction(provider_) || isArray(provider_)) {
3981 provider_ = providerInjector.instantiate(provider_);
3982 }
3983 if (!provider_.$get) {
3984 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
3985 }
3986 return providerCache[name + providerSuffix] = provider_;
3987 }
3988
3989 function enforceReturnValue(name, factory) {
3990 return function enforcedReturnValue() {
3991 var result = instanceInjector.invoke(factory, this, undefined, name);
3992 if (isUndefined(result)) {
3993 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
3994 }
3995 return result;
3996 };
3997 }
3998
3999 function factory(name, factoryFn, enforce) {
4000 return provider(name, {
4001 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4002 });
4003 }
4004
4005 function service(name, constructor) {
4006 return factory(name, ['$injector', function($injector) {
4007 return $injector.instantiate(constructor);
4008 }]);
4009 }
4010
4011 function value(name, val) { return factory(name, valueFn(val), false); }
4012
4013 function constant(name, value) {
4014 assertNotHasOwnProperty(name, 'constant');
4015 providerCache[name] = value;
4016 instanceCache[name] = value;
4017 }
4018
4019 function decorator(serviceName, decorFn) {
4020 var origProvider = providerInjector.get(serviceName + providerSuffix),
4021 orig$get = origProvider.$get;
4022
4023 origProvider.$get = function() {
4024 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4025 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4026 };
4027 }
4028
4029 ////////////////////////////////////
4030 // Module Loading
4031 ////////////////////////////////////
4032 function loadModules(modulesToLoad) {
4033 var runBlocks = [], moduleFn;
4034 forEach(modulesToLoad, function(module) {
4035 if (loadedModules.get(module)) return;
4036 loadedModules.put(module, true);
4037
4038 function runInvokeQueue(queue) {
4039 var i, ii;
4040 for (i = 0, ii = queue.length; i < ii; i++) {
4041 var invokeArgs = queue[i],
4042 provider = providerInjector.get(invokeArgs[0]);
4043
4044 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4045 }
4046 }
4047
4048 try {
4049 if (isString(module)) {
4050 moduleFn = angularModule(module);
4051 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4052 runInvokeQueue(moduleFn._invokeQueue);
4053 runInvokeQueue(moduleFn._configBlocks);
4054 } else if (isFunction(module)) {
4055 runBlocks.push(providerInjector.invoke(module));
4056 } else if (isArray(module)) {
4057 runBlocks.push(providerInjector.invoke(module));
4058 } else {
4059 assertArgFn(module, 'module');
4060 }
4061 } catch (e) {
4062 if (isArray(module)) {
4063 module = module[module.length - 1];
4064 }
4065 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4066 // Safari & FF's stack traces don't contain error.message content
4067 // unlike those of Chrome and IE
4068 // So if stack doesn't contain message, we create a new string that contains both.
4069 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4070 /* jshint -W022 */
4071 e = e.message + '\n' + e.stack;
4072 }
4073 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4074 module, e.stack || e.message || e);
4075 }
4076 });
4077 return runBlocks;
4078 }
4079
4080 ////////////////////////////////////
4081 // internal Injector
4082 ////////////////////////////////////
4083
4084 function createInternalInjector(cache, factory) {
4085
4086 function getService(serviceName) {
4087 if (cache.hasOwnProperty(serviceName)) {
4088 if (cache[serviceName] === INSTANTIATING) {
4089 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4090 serviceName + ' <- ' + path.join(' <- '));
4091 }
4092 return cache[serviceName];
4093 } else {
4094 try {
4095 path.unshift(serviceName);
4096 cache[serviceName] = INSTANTIATING;
4097 return cache[serviceName] = factory(serviceName);
4098 } catch (err) {
4099 if (cache[serviceName] === INSTANTIATING) {
4100 delete cache[serviceName];
4101 }
4102 throw err;
4103 } finally {
4104 path.shift();
4105 }
4106 }
4107 }
4108
4109 function invoke(fn, self, locals, serviceName) {
4110 if (typeof locals === 'string') {
4111 serviceName = locals;
4112 locals = null;
4113 }
4114
4115 var args = [],
4116 $inject = annotate(fn, strictDi, serviceName),
4117 length, i,
4118 key;
4119
4120 for (i = 0, length = $inject.length; i < length; i++) {
4121 key = $inject[i];
4122 if (typeof key !== 'string') {
4123 throw $injectorMinErr('itkn',
4124 'Incorrect injection token! Expected service name as string, got {0}', key);
4125 }
4126 args.push(
4127 locals && locals.hasOwnProperty(key)
4128 ? locals[key]
4129 : getService(key)
4130 );
4131 }
4132 if (isArray(fn)) {
4133 fn = fn[length];
4134 }
4135
4136 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4137 // #5388
4138 return fn.apply(self, args);
4139 }
4140
4141 function instantiate(Type, locals, serviceName) {
4142 // Check if Type is annotated and use just the given function at n-1 as parameter
4143 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4144 // Object creation: http://jsperf.com/create-constructor/2
4145 var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype);
4146 var returnedValue = invoke(Type, instance, locals, serviceName);
4147
4148 return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4149 }
4150
4151 return {
4152 invoke: invoke,
4153 instantiate: instantiate,
4154 get: getService,
4155 annotate: annotate,
4156 has: function(name) {
4157 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4158 }
4159 };
4160 }
4161}
4162
4163createInjector.$$annotate = annotate;
4164
4165/**
4166 * @ngdoc provider
4167 * @name $anchorScrollProvider
4168 *
4169 * @description
4170 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4171 * {@link ng.$location#hash $location.hash()} changes.
4172 */
4173function $AnchorScrollProvider() {
4174
4175 var autoScrollingEnabled = true;
4176
4177 /**
4178 * @ngdoc method
4179 * @name $anchorScrollProvider#disableAutoScrolling
4180 *
4181 * @description
4182 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4183 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4184 * Use this method to disable automatic scrolling.
4185 *
4186 * If automatic scrolling is disabled, one must explicitly call
4187 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4188 * current hash.
4189 */
4190 this.disableAutoScrolling = function() {
4191 autoScrollingEnabled = false;
4192 };
4193
4194 /**
4195 * @ngdoc service
4196 * @name $anchorScroll
4197 * @kind function
4198 * @requires $window
4199 * @requires $location
4200 * @requires $rootScope
4201 *
4202 * @description
4203 * When called, it checks the current value of {@link ng.$location#hash $location.hash()} and
4204 * scrolls to the related element, according to the rules specified in the
4205 * [Html5 spec](http://dev.w3.org/html5/spec/Overview.html#the-indicated-part-of-the-document).
4206 *
4207 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4208 * match any anchor whenever it changes. This can be disabled by calling
4209 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4210 *
4211 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4212 * vertical scroll-offset (either fixed or dynamic).
4213 *
4214 * @property {(number|function|jqLite)} yOffset
4215 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4216 * positioned elements at the top of the page, such as navbars, headers etc.
4217 *
4218 * `yOffset` can be specified in various ways:
4219 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4220 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4221 * a number representing the offset (in pixels).<br /><br />
4222 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4223 * the top of the page to the element's bottom will be used as offset.<br />
4224 * **Note**: The element will be taken into account only as long as its `position` is set to
4225 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4226 * their height and/or positioning according to the viewport's size.
4227 *
4228 * <br />
4229 * <div class="alert alert-warning">
4230 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4231 * not some child element.
4232 * </div>
4233 *
4234 * @example
4235 <example module="anchorScrollExample">
4236 <file name="index.html">
4237 <div id="scrollArea" ng-controller="ScrollController">
4238 <a ng-click="gotoBottom()">Go to bottom</a>
4239 <a id="bottom"></a> You're at the bottom!
4240 </div>
4241 </file>
4242 <file name="script.js">
4243 angular.module('anchorScrollExample', [])
4244 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4245 function ($scope, $location, $anchorScroll) {
4246 $scope.gotoBottom = function() {
4247 // set the location.hash to the id of
4248 // the element you wish to scroll to.
4249 $location.hash('bottom');
4250
4251 // call $anchorScroll()
4252 $anchorScroll();
4253 };
4254 }]);
4255 </file>
4256 <file name="style.css">
4257 #scrollArea {
4258 height: 280px;
4259 overflow: auto;
4260 }
4261
4262 #bottom {
4263 display: block;
4264 margin-top: 2000px;
4265 }
4266 </file>
4267 </example>
4268 *
4269 * <hr />
4270 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4271 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4272 *
4273 * @example
4274 <example module="anchorScrollOffsetExample">
4275 <file name="index.html">
4276 <div class="fixed-header" ng-controller="headerCtrl">
4277 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4278 Go to anchor {{x}}
4279 </a>
4280 </div>
4281 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4282 Anchor {{x}} of 5
4283 </div>
4284 </file>
4285 <file name="script.js">
4286 angular.module('anchorScrollOffsetExample', [])
4287 .run(['$anchorScroll', function($anchorScroll) {
4288 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4289 }])
4290 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4291 function ($anchorScroll, $location, $scope) {
4292 $scope.gotoAnchor = function(x) {
4293 var newHash = 'anchor' + x;
4294 if ($location.hash() !== newHash) {
4295 // set the $location.hash to `newHash` and
4296 // $anchorScroll will automatically scroll to it
4297 $location.hash('anchor' + x);
4298 } else {
4299 // call $anchorScroll() explicitly,
4300 // since $location.hash hasn't changed
4301 $anchorScroll();
4302 }
4303 };
4304 }
4305 ]);
4306 </file>
4307 <file name="style.css">
4308 body {
4309 padding-top: 50px;
4310 }
4311
4312 .anchor {
4313 border: 2px dashed DarkOrchid;
4314 padding: 10px 10px 200px 10px;
4315 }
4316
4317 .fixed-header {
4318 background-color: rgba(0, 0, 0, 0.2);
4319 height: 50px;
4320 position: fixed;
4321 top: 0; left: 0; right: 0;
4322 }
4323
4324 .fixed-header > a {
4325 display: inline-block;
4326 margin: 5px 15px;
4327 }
4328 </file>
4329 </example>
4330 */
4331 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4332 var document = $window.document;
4333
4334 // Helper function to get first anchor from a NodeList
4335 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4336 // and working in all supported browsers.)
4337 function getFirstAnchor(list) {
4338 var result = null;
4339 Array.prototype.some.call(list, function(element) {
4340 if (nodeName_(element) === 'a') {
4341 result = element;
4342 return true;
4343 }
4344 });
4345 return result;
4346 }
4347
4348 function getYOffset() {
4349
4350 var offset = scroll.yOffset;
4351
4352 if (isFunction(offset)) {
4353 offset = offset();
4354 } else if (isElement(offset)) {
4355 var elem = offset[0];
4356 var style = $window.getComputedStyle(elem);
4357 if (style.position !== 'fixed') {
4358 offset = 0;
4359 } else {
4360 offset = elem.getBoundingClientRect().bottom;
4361 }
4362 } else if (!isNumber(offset)) {
4363 offset = 0;
4364 }
4365
4366 return offset;
4367 }
4368
4369 function scrollTo(elem) {
4370 if (elem) {
4371 elem.scrollIntoView();
4372
4373 var offset = getYOffset();
4374
4375 if (offset) {
4376 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4377 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4378 // top of the viewport.
4379 //
4380 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4381 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4382 // way down the page.
4383 //
4384 // This is often the case for elements near the bottom of the page.
4385 //
4386 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4387 // the top of the element and the offset, which is enough to align the top of `elem` at the
4388 // desired position.
4389 var elemTop = elem.getBoundingClientRect().top;
4390 $window.scrollBy(0, elemTop - offset);
4391 }
4392 } else {
4393 $window.scrollTo(0, 0);
4394 }
4395 }
4396
4397 function scroll() {
4398 var hash = $location.hash(), elm;
4399
4400 // empty hash, scroll to the top of the page
4401 if (!hash) scrollTo(null);
4402
4403 // element with given id
4404 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4405
4406 // first anchor with given name :-D
4407 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4408
4409 // no element and hash == 'top', scroll to the top of the page
4410 else if (hash === 'top') scrollTo(null);
4411 }
4412
4413 // does not scroll when user clicks on anchor link that is currently on
4414 // (no url change, no $location.hash() change), browser native does scroll
4415 if (autoScrollingEnabled) {
4416 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4417 function autoScrollWatchAction(newVal, oldVal) {
4418 // skip the initial scroll if $location.hash is empty
4419 if (newVal === oldVal && newVal === '') return;
4420
4421 jqLiteDocumentLoaded(function() {
4422 $rootScope.$evalAsync(scroll);
4423 });
4424 });
4425 }
4426
4427 return scroll;
4428 }];
4429}
4430
4431var $animateMinErr = minErr('$animate');
4432
4433/**
4434 * @ngdoc provider
4435 * @name $animateProvider
4436 *
4437 * @description
4438 * Default implementation of $animate that doesn't perform any animations, instead just
4439 * synchronously performs DOM
4440 * updates and calls done() callbacks.
4441 *
4442 * In order to enable animations the ngAnimate module has to be loaded.
4443 *
4444 * To see the functional implementation check out src/ngAnimate/animate.js
4445 */
4446var $AnimateProvider = ['$provide', function($provide) {
4447
4448
4449 this.$$selectors = {};
4450
4451
4452 /**
4453 * @ngdoc method
4454 * @name $animateProvider#register
4455 *
4456 * @description
4457 * Registers a new injectable animation factory function. The factory function produces the
4458 * animation object which contains callback functions for each event that is expected to be
4459 * animated.
4460 *
4461 * * `eventFn`: `function(Element, doneFunction)` The element to animate, the `doneFunction`
4462 * must be called once the element animation is complete. If a function is returned then the
4463 * animation service will use this function to cancel the animation whenever a cancel event is
4464 * triggered.
4465 *
4466 *
4467 * ```js
4468 * return {
4469 * eventFn : function(element, done) {
4470 * //code to run the animation
4471 * //once complete, then run done()
4472 * return function cancellationFunction() {
4473 * //code to cancel the animation
4474 * }
4475 * }
4476 * }
4477 * ```
4478 *
4479 * @param {string} name The name of the animation.
4480 * @param {Function} factory The factory function that will be executed to return the animation
4481 * object.
4482 */
4483 this.register = function(name, factory) {
4484 var key = name + '-animation';
4485 if (name && name.charAt(0) != '.') throw $animateMinErr('notcsel',
4486 "Expecting class selector starting with '.' got '{0}'.", name);
4487 this.$$selectors[name.substr(1)] = key;
4488 $provide.factory(key, factory);
4489 };
4490
4491 /**
4492 * @ngdoc method
4493 * @name $animateProvider#classNameFilter
4494 *
4495 * @description
4496 * Sets and/or returns the CSS class regular expression that is checked when performing
4497 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
4498 * therefore enable $animate to attempt to perform an animation on any element.
4499 * When setting the classNameFilter value, animations will only be performed on elements
4500 * that successfully match the filter expression. This in turn can boost performance
4501 * for low-powered devices as well as applications containing a lot of structural operations.
4502 * @param {RegExp=} expression The className expression which will be checked against all animations
4503 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
4504 */
4505 this.classNameFilter = function(expression) {
4506 if (arguments.length === 1) {
4507 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
4508 }
4509 return this.$$classNameFilter;
4510 };
4511
4512 this.$get = ['$$q', '$$asyncCallback', '$rootScope', function($$q, $$asyncCallback, $rootScope) {
4513
4514 var currentDefer;
4515
4516 function runAnimationPostDigest(fn) {
4517 var cancelFn, defer = $$q.defer();
4518 defer.promise.$$cancelFn = function ngAnimateMaybeCancel() {
4519 cancelFn && cancelFn();
4520 };
4521
4522 $rootScope.$$postDigest(function ngAnimatePostDigest() {
4523 cancelFn = fn(function ngAnimateNotifyComplete() {
4524 defer.resolve();
4525 });
4526 });
4527
4528 return defer.promise;
4529 }
4530
4531 function resolveElementClasses(element, classes) {
4532 var toAdd = [], toRemove = [];
4533
4534 var hasClasses = createMap();
4535 forEach((element.attr('class') || '').split(/\s+/), function(className) {
4536 hasClasses[className] = true;
4537 });
4538
4539 forEach(classes, function(status, className) {
4540 var hasClass = hasClasses[className];
4541
4542 // If the most recent class manipulation (via $animate) was to remove the class, and the
4543 // element currently has the class, the class is scheduled for removal. Otherwise, if
4544 // the most recent class manipulation (via $animate) was to add the class, and the
4545 // element does not currently have the class, the class is scheduled to be added.
4546 if (status === false && hasClass) {
4547 toRemove.push(className);
4548 } else if (status === true && !hasClass) {
4549 toAdd.push(className);
4550 }
4551 });
4552
4553 return (toAdd.length + toRemove.length) > 0 &&
4554 [toAdd.length ? toAdd : null, toRemove.length ? toRemove : null];
4555 }
4556
4557 function cachedClassManipulation(cache, classes, op) {
4558 for (var i=0, ii = classes.length; i < ii; ++i) {
4559 var className = classes[i];
4560 cache[className] = op;
4561 }
4562 }
4563
4564 function asyncPromise() {
4565 // only serve one instance of a promise in order to save CPU cycles
4566 if (!currentDefer) {
4567 currentDefer = $$q.defer();
4568 $$asyncCallback(function() {
4569 currentDefer.resolve();
4570 currentDefer = null;
4571 });
4572 }
4573 return currentDefer.promise;
4574 }
4575
4576 function applyStyles(element, options) {
4577 if (angular.isObject(options)) {
4578 var styles = extend(options.from || {}, options.to || {});
4579 element.css(styles);
4580 }
4581 }
4582
4583 /**
4584 *
4585 * @ngdoc service
4586 * @name $animate
4587 * @description The $animate service provides rudimentary DOM manipulation functions to
4588 * insert, remove and move elements within the DOM, as well as adding and removing classes.
4589 * This service is the core service used by the ngAnimate $animator service which provides
4590 * high-level animation hooks for CSS and JavaScript.
4591 *
4592 * $animate is available in the AngularJS core, however, the ngAnimate module must be included
4593 * to enable full out animation support. Otherwise, $animate will only perform simple DOM
4594 * manipulation operations.
4595 *
4596 * To learn more about enabling animation support, click here to visit the {@link ngAnimate
4597 * ngAnimate module page} as well as the {@link ngAnimate.$animate ngAnimate $animate service
4598 * page}.
4599 */
4600 return {
4601 animate: function(element, from, to) {
4602 applyStyles(element, { from: from, to: to });
4603 return asyncPromise();
4604 },
4605
4606 /**
4607 *
4608 * @ngdoc method
4609 * @name $animate#enter
4610 * @kind function
4611 * @description Inserts the element into the DOM either after the `after` element or
4612 * as the first child within the `parent` element. When the function is called a promise
4613 * is returned that will be resolved at a later time.
4614 * @param {DOMElement} element the element which will be inserted into the DOM
4615 * @param {DOMElement} parent the parent element which will append the element as
4616 * a child (if the after element is not present)
4617 * @param {DOMElement} after the sibling element which will append the element
4618 * after itself
4619 * @param {object=} options an optional collection of styles that will be applied to the element.
4620 * @return {Promise} the animation callback promise
4621 */
4622 enter: function(element, parent, after, options) {
4623 applyStyles(element, options);
4624 after ? after.after(element)
4625 : parent.prepend(element);
4626 return asyncPromise();
4627 },
4628
4629 /**
4630 *
4631 * @ngdoc method
4632 * @name $animate#leave
4633 * @kind function
4634 * @description Removes the element from the DOM. When the function is called a promise
4635 * is returned that will be resolved at a later time.
4636 * @param {DOMElement} element the element which will be removed from the DOM
4637 * @param {object=} options an optional collection of options that will be applied to the element.
4638 * @return {Promise} the animation callback promise
4639 */
4640 leave: function(element, options) {
4641 element.remove();
4642 return asyncPromise();
4643 },
4644
4645 /**
4646 *
4647 * @ngdoc method
4648 * @name $animate#move
4649 * @kind function
4650 * @description Moves the position of the provided element within the DOM to be placed
4651 * either after the `after` element or inside of the `parent` element. When the function
4652 * is called a promise is returned that will be resolved at a later time.
4653 *
4654 * @param {DOMElement} element the element which will be moved around within the
4655 * DOM
4656 * @param {DOMElement} parent the parent element where the element will be
4657 * inserted into (if the after element is not present)
4658 * @param {DOMElement} after the sibling element where the element will be
4659 * positioned next to
4660 * @param {object=} options an optional collection of options that will be applied to the element.
4661 * @return {Promise} the animation callback promise
4662 */
4663 move: function(element, parent, after, options) {
4664 // Do not remove element before insert. Removing will cause data associated with the
4665 // element to be dropped. Insert will implicitly do the remove.
4666 return this.enter(element, parent, after, options);
4667 },
4668
4669 /**
4670 *
4671 * @ngdoc method
4672 * @name $animate#addClass
4673 * @kind function
4674 * @description Adds the provided className CSS class value to the provided element.
4675 * When the function is called a promise is returned that will be resolved at a later time.
4676 * @param {DOMElement} element the element which will have the className value
4677 * added to it
4678 * @param {string} className the CSS class which will be added to the element
4679 * @param {object=} options an optional collection of options that will be applied to the element.
4680 * @return {Promise} the animation callback promise
4681 */
4682 addClass: function(element, className, options) {
4683 return this.setClass(element, className, [], options);
4684 },
4685
4686 $$addClassImmediately: function(element, className, options) {
4687 element = jqLite(element);
4688 className = !isString(className)
4689 ? (isArray(className) ? className.join(' ') : '')
4690 : className;
4691 forEach(element, function(element) {
4692 jqLiteAddClass(element, className);
4693 });
4694 applyStyles(element, options);
4695 return asyncPromise();
4696 },
4697
4698 /**
4699 *
4700 * @ngdoc method
4701 * @name $animate#removeClass
4702 * @kind function
4703 * @description Removes the provided className CSS class value from the provided element.
4704 * When the function is called a promise is returned that will be resolved at a later time.
4705 * @param {DOMElement} element the element which will have the className value
4706 * removed from it
4707 * @param {string} className the CSS class which will be removed from the element
4708 * @param {object=} options an optional collection of options that will be applied to the element.
4709 * @return {Promise} the animation callback promise
4710 */
4711 removeClass: function(element, className, options) {
4712 return this.setClass(element, [], className, options);
4713 },
4714
4715 $$removeClassImmediately: function(element, className, options) {
4716 element = jqLite(element);
4717 className = !isString(className)
4718 ? (isArray(className) ? className.join(' ') : '')
4719 : className;
4720 forEach(element, function(element) {
4721 jqLiteRemoveClass(element, className);
4722 });
4723 applyStyles(element, options);
4724 return asyncPromise();
4725 },
4726
4727 /**
4728 *
4729 * @ngdoc method
4730 * @name $animate#setClass
4731 * @kind function
4732 * @description Adds and/or removes the given CSS classes to and from the element.
4733 * When the function is called a promise is returned that will be resolved at a later time.
4734 * @param {DOMElement} element the element which will have its CSS classes changed
4735 * removed from it
4736 * @param {string} add the CSS classes which will be added to the element
4737 * @param {string} remove the CSS class which will be removed from the element
4738 * @param {object=} options an optional collection of options that will be applied to the element.
4739 * @return {Promise} the animation callback promise
4740 */
4741 setClass: function(element, add, remove, options) {
4742 var self = this;
4743 var STORAGE_KEY = '$$animateClasses';
4744 var createdCache = false;
4745 element = jqLite(element);
4746
4747 var cache = element.data(STORAGE_KEY);
4748 if (!cache) {
4749 cache = {
4750 classes: {},
4751 options: options
4752 };
4753 createdCache = true;
4754 } else if (options && cache.options) {
4755 cache.options = angular.extend(cache.options || {}, options);
4756 }
4757
4758 var classes = cache.classes;
4759
4760 add = isArray(add) ? add : add.split(' ');
4761 remove = isArray(remove) ? remove : remove.split(' ');
4762 cachedClassManipulation(classes, add, true);
4763 cachedClassManipulation(classes, remove, false);
4764
4765 if (createdCache) {
4766 cache.promise = runAnimationPostDigest(function(done) {
4767 var cache = element.data(STORAGE_KEY);
4768 element.removeData(STORAGE_KEY);
4769
4770 // in the event that the element is removed before postDigest
4771 // is run then the cache will be undefined and there will be
4772 // no need anymore to add or remove and of the element classes
4773 if (cache) {
4774 var classes = resolveElementClasses(element, cache.classes);
4775 if (classes) {
4776 self.$$setClassImmediately(element, classes[0], classes[1], cache.options);
4777 }
4778 }
4779
4780 done();
4781 });
4782 element.data(STORAGE_KEY, cache);
4783 }
4784
4785 return cache.promise;
4786 },
4787
4788 $$setClassImmediately: function(element, add, remove, options) {
4789 add && this.$$addClassImmediately(element, add);
4790 remove && this.$$removeClassImmediately(element, remove);
4791 applyStyles(element, options);
4792 return asyncPromise();
4793 },
4794
4795 enabled: noop,
4796 cancel: noop
4797 };
4798 }];
4799}];
4800
4801function $$AsyncCallbackProvider() {
4802 this.$get = ['$$rAF', '$timeout', function($$rAF, $timeout) {
4803 return $$rAF.supported
4804 ? function(fn) { return $$rAF(fn); }
4805 : function(fn) {
4806 return $timeout(fn, 0, false);
4807 };
4808 }];
4809}
4810
4811/* global stripHash: true */
4812
4813/**
4814 * ! This is a private undocumented service !
4815 *
4816 * @name $browser
4817 * @requires $log
4818 * @description
4819 * This object has two goals:
4820 *
4821 * - hide all the global state in the browser caused by the window object
4822 * - abstract away all the browser specific features and inconsistencies
4823 *
4824 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
4825 * service, which can be used for convenient testing of the application without the interaction with
4826 * the real browser apis.
4827 */
4828/**
4829 * @param {object} window The global window object.
4830 * @param {object} document jQuery wrapped document.
4831 * @param {object} $log window.console or an object with the same interface.
4832 * @param {object} $sniffer $sniffer service
4833 */
4834function Browser(window, document, $log, $sniffer) {
4835 var self = this,
4836 rawDocument = document[0],
4837 location = window.location,
4838 history = window.history,
4839 setTimeout = window.setTimeout,
4840 clearTimeout = window.clearTimeout,
4841 pendingDeferIds = {};
4842
4843 self.isMock = false;
4844
4845 var outstandingRequestCount = 0;
4846 var outstandingRequestCallbacks = [];
4847
4848 // TODO(vojta): remove this temporary api
4849 self.$$completeOutstandingRequest = completeOutstandingRequest;
4850 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
4851
4852 /**
4853 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
4854 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
4855 */
4856 function completeOutstandingRequest(fn) {
4857 try {
4858 fn.apply(null, sliceArgs(arguments, 1));
4859 } finally {
4860 outstandingRequestCount--;
4861 if (outstandingRequestCount === 0) {
4862 while (outstandingRequestCallbacks.length) {
4863 try {
4864 outstandingRequestCallbacks.pop()();
4865 } catch (e) {
4866 $log.error(e);
4867 }
4868 }
4869 }
4870 }
4871 }
4872
4873 /**
4874 * @private
4875 * Note: this method is used only by scenario runner
4876 * TODO(vojta): prefix this method with $$ ?
4877 * @param {function()} callback Function that will be called when no outstanding request
4878 */
4879 self.notifyWhenNoOutstandingRequests = function(callback) {
4880 // force browser to execute all pollFns - this is needed so that cookies and other pollers fire
4881 // at some deterministic time in respect to the test runner's actions. Leaving things up to the
4882 // regular poller would result in flaky tests.
4883 forEach(pollFns, function(pollFn) { pollFn(); });
4884
4885 if (outstandingRequestCount === 0) {
4886 callback();
4887 } else {
4888 outstandingRequestCallbacks.push(callback);
4889 }
4890 };
4891
4892 //////////////////////////////////////////////////////////////
4893 // Poll Watcher API
4894 //////////////////////////////////////////////////////////////
4895 var pollFns = [],
4896 pollTimeout;
4897
4898 /**
4899 * @name $browser#addPollFn
4900 *
4901 * @param {function()} fn Poll function to add
4902 *
4903 * @description
4904 * Adds a function to the list of functions that poller periodically executes,
4905 * and starts polling if not started yet.
4906 *
4907 * @returns {function()} the added function
4908 */
4909 self.addPollFn = function(fn) {
4910 if (isUndefined(pollTimeout)) startPoller(100, setTimeout);
4911 pollFns.push(fn);
4912 return fn;
4913 };
4914
4915 /**
4916 * @param {number} interval How often should browser call poll functions (ms)
4917 * @param {function()} setTimeout Reference to a real or fake `setTimeout` function.
4918 *
4919 * @description
4920 * Configures the poller to run in the specified intervals, using the specified
4921 * setTimeout fn and kicks it off.
4922 */
4923 function startPoller(interval, setTimeout) {
4924 (function check() {
4925 forEach(pollFns, function(pollFn) { pollFn(); });
4926 pollTimeout = setTimeout(check, interval);
4927 })();
4928 }
4929
4930 //////////////////////////////////////////////////////////////
4931 // URL API
4932 //////////////////////////////////////////////////////////////
4933
4934 var cachedState, lastHistoryState,
4935 lastBrowserUrl = location.href,
4936 baseElement = document.find('base'),
4937 reloadLocation = null;
4938
4939 cacheState();
4940 lastHistoryState = cachedState;
4941
4942 /**
4943 * @name $browser#url
4944 *
4945 * @description
4946 * GETTER:
4947 * Without any argument, this method just returns current value of location.href.
4948 *
4949 * SETTER:
4950 * With at least one argument, this method sets url to new value.
4951 * If html5 history api supported, pushState/replaceState is used, otherwise
4952 * location.href/location.replace is used.
4953 * Returns its own instance to allow chaining
4954 *
4955 * NOTE: this api is intended for use only by the $location service. Please use the
4956 * {@link ng.$location $location service} to change url.
4957 *
4958 * @param {string} url New url (when used as setter)
4959 * @param {boolean=} replace Should new url replace current history record?
4960 * @param {object=} state object to use with pushState/replaceState
4961 */
4962 self.url = function(url, replace, state) {
4963 // In modern browsers `history.state` is `null` by default; treating it separately
4964 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
4965 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
4966 if (isUndefined(state)) {
4967 state = null;
4968 }
4969
4970 // Android Browser BFCache causes location, history reference to become stale.
4971 if (location !== window.location) location = window.location;
4972 if (history !== window.history) history = window.history;
4973
4974 // setter
4975 if (url) {
4976 var sameState = lastHistoryState === state;
4977
4978 // Don't change anything if previous and current URLs and states match. This also prevents
4979 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
4980 // See https://github.com/angular/angular.js/commit/ffb2701
4981 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
4982 return self;
4983 }
4984 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
4985 lastBrowserUrl = url;
4986 lastHistoryState = state;
4987 // Don't use history API if only the hash changed
4988 // due to a bug in IE10/IE11 which leads
4989 // to not firing a `hashchange` nor `popstate` event
4990 // in some cases (see #9143).
4991 if ($sniffer.history && (!sameBase || !sameState)) {
4992 history[replace ? 'replaceState' : 'pushState'](state, '', url);
4993 cacheState();
4994 // Do the assignment again so that those two variables are referentially identical.
4995 lastHistoryState = cachedState;
4996 } else {
4997 if (!sameBase) {
4998 reloadLocation = url;
4999 }
5000 if (replace) {
5001 location.replace(url);
5002 } else {
5003 location.href = url;
5004 }
5005 }
5006 return self;
5007 // getter
5008 } else {
5009 // - reloadLocation is needed as browsers don't allow to read out
5010 // the new location.href if a reload happened.
5011 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5012 return reloadLocation || location.href.replace(/%27/g,"'");
5013 }
5014 };
5015
5016 /**
5017 * @name $browser#state
5018 *
5019 * @description
5020 * This method is a getter.
5021 *
5022 * Return history.state or null if history.state is undefined.
5023 *
5024 * @returns {object} state
5025 */
5026 self.state = function() {
5027 return cachedState;
5028 };
5029
5030 var urlChangeListeners = [],
5031 urlChangeInit = false;
5032
5033 function cacheStateAndFireUrlChange() {
5034 cacheState();
5035 fireUrlChange();
5036 }
5037
5038 // This variable should be used *only* inside the cacheState function.
5039 var lastCachedState = null;
5040 function cacheState() {
5041 // This should be the only place in $browser where `history.state` is read.
5042 cachedState = window.history.state;
5043 cachedState = isUndefined(cachedState) ? null : cachedState;
5044
5045 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5046 if (equals(cachedState, lastCachedState)) {
5047 cachedState = lastCachedState;
5048 }
5049 lastCachedState = cachedState;
5050 }
5051
5052 function fireUrlChange() {
5053 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5054 return;
5055 }
5056
5057 lastBrowserUrl = self.url();
5058 lastHistoryState = cachedState;
5059 forEach(urlChangeListeners, function(listener) {
5060 listener(self.url(), cachedState);
5061 });
5062 }
5063
5064 /**
5065 * @name $browser#onUrlChange
5066 *
5067 * @description
5068 * Register callback function that will be called, when url changes.
5069 *
5070 * It's only called when the url is changed from outside of angular:
5071 * - user types different url into address bar
5072 * - user clicks on history (forward/back) button
5073 * - user clicks on a link
5074 *
5075 * It's not called when url is changed by $browser.url() method
5076 *
5077 * The listener gets called with new url as parameter.
5078 *
5079 * NOTE: this api is intended for use only by the $location service. Please use the
5080 * {@link ng.$location $location service} to monitor url changes in angular apps.
5081 *
5082 * @param {function(string)} listener Listener function to be called when url changes.
5083 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5084 */
5085 self.onUrlChange = function(callback) {
5086 // TODO(vojta): refactor to use node's syntax for events
5087 if (!urlChangeInit) {
5088 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5089 // don't fire popstate when user change the address bar and don't fire hashchange when url
5090 // changed by push/replaceState
5091
5092 // html5 history api - popstate event
5093 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5094 // hashchange event
5095 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5096
5097 urlChangeInit = true;
5098 }
5099
5100 urlChangeListeners.push(callback);
5101 return callback;
5102 };
5103
5104 /**
5105 * Checks whether the url has changed outside of Angular.
5106 * Needs to be exported to be able to check for changes that have been done in sync,
5107 * as hashchange/popstate events fire in async.
5108 */
5109 self.$$checkUrlChange = fireUrlChange;
5110
5111 //////////////////////////////////////////////////////////////
5112 // Misc API
5113 //////////////////////////////////////////////////////////////
5114
5115 /**
5116 * @name $browser#baseHref
5117 *
5118 * @description
5119 * Returns current <base href>
5120 * (always relative - without domain)
5121 *
5122 * @returns {string} The current base href
5123 */
5124 self.baseHref = function() {
5125 var href = baseElement.attr('href');
5126 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5127 };
5128
5129 //////////////////////////////////////////////////////////////
5130 // Cookies API
5131 //////////////////////////////////////////////////////////////
5132 var lastCookies = {};
5133 var lastCookieString = '';
5134 var cookiePath = self.baseHref();
5135
5136 function safeDecodeURIComponent(str) {
5137 try {
5138 return decodeURIComponent(str);
5139 } catch (e) {
5140 return str;
5141 }
5142 }
5143
5144 /**
5145 * @name $browser#cookies
5146 *
5147 * @param {string=} name Cookie name
5148 * @param {string=} value Cookie value
5149 *
5150 * @description
5151 * The cookies method provides a 'private' low level access to browser cookies.
5152 * It is not meant to be used directly, use the $cookie service instead.
5153 *
5154 * The return values vary depending on the arguments that the method was called with as follows:
5155 *
5156 * - cookies() -> hash of all cookies, this is NOT a copy of the internal state, so do not modify
5157 * it
5158 * - cookies(name, value) -> set name to value, if value is undefined delete the cookie
5159 * - cookies(name) -> the same as (name, undefined) == DELETES (no one calls it right now that
5160 * way)
5161 *
5162 * @returns {Object} Hash of all cookies (if called without any parameter)
5163 */
5164 self.cookies = function(name, value) {
5165 var cookieLength, cookieArray, cookie, i, index;
5166
5167 if (name) {
5168 if (value === undefined) {
5169 rawDocument.cookie = encodeURIComponent(name) + "=;path=" + cookiePath +
5170 ";expires=Thu, 01 Jan 1970 00:00:00 GMT";
5171 } else {
5172 if (isString(value)) {
5173 cookieLength = (rawDocument.cookie = encodeURIComponent(name) + '=' + encodeURIComponent(value) +
5174 ';path=' + cookiePath).length + 1;
5175
5176 // per http://www.ietf.org/rfc/rfc2109.txt browser must allow at minimum:
5177 // - 300 cookies
5178 // - 20 cookies per unique domain
5179 // - 4096 bytes per cookie
5180 if (cookieLength > 4096) {
5181 $log.warn("Cookie '" + name +
5182 "' possibly not set or overflowed because it was too large (" +
5183 cookieLength + " > 4096 bytes)!");
5184 }
5185 }
5186 }
5187 } else {
5188 if (rawDocument.cookie !== lastCookieString) {
5189 lastCookieString = rawDocument.cookie;
5190 cookieArray = lastCookieString.split("; ");
5191 lastCookies = {};
5192
5193 for (i = 0; i < cookieArray.length; i++) {
5194 cookie = cookieArray[i];
5195 index = cookie.indexOf('=');
5196 if (index > 0) { //ignore nameless cookies
5197 name = safeDecodeURIComponent(cookie.substring(0, index));
5198 // the first value that is seen for a cookie is the most
5199 // specific one. values for the same cookie name that
5200 // follow are for less specific paths.
5201 if (lastCookies[name] === undefined) {
5202 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
5203 }
5204 }
5205 }
5206 }
5207 return lastCookies;
5208 }
5209 };
5210
5211
5212 /**
5213 * @name $browser#defer
5214 * @param {function()} fn A function, who's execution should be deferred.
5215 * @param {number=} [delay=0] of milliseconds to defer the function execution.
5216 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5217 *
5218 * @description
5219 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5220 *
5221 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5222 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5223 * via `$browser.defer.flush()`.
5224 *
5225 */
5226 self.defer = function(fn, delay) {
5227 var timeoutId;
5228 outstandingRequestCount++;
5229 timeoutId = setTimeout(function() {
5230 delete pendingDeferIds[timeoutId];
5231 completeOutstandingRequest(fn);
5232 }, delay || 0);
5233 pendingDeferIds[timeoutId] = true;
5234 return timeoutId;
5235 };
5236
5237
5238 /**
5239 * @name $browser#defer.cancel
5240 *
5241 * @description
5242 * Cancels a deferred task identified with `deferId`.
5243 *
5244 * @param {*} deferId Token returned by the `$browser.defer` function.
5245 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5246 * canceled.
5247 */
5248 self.defer.cancel = function(deferId) {
5249 if (pendingDeferIds[deferId]) {
5250 delete pendingDeferIds[deferId];
5251 clearTimeout(deferId);
5252 completeOutstandingRequest(noop);
5253 return true;
5254 }
5255 return false;
5256 };
5257
5258}
5259
5260function $BrowserProvider() {
5261 this.$get = ['$window', '$log', '$sniffer', '$document',
5262 function($window, $log, $sniffer, $document) {
5263 return new Browser($window, $document, $log, $sniffer);
5264 }];
5265}
5266
5267/**
5268 * @ngdoc service
5269 * @name $cacheFactory
5270 *
5271 * @description
5272 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5273 * them.
5274 *
5275 * ```js
5276 *
5277 * var cache = $cacheFactory('cacheId');
5278 * expect($cacheFactory.get('cacheId')).toBe(cache);
5279 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5280 *
5281 * cache.put("key", "value");
5282 * cache.put("another key", "another value");
5283 *
5284 * // We've specified no options on creation
5285 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5286 *
5287 * ```
5288 *
5289 *
5290 * @param {string} cacheId Name or id of the newly created cache.
5291 * @param {object=} options Options object that specifies the cache behavior. Properties:
5292 *
5293 * - `{number=}` `capacity` — turns the cache into LRU cache.
5294 *
5295 * @returns {object} Newly created cache object with the following set of methods:
5296 *
5297 * - `{object}` `info()` — Returns id, size, and options of cache.
5298 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5299 * it.
5300 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5301 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5302 * - `{void}` `removeAll()` — Removes all cached values.
5303 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5304 *
5305 * @example
5306 <example module="cacheExampleApp">
5307 <file name="index.html">
5308 <div ng-controller="CacheController">
5309 <input ng-model="newCacheKey" placeholder="Key">
5310 <input ng-model="newCacheValue" placeholder="Value">
5311 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5312
5313 <p ng-if="keys.length">Cached Values</p>
5314 <div ng-repeat="key in keys">
5315 <span ng-bind="key"></span>
5316 <span>: </span>
5317 <b ng-bind="cache.get(key)"></b>
5318 </div>
5319
5320 <p>Cache Info</p>
5321 <div ng-repeat="(key, value) in cache.info()">
5322 <span ng-bind="key"></span>
5323 <span>: </span>
5324 <b ng-bind="value"></b>
5325 </div>
5326 </div>
5327 </file>
5328 <file name="script.js">
5329 angular.module('cacheExampleApp', []).
5330 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
5331 $scope.keys = [];
5332 $scope.cache = $cacheFactory('cacheId');
5333 $scope.put = function(key, value) {
5334 if ($scope.cache.get(key) === undefined) {
5335 $scope.keys.push(key);
5336 }
5337 $scope.cache.put(key, value === undefined ? null : value);
5338 };
5339 }]);
5340 </file>
5341 <file name="style.css">
5342 p {
5343 margin: 10px 0 3px;
5344 }
5345 </file>
5346 </example>
5347 */
5348function $CacheFactoryProvider() {
5349
5350 this.$get = function() {
5351 var caches = {};
5352
5353 function cacheFactory(cacheId, options) {
5354 if (cacheId in caches) {
5355 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
5356 }
5357
5358 var size = 0,
5359 stats = extend({}, options, {id: cacheId}),
5360 data = {},
5361 capacity = (options && options.capacity) || Number.MAX_VALUE,
5362 lruHash = {},
5363 freshEnd = null,
5364 staleEnd = null;
5365
5366 /**
5367 * @ngdoc type
5368 * @name $cacheFactory.Cache
5369 *
5370 * @description
5371 * A cache object used to store and retrieve data, primarily used by
5372 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
5373 * templates and other data.
5374 *
5375 * ```js
5376 * angular.module('superCache')
5377 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
5378 * return $cacheFactory('super-cache');
5379 * }]);
5380 * ```
5381 *
5382 * Example test:
5383 *
5384 * ```js
5385 * it('should behave like a cache', inject(function(superCache) {
5386 * superCache.put('key', 'value');
5387 * superCache.put('another key', 'another value');
5388 *
5389 * expect(superCache.info()).toEqual({
5390 * id: 'super-cache',
5391 * size: 2
5392 * });
5393 *
5394 * superCache.remove('another key');
5395 * expect(superCache.get('another key')).toBeUndefined();
5396 *
5397 * superCache.removeAll();
5398 * expect(superCache.info()).toEqual({
5399 * id: 'super-cache',
5400 * size: 0
5401 * });
5402 * }));
5403 * ```
5404 */
5405 return caches[cacheId] = {
5406
5407 /**
5408 * @ngdoc method
5409 * @name $cacheFactory.Cache#put
5410 * @kind function
5411 *
5412 * @description
5413 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
5414 * retrieved later, and incrementing the size of the cache if the key was not already
5415 * present in the cache. If behaving like an LRU cache, it will also remove stale
5416 * entries from the set.
5417 *
5418 * It will not insert undefined values into the cache.
5419 *
5420 * @param {string} key the key under which the cached data is stored.
5421 * @param {*} value the value to store alongside the key. If it is undefined, the key
5422 * will not be stored.
5423 * @returns {*} the value stored.
5424 */
5425 put: function(key, value) {
5426 if (capacity < Number.MAX_VALUE) {
5427 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
5428
5429 refresh(lruEntry);
5430 }
5431
5432 if (isUndefined(value)) return;
5433 if (!(key in data)) size++;
5434 data[key] = value;
5435
5436 if (size > capacity) {
5437 this.remove(staleEnd.key);
5438 }
5439
5440 return value;
5441 },
5442
5443 /**
5444 * @ngdoc method
5445 * @name $cacheFactory.Cache#get
5446 * @kind function
5447 *
5448 * @description
5449 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
5450 *
5451 * @param {string} key the key of the data to be retrieved
5452 * @returns {*} the value stored.
5453 */
5454 get: function(key) {
5455 if (capacity < Number.MAX_VALUE) {
5456 var lruEntry = lruHash[key];
5457
5458 if (!lruEntry) return;
5459
5460 refresh(lruEntry);
5461 }
5462
5463 return data[key];
5464 },
5465
5466
5467 /**
5468 * @ngdoc method
5469 * @name $cacheFactory.Cache#remove
5470 * @kind function
5471 *
5472 * @description
5473 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
5474 *
5475 * @param {string} key the key of the entry to be removed
5476 */
5477 remove: function(key) {
5478 if (capacity < Number.MAX_VALUE) {
5479 var lruEntry = lruHash[key];
5480
5481 if (!lruEntry) return;
5482
5483 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
5484 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
5485 link(lruEntry.n,lruEntry.p);
5486
5487 delete lruHash[key];
5488 }
5489
5490 delete data[key];
5491 size--;
5492 },
5493
5494
5495 /**
5496 * @ngdoc method
5497 * @name $cacheFactory.Cache#removeAll
5498 * @kind function
5499 *
5500 * @description
5501 * Clears the cache object of any entries.
5502 */
5503 removeAll: function() {
5504 data = {};
5505 size = 0;
5506 lruHash = {};
5507 freshEnd = staleEnd = null;
5508 },
5509
5510
5511 /**
5512 * @ngdoc method
5513 * @name $cacheFactory.Cache#destroy
5514 * @kind function
5515 *
5516 * @description
5517 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
5518 * removing it from the {@link $cacheFactory $cacheFactory} set.
5519 */
5520 destroy: function() {
5521 data = null;
5522 stats = null;
5523 lruHash = null;
5524 delete caches[cacheId];
5525 },
5526
5527
5528 /**
5529 * @ngdoc method
5530 * @name $cacheFactory.Cache#info
5531 * @kind function
5532 *
5533 * @description
5534 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
5535 *
5536 * @returns {object} an object with the following properties:
5537 * <ul>
5538 * <li>**id**: the id of the cache instance</li>
5539 * <li>**size**: the number of entries kept in the cache instance</li>
5540 * <li>**...**: any additional properties from the options object when creating the
5541 * cache.</li>
5542 * </ul>
5543 */
5544 info: function() {
5545 return extend({}, stats, {size: size});
5546 }
5547 };
5548
5549
5550 /**
5551 * makes the `entry` the freshEnd of the LRU linked list
5552 */
5553 function refresh(entry) {
5554 if (entry != freshEnd) {
5555 if (!staleEnd) {
5556 staleEnd = entry;
5557 } else if (staleEnd == entry) {
5558 staleEnd = entry.n;
5559 }
5560
5561 link(entry.n, entry.p);
5562 link(entry, freshEnd);
5563 freshEnd = entry;
5564 freshEnd.n = null;
5565 }
5566 }
5567
5568
5569 /**
5570 * bidirectionally links two entries of the LRU linked list
5571 */
5572 function link(nextEntry, prevEntry) {
5573 if (nextEntry != prevEntry) {
5574 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
5575 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
5576 }
5577 }
5578 }
5579
5580
5581 /**
5582 * @ngdoc method
5583 * @name $cacheFactory#info
5584 *
5585 * @description
5586 * Get information about all the caches that have been created
5587 *
5588 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
5589 */
5590 cacheFactory.info = function() {
5591 var info = {};
5592 forEach(caches, function(cache, cacheId) {
5593 info[cacheId] = cache.info();
5594 });
5595 return info;
5596 };
5597
5598
5599 /**
5600 * @ngdoc method
5601 * @name $cacheFactory#get
5602 *
5603 * @description
5604 * Get access to a cache object by the `cacheId` used when it was created.
5605 *
5606 * @param {string} cacheId Name or id of a cache to access.
5607 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
5608 */
5609 cacheFactory.get = function(cacheId) {
5610 return caches[cacheId];
5611 };
5612
5613
5614 return cacheFactory;
5615 };
5616}
5617
5618/**
5619 * @ngdoc service
5620 * @name $templateCache
5621 *
5622 * @description
5623 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
5624 * can load templates directly into the cache in a `script` tag, or by consuming the
5625 * `$templateCache` service directly.
5626 *
5627 * Adding via the `script` tag:
5628 *
5629 * ```html
5630 * <script type="text/ng-template" id="templateId.html">
5631 * <p>This is the content of the template</p>
5632 * </script>
5633 * ```
5634 *
5635 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
5636 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
5637 * element with ng-app attribute), otherwise the template will be ignored.
5638 *
5639 * Adding via the $templateCache service:
5640 *
5641 * ```js
5642 * var myApp = angular.module('myApp', []);
5643 * myApp.run(function($templateCache) {
5644 * $templateCache.put('templateId.html', 'This is the content of the template');
5645 * });
5646 * ```
5647 *
5648 * To retrieve the template later, simply use it in your HTML:
5649 * ```html
5650 * <div ng-include=" 'templateId.html' "></div>
5651 * ```
5652 *
5653 * or get it via Javascript:
5654 * ```js
5655 * $templateCache.get('templateId.html')
5656 * ```
5657 *
5658 * See {@link ng.$cacheFactory $cacheFactory}.
5659 *
5660 */
5661function $TemplateCacheProvider() {
5662 this.$get = ['$cacheFactory', function($cacheFactory) {
5663 return $cacheFactory('templates');
5664 }];
5665}
5666
5667/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
5668 *
5669 * DOM-related variables:
5670 *
5671 * - "node" - DOM Node
5672 * - "element" - DOM Element or Node
5673 * - "$node" or "$element" - jqLite-wrapped node or element
5674 *
5675 *
5676 * Compiler related stuff:
5677 *
5678 * - "linkFn" - linking fn of a single directive
5679 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
5680 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
5681 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
5682 */
5683
5684
5685/**
5686 * @ngdoc service
5687 * @name $compile
5688 * @kind function
5689 *
5690 * @description
5691 * Compiles an HTML string or DOM into a template and produces a template function, which
5692 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
5693 *
5694 * The compilation is a process of walking the DOM tree and matching DOM elements to
5695 * {@link ng.$compileProvider#directive directives}.
5696 *
5697 * <div class="alert alert-warning">
5698 * **Note:** This document is an in-depth reference of all directive options.
5699 * For a gentle introduction to directives with examples of common use cases,
5700 * see the {@link guide/directive directive guide}.
5701 * </div>
5702 *
5703 * ## Comprehensive Directive API
5704 *
5705 * There are many different options for a directive.
5706 *
5707 * The difference resides in the return value of the factory function.
5708 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
5709 * or just the `postLink` function (all other properties will have the default values).
5710 *
5711 * <div class="alert alert-success">
5712 * **Best Practice:** It's recommended to use the "directive definition object" form.
5713 * </div>
5714 *
5715 * Here's an example directive declared with a Directive Definition Object:
5716 *
5717 * ```js
5718 * var myModule = angular.module(...);
5719 *
5720 * myModule.directive('directiveName', function factory(injectables) {
5721 * var directiveDefinitionObject = {
5722 * priority: 0,
5723 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
5724 * // or
5725 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
5726 * transclude: false,
5727 * restrict: 'A',
5728 * templateNamespace: 'html',
5729 * scope: false,
5730 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
5731 * controllerAs: 'stringAlias',
5732 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
5733 * compile: function compile(tElement, tAttrs, transclude) {
5734 * return {
5735 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5736 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
5737 * }
5738 * // or
5739 * // return function postLink( ... ) { ... }
5740 * },
5741 * // or
5742 * // link: {
5743 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
5744 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
5745 * // }
5746 * // or
5747 * // link: function postLink( ... ) { ... }
5748 * };
5749 * return directiveDefinitionObject;
5750 * });
5751 * ```
5752 *
5753 * <div class="alert alert-warning">
5754 * **Note:** Any unspecified options will use the default value. You can see the default values below.
5755 * </div>
5756 *
5757 * Therefore the above can be simplified as:
5758 *
5759 * ```js
5760 * var myModule = angular.module(...);
5761 *
5762 * myModule.directive('directiveName', function factory(injectables) {
5763 * var directiveDefinitionObject = {
5764 * link: function postLink(scope, iElement, iAttrs) { ... }
5765 * };
5766 * return directiveDefinitionObject;
5767 * // or
5768 * // return function postLink(scope, iElement, iAttrs) { ... }
5769 * });
5770 * ```
5771 *
5772 *
5773 *
5774 * ### Directive Definition Object
5775 *
5776 * The directive definition object provides instructions to the {@link ng.$compile
5777 * compiler}. The attributes are:
5778 *
5779 * #### `multiElement`
5780 * When this property is set to true, the HTML compiler will collect DOM nodes between
5781 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
5782 * together as the directive elements. It is recomended that this feature be used on directives
5783 * which are not strictly behavioural (such as {@link ngClick}), and which
5784 * do not manipulate or replace child nodes (such as {@link ngInclude}).
5785 *
5786 * #### `priority`
5787 * When there are multiple directives defined on a single DOM element, sometimes it
5788 * is necessary to specify the order in which the directives are applied. The `priority` is used
5789 * to sort the directives before their `compile` functions get called. Priority is defined as a
5790 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
5791 * are also run in priority order, but post-link functions are run in reverse order. The order
5792 * of directives with the same priority is undefined. The default priority is `0`.
5793 *
5794 * #### `terminal`
5795 * If set to true then the current `priority` will be the last set of directives
5796 * which will execute (any directives at the current priority will still execute
5797 * as the order of execution on same `priority` is undefined). Note that expressions
5798 * and other directives used in the directive's template will also be excluded from execution.
5799 *
5800 * #### `scope`
5801 * **If set to `true`,** then a new scope will be created for this directive. If multiple directives on the
5802 * same element request a new scope, only one new scope is created. The new scope rule does not
5803 * apply for the root of the template since the root of the template always gets a new scope.
5804 *
5805 * **If set to `{}` (object hash),** then a new "isolate" scope is created. The 'isolate' scope differs from
5806 * normal scope in that it does not prototypically inherit from the parent scope. This is useful
5807 * when creating reusable components, which should not accidentally read or modify data in the
5808 * parent scope.
5809 *
5810 * The 'isolate' scope takes an object hash which defines a set of local scope properties
5811 * derived from the parent scope. These local properties are useful for aliasing values for
5812 * templates. Locals definition is a hash of local scope property to its source:
5813 *
5814 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
5815 * always a string since DOM attributes are strings. If no `attr` name is specified then the
5816 * attribute name is assumed to be the same as the local name.
5817 * Given `<widget my-attr="hello {{name}}">` and widget definition
5818 * of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
5819 * the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
5820 * `localName` property on the widget scope. The `name` is read from the parent scope (not
5821 * component scope).
5822 *
5823 * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
5824 * parent scope property of name defined via the value of the `attr` attribute. If no `attr`
5825 * name is specified then the attribute name is assumed to be the same as the local name.
5826 * Given `<widget my-attr="parentModel">` and widget definition of
5827 * `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
5828 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
5829 * in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
5830 * scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
5831 * can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
5832 * you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
5833 * `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
5834 *
5835 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
5836 * If no `attr` name is specified then the attribute name is assumed to be the same as the
5837 * local name. Given `<widget my-attr="count = count + value">` and widget definition of
5838 * `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
5839 * a function wrapper for the `count = count + value` expression. Often it's desirable to
5840 * pass data from the isolated scope via an expression to the parent scope, this can be
5841 * done by passing a map of local variable names and values into the expression wrapper fn.
5842 * For example, if the expression is `increment(amount)` then we can specify the amount value
5843 * by calling the `localFn` as `localFn({amount: 22})`.
5844 *
5845 *
5846 * #### `bindToController`
5847 * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
5848 * allow a component to have its properties bound to the controller, rather than to scope. When the controller
5849 * is instantiated, the initial values of the isolate scope bindings are already available.
5850 *
5851 * #### `controller`
5852 * Controller constructor function. The controller is instantiated before the
5853 * pre-linking phase and it is shared with other directives (see
5854 * `require` attribute). This allows the directives to communicate with each other and augment
5855 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
5856 *
5857 * * `$scope` - Current scope associated with the element
5858 * * `$element` - Current element
5859 * * `$attrs` - Current attributes object for the element
5860 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
5861 * `function([scope], cloneLinkingFn, futureParentElement)`.
5862 * * `scope`: optional argument to override the scope.
5863 * * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
5864 * * `futureParentElement`:
5865 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
5866 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
5867 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
5868 * and when the `cloneLinkinFn` is passed,
5869 * as those elements need to created and cloned in a special way when they are defined outside their
5870 * usual containers (e.g. like `<svg>`).
5871 * * See also the `directive.templateNamespace` property.
5872 *
5873 *
5874 * #### `require`
5875 * Require another directive and inject its controller as the fourth argument to the linking function. The
5876 * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
5877 * injected argument will be an array in corresponding order. If no such directive can be
5878 * found, or if the directive does not have a controller, then an error is raised. The name can be prefixed with:
5879 *
5880 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
5881 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
5882 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
5883 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
5884 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
5885 * `null` to the `link` fn if not found.
5886 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
5887 * `null` to the `link` fn if not found.
5888 *
5889 *
5890 * #### `controllerAs`
5891 * Controller alias at the directive scope. An alias for the controller so it
5892 * can be referenced at the directive template. The directive needs to define a scope for this
5893 * configuration to be used. Useful in the case when directive is used as component.
5894 *
5895 *
5896 * #### `restrict`
5897 * String of subset of `EACM` which restricts the directive to a specific directive
5898 * declaration style. If omitted, the defaults (elements and attributes) are used.
5899 *
5900 * * `E` - Element name (default): `<my-directive></my-directive>`
5901 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
5902 * * `C` - Class: `<div class="my-directive: exp;"></div>`
5903 * * `M` - Comment: `<!-- directive: my-directive exp -->`
5904 *
5905 *
5906 * #### `templateNamespace`
5907 * String representing the document type used by the markup in the template.
5908 * AngularJS needs this information as those elements need to be created and cloned
5909 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
5910 *
5911 * * `html` - All root nodes in the template are HTML. Root nodes may also be
5912 * top-level elements such as `<svg>` or `<math>`.
5913 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
5914 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
5915 *
5916 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
5917 *
5918 * #### `template`
5919 * HTML markup that may:
5920 * * Replace the contents of the directive's element (default).
5921 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
5922 * * Wrap the contents of the directive's element (if `transclude` is true).
5923 *
5924 * Value may be:
5925 *
5926 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
5927 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
5928 * function api below) and returns a string value.
5929 *
5930 *
5931 * #### `templateUrl`
5932 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
5933 *
5934 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
5935 * for later when the template has been resolved. In the meantime it will continue to compile and link
5936 * sibling and parent elements as though this element had not contained any directives.
5937 *
5938 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
5939 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
5940 * case when only one deeply nested directive has `templateUrl`.
5941 *
5942 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
5943 *
5944 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
5945 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
5946 * a string value representing the url. In either case, the template URL is passed through {@link
5947 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
5948 *
5949 *
5950 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
5951 * specify what the template should replace. Defaults to `false`.
5952 *
5953 * * `true` - the template will replace the directive's element.
5954 * * `false` - the template will replace the contents of the directive's element.
5955 *
5956 * The replacement process migrates all of the attributes / classes from the old element to the new
5957 * one. See the {@link guide/directive#template-expanding-directive
5958 * Directives Guide} for an example.
5959 *
5960 * There are very few scenarios where element replacement is required for the application function,
5961 * the main one being reusable custom components that are used within SVG contexts
5962 * (because SVG doesn't work with custom elements in the DOM tree).
5963 *
5964 * #### `transclude`
5965 * Extract the contents of the element where the directive appears and make it available to the directive.
5966 * The contents are compiled and provided to the directive as a **transclusion function**. See the
5967 * {@link $compile#transclusion Transclusion} section below.
5968 *
5969 * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
5970 * directive's element or the entire element:
5971 *
5972 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
5973 * * `'element'` - transclude the whole of the directive's element including any directives on this
5974 * element that defined at a lower priority than this directive. When used, the `template`
5975 * property is ignored.
5976 *
5977 *
5978 * #### `compile`
5979 *
5980 * ```js
5981 * function compile(tElement, tAttrs, transclude) { ... }
5982 * ```
5983 *
5984 * The compile function deals with transforming the template DOM. Since most directives do not do
5985 * template transformation, it is not used often. The compile function takes the following arguments:
5986 *
5987 * * `tElement` - template element - The element where the directive has been declared. It is
5988 * safe to do template transformation on the element and child elements only.
5989 *
5990 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
5991 * between all directive compile functions.
5992 *
5993 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
5994 *
5995 * <div class="alert alert-warning">
5996 * **Note:** The template instance and the link instance may be different objects if the template has
5997 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
5998 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
5999 * should be done in a linking function rather than in a compile function.
6000 * </div>
6001
6002 * <div class="alert alert-warning">
6003 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6004 * own templates or compile functions. Compiling these directives results in an infinite loop and a
6005 * stack overflow errors.
6006 *
6007 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6008 * a directive's template instead of relying on automatic template compilation via `template` or
6009 * `templateUrl` declaration or manual compilation inside the compile function.
6010 * </div>
6011 *
6012 * <div class="alert alert-error">
6013 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6014 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
6015 * to the link function instead.
6016 * </div>
6017
6018 * A compile function can have a return value which can be either a function or an object.
6019 *
6020 * * returning a (post-link) function - is equivalent to registering the linking function via the
6021 * `link` property of the config object when the compile function is empty.
6022 *
6023 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6024 * control when a linking function should be called during the linking phase. See info about
6025 * pre-linking and post-linking functions below.
6026 *
6027 *
6028 * #### `link`
6029 * This property is used only if the `compile` property is not defined.
6030 *
6031 * ```js
6032 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6033 * ```
6034 *
6035 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6036 * executed after the template has been cloned. This is where most of the directive logic will be
6037 * put.
6038 *
6039 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6040 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6041 *
6042 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
6043 * manipulate the children of the element only in `postLink` function since the children have
6044 * already been linked.
6045 *
6046 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6047 * between all directive linking functions.
6048 *
6049 * * `controller` - a controller instance - A controller instance if at least one directive on the
6050 * element defines a controller. The controller is shared among all the directives, which allows
6051 * the directives to use the controllers as a communication channel.
6052 *
6053 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6054 * This is the same as the `$transclude`
6055 * parameter of directive controllers, see there for details.
6056 * `function([scope], cloneLinkingFn, futureParentElement)`.
6057 *
6058 * #### Pre-linking function
6059 *
6060 * Executed before the child elements are linked. Not safe to do DOM transformation since the
6061 * compiler linking function will fail to locate the correct elements for linking.
6062 *
6063 * #### Post-linking function
6064 *
6065 * Executed after the child elements are linked.
6066 *
6067 * Note that child elements that contain `templateUrl` directives will not have been compiled
6068 * and linked since they are waiting for their template to load asynchronously and their own
6069 * compilation and linking has been suspended until that occurs.
6070 *
6071 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6072 * for their async templates to be resolved.
6073 *
6074 *
6075 * ### Transclusion
6076 *
6077 * Transclusion is the process of extracting a collection of DOM element from one part of the DOM and
6078 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6079 * scope from where they were taken.
6080 *
6081 * Transclusion is used (often with {@link ngTransclude}) to insert the
6082 * original contents of a directive's element into a specified place in the template of the directive.
6083 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6084 * content has access to the properties on the scope from which it was taken, even if the directive
6085 * has isolated scope.
6086 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6087 *
6088 * This makes it possible for the widget to have private state for its template, while the transcluded
6089 * content has access to its originating scope.
6090 *
6091 * <div class="alert alert-warning">
6092 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6093 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6094 * Testing Transclusion Directives}.
6095 * </div>
6096 *
6097 * #### Transclusion Functions
6098 *
6099 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6100 * function** to the directive's `link` function and `controller`. This transclusion function is a special
6101 * **linking function** that will return the compiled contents linked to a new transclusion scope.
6102 *
6103 * <div class="alert alert-info">
6104 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6105 * ngTransclude will deal with it for us.
6106 * </div>
6107 *
6108 * If you want to manually control the insertion and removal of the transcluded content in your directive
6109 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6110 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6111 *
6112 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6113 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6114 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6115 *
6116 * <div class="alert alert-info">
6117 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6118 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6119 * </div>
6120 *
6121 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6122 * attach function**:
6123 *
6124 * ```js
6125 * var transcludedContent, transclusionScope;
6126 *
6127 * $transclude(function(clone, scope) {
6128 * element.append(clone);
6129 * transcludedContent = clone;
6130 * transclusionScope = scope;
6131 * });
6132 * ```
6133 *
6134 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6135 * associated transclusion scope:
6136 *
6137 * ```js
6138 * transcludedContent.remove();
6139 * transclusionScope.$destroy();
6140 * ```
6141 *
6142 * <div class="alert alert-info">
6143 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6144 * (by calling the transclude function to get the DOM and and calling `element.remove()` to remove it),
6145 * then you are also responsible for calling `$destroy` on the transclusion scope.
6146 * </div>
6147 *
6148 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6149 * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6150 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6151 *
6152 *
6153 * #### Transclusion Scopes
6154 *
6155 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6156 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6157 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6158 * was taken.
6159 *
6160 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6161 * like this:
6162 *
6163 * ```html
6164 * <div ng-app>
6165 * <div isolate>
6166 * <div transclusion>
6167 * </div>
6168 * </div>
6169 * </div>
6170 * ```
6171 *
6172 * The `$parent` scope hierarchy will look like this:
6173 *
6174 * ```
6175 * - $rootScope
6176 * - isolate
6177 * - transclusion
6178 * ```
6179 *
6180 * but the scopes will inherit prototypically from different scopes to their `$parent`.
6181 *
6182 * ```
6183 * - $rootScope
6184 * - transclusion
6185 * - isolate
6186 * ```
6187 *
6188 *
6189 * ### Attributes
6190 *
6191 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6192 * `link()` or `compile()` functions. It has a variety of uses.
6193 *
6194 * accessing *Normalized attribute names:*
6195 * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6196 * the attributes object allows for normalized access to
6197 * the attributes.
6198 *
6199 * * *Directive inter-communication:* All directives share the same instance of the attributes
6200 * object which allows the directives to use the attributes object as inter directive
6201 * communication.
6202 *
6203 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6204 * allowing other directives to read the interpolated value.
6205 *
6206 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6207 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6208 * the only way to easily get the actual value because during the linking phase the interpolation
6209 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
6210 *
6211 * ```js
6212 * function linkingFn(scope, elm, attrs, ctrl) {
6213 * // get the attribute value
6214 * console.log(attrs.ngModel);
6215 *
6216 * // change the attribute
6217 * attrs.$set('ngModel', 'new value');
6218 *
6219 * // observe changes to interpolated attribute
6220 * attrs.$observe('ngModel', function(value) {
6221 * console.log('ngModel has changed value to ' + value);
6222 * });
6223 * }
6224 * ```
6225 *
6226 * ## Example
6227 *
6228 * <div class="alert alert-warning">
6229 * **Note**: Typically directives are registered with `module.directive`. The example below is
6230 * to illustrate how `$compile` works.
6231 * </div>
6232 *
6233 <example module="compileExample">
6234 <file name="index.html">
6235 <script>
6236 angular.module('compileExample', [], function($compileProvider) {
6237 // configure new 'compile' directive by passing a directive
6238 // factory function. The factory function injects the '$compile'
6239 $compileProvider.directive('compile', function($compile) {
6240 // directive factory creates a link function
6241 return function(scope, element, attrs) {
6242 scope.$watch(
6243 function(scope) {
6244 // watch the 'compile' expression for changes
6245 return scope.$eval(attrs.compile);
6246 },
6247 function(value) {
6248 // when the 'compile' expression changes
6249 // assign it into the current DOM
6250 element.html(value);
6251
6252 // compile the new DOM and link it to the current
6253 // scope.
6254 // NOTE: we only compile .childNodes so that
6255 // we don't get into infinite loop compiling ourselves
6256 $compile(element.contents())(scope);
6257 }
6258 );
6259 };
6260 });
6261 })
6262 .controller('GreeterController', ['$scope', function($scope) {
6263 $scope.name = 'Angular';
6264 $scope.html = 'Hello {{name}}';
6265 }]);
6266 </script>
6267 <div ng-controller="GreeterController">
6268 <input ng-model="name"> <br>
6269 <textarea ng-model="html"></textarea> <br>
6270 <div compile="html"></div>
6271 </div>
6272 </file>
6273 <file name="protractor.js" type="protractor">
6274 it('should auto compile', function() {
6275 var textarea = $('textarea');
6276 var output = $('div[compile]');
6277 // The initial state reads 'Hello Angular'.
6278 expect(output.getText()).toBe('Hello Angular');
6279 textarea.clear();
6280 textarea.sendKeys('{{name}}!');
6281 expect(output.getText()).toBe('Angular!');
6282 });
6283 </file>
6284 </example>
6285
6286 *
6287 *
6288 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
6289 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
6290 *
6291 * <div class="alert alert-error">
6292 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
6293 * e.g. will not use the right outer scope. Please pass the transclude function as a
6294 * `parentBoundTranscludeFn` to the link function instead.
6295 * </div>
6296 *
6297 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
6298 * root element(s), not their children)
6299 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
6300 * (a DOM element/tree) to a scope. Where:
6301 *
6302 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
6303 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
6304 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
6305 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
6306 * called as: <br> `cloneAttachFn(clonedElement, scope)` where:
6307 *
6308 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
6309 * * `scope` - is the current scope with which the linking function is working with.
6310 *
6311 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
6312 * keys may be used to control linking behavior:
6313 *
6314 * * `parentBoundTranscludeFn` - the transclude function made available to
6315 * directives; if given, it will be passed through to the link functions of
6316 * directives found in `element` during compilation.
6317 * * `transcludeControllers` - an object hash with keys that map controller names
6318 * to controller instances; if given, it will make the controllers
6319 * available to directives.
6320 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
6321 * the cloned elements; only needed for transcludes that are allowed to contain non html
6322 * elements (e.g. SVG elements). See also the directive.controller property.
6323 *
6324 * Calling the linking function returns the element of the template. It is either the original
6325 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
6326 *
6327 * After linking the view is not updated until after a call to $digest which typically is done by
6328 * Angular automatically.
6329 *
6330 * If you need access to the bound view, there are two ways to do it:
6331 *
6332 * - If you are not asking the linking function to clone the template, create the DOM element(s)
6333 * before you send them to the compiler and keep this reference around.
6334 * ```js
6335 * var element = $compile('<p>{{total}}</p>')(scope);
6336 * ```
6337 *
6338 * - if on the other hand, you need the element to be cloned, the view reference from the original
6339 * example would not point to the clone, but rather to the original template that was cloned. In
6340 * this case, you can access the clone via the cloneAttachFn:
6341 * ```js
6342 * var templateElement = angular.element('<p>{{total}}</p>'),
6343 * scope = ....;
6344 *
6345 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
6346 * //attach the clone to DOM document at the right place
6347 * });
6348 *
6349 * //now we have reference to the cloned DOM via `clonedElement`
6350 * ```
6351 *
6352 *
6353 * For information on how the compiler works, see the
6354 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
6355 */
6356
6357var $compileMinErr = minErr('$compile');
6358
6359/**
6360 * @ngdoc provider
6361 * @name $compileProvider
6362 *
6363 * @description
6364 */
6365$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
6366function $CompileProvider($provide, $$sanitizeUriProvider) {
6367 var hasDirectives = {},
6368 Suffix = 'Directive',
6369 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
6370 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
6371 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
6372 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
6373
6374 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
6375 // The assumption is that future DOM event attribute names will begin with
6376 // 'on' and be composed of only English letters.
6377 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
6378
6379 function parseIsolateBindings(scope, directiveName) {
6380 var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
6381
6382 var bindings = {};
6383
6384 forEach(scope, function(definition, scopeName) {
6385 var match = definition.match(LOCAL_REGEXP);
6386
6387 if (!match) {
6388 throw $compileMinErr('iscp',
6389 "Invalid isolate scope definition for directive '{0}'." +
6390 " Definition: {... {1}: '{2}' ...}",
6391 directiveName, scopeName, definition);
6392 }
6393
6394 bindings[scopeName] = {
6395 mode: match[1][0],
6396 collection: match[2] === '*',
6397 optional: match[3] === '?',
6398 attrName: match[4] || scopeName
6399 };
6400 });
6401
6402 return bindings;
6403 }
6404
6405 /**
6406 * @ngdoc method
6407 * @name $compileProvider#directive
6408 * @kind function
6409 *
6410 * @description
6411 * Register a new directive with the compiler.
6412 *
6413 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
6414 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
6415 * names and the values are the factories.
6416 * @param {Function|Array} directiveFactory An injectable directive factory function. See
6417 * {@link guide/directive} for more info.
6418 * @returns {ng.$compileProvider} Self for chaining.
6419 */
6420 this.directive = function registerDirective(name, directiveFactory) {
6421 assertNotHasOwnProperty(name, 'directive');
6422 if (isString(name)) {
6423 assertArg(directiveFactory, 'directiveFactory');
6424 if (!hasDirectives.hasOwnProperty(name)) {
6425 hasDirectives[name] = [];
6426 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
6427 function($injector, $exceptionHandler) {
6428 var directives = [];
6429 forEach(hasDirectives[name], function(directiveFactory, index) {
6430 try {
6431 var directive = $injector.invoke(directiveFactory);
6432 if (isFunction(directive)) {
6433 directive = { compile: valueFn(directive) };
6434 } else if (!directive.compile && directive.link) {
6435 directive.compile = valueFn(directive.link);
6436 }
6437 directive.priority = directive.priority || 0;
6438 directive.index = index;
6439 directive.name = directive.name || name;
6440 directive.require = directive.require || (directive.controller && directive.name);
6441 directive.restrict = directive.restrict || 'EA';
6442 if (isObject(directive.scope)) {
6443 directive.$$isolateBindings = parseIsolateBindings(directive.scope, directive.name);
6444 }
6445 directives.push(directive);
6446 } catch (e) {
6447 $exceptionHandler(e);
6448 }
6449 });
6450 return directives;
6451 }]);
6452 }
6453 hasDirectives[name].push(directiveFactory);
6454 } else {
6455 forEach(name, reverseParams(registerDirective));
6456 }
6457 return this;
6458 };
6459
6460
6461 /**
6462 * @ngdoc method
6463 * @name $compileProvider#aHrefSanitizationWhitelist
6464 * @kind function
6465 *
6466 * @description
6467 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6468 * urls during a[href] sanitization.
6469 *
6470 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
6471 *
6472 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
6473 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
6474 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6475 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6476 *
6477 * @param {RegExp=} regexp New regexp to whitelist urls with.
6478 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6479 * chaining otherwise.
6480 */
6481 this.aHrefSanitizationWhitelist = function(regexp) {
6482 if (isDefined(regexp)) {
6483 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
6484 return this;
6485 } else {
6486 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
6487 }
6488 };
6489
6490
6491 /**
6492 * @ngdoc method
6493 * @name $compileProvider#imgSrcSanitizationWhitelist
6494 * @kind function
6495 *
6496 * @description
6497 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
6498 * urls during img[src] sanitization.
6499 *
6500 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
6501 *
6502 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
6503 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
6504 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
6505 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
6506 *
6507 * @param {RegExp=} regexp New regexp to whitelist urls with.
6508 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
6509 * chaining otherwise.
6510 */
6511 this.imgSrcSanitizationWhitelist = function(regexp) {
6512 if (isDefined(regexp)) {
6513 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
6514 return this;
6515 } else {
6516 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
6517 }
6518 };
6519
6520 /**
6521 * @ngdoc method
6522 * @name $compileProvider#debugInfoEnabled
6523 *
6524 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
6525 * current debugInfoEnabled state
6526 * @returns {*} current value if used as getter or itself (chaining) if used as setter
6527 *
6528 * @kind function
6529 *
6530 * @description
6531 * Call this method to enable/disable various debug runtime information in the compiler such as adding
6532 * binding information and a reference to the current scope on to DOM elements.
6533 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
6534 * * `ng-binding` CSS class
6535 * * `$binding` data property containing an array of the binding expressions
6536 *
6537 * You may want to disable this in production for a significant performance boost. See
6538 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
6539 *
6540 * The default value is true.
6541 */
6542 var debugInfoEnabled = true;
6543 this.debugInfoEnabled = function(enabled) {
6544 if (isDefined(enabled)) {
6545 debugInfoEnabled = enabled;
6546 return this;
6547 }
6548 return debugInfoEnabled;
6549 };
6550
6551 this.$get = [
6552 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
6553 '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
6554 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
6555 $controller, $rootScope, $document, $sce, $animate, $$sanitizeUri) {
6556
6557 var Attributes = function(element, attributesToCopy) {
6558 if (attributesToCopy) {
6559 var keys = Object.keys(attributesToCopy);
6560 var i, l, key;
6561
6562 for (i = 0, l = keys.length; i < l; i++) {
6563 key = keys[i];
6564 this[key] = attributesToCopy[key];
6565 }
6566 } else {
6567 this.$attr = {};
6568 }
6569
6570 this.$$element = element;
6571 };
6572
6573 Attributes.prototype = {
6574 $normalize: directiveNormalize,
6575
6576
6577 /**
6578 * @ngdoc method
6579 * @name $compile.directive.Attributes#$addClass
6580 * @kind function
6581 *
6582 * @description
6583 * Adds the CSS class value specified by the classVal parameter to the element. If animations
6584 * are enabled then an animation will be triggered for the class addition.
6585 *
6586 * @param {string} classVal The className value that will be added to the element
6587 */
6588 $addClass: function(classVal) {
6589 if (classVal && classVal.length > 0) {
6590 $animate.addClass(this.$$element, classVal);
6591 }
6592 },
6593
6594 /**
6595 * @ngdoc method
6596 * @name $compile.directive.Attributes#$removeClass
6597 * @kind function
6598 *
6599 * @description
6600 * Removes the CSS class value specified by the classVal parameter from the element. If
6601 * animations are enabled then an animation will be triggered for the class removal.
6602 *
6603 * @param {string} classVal The className value that will be removed from the element
6604 */
6605 $removeClass: function(classVal) {
6606 if (classVal && classVal.length > 0) {
6607 $animate.removeClass(this.$$element, classVal);
6608 }
6609 },
6610
6611 /**
6612 * @ngdoc method
6613 * @name $compile.directive.Attributes#$updateClass
6614 * @kind function
6615 *
6616 * @description
6617 * Adds and removes the appropriate CSS class values to the element based on the difference
6618 * between the new and old CSS class values (specified as newClasses and oldClasses).
6619 *
6620 * @param {string} newClasses The current CSS className value
6621 * @param {string} oldClasses The former CSS className value
6622 */
6623 $updateClass: function(newClasses, oldClasses) {
6624 var toAdd = tokenDifference(newClasses, oldClasses);
6625 if (toAdd && toAdd.length) {
6626 $animate.addClass(this.$$element, toAdd);
6627 }
6628
6629 var toRemove = tokenDifference(oldClasses, newClasses);
6630 if (toRemove && toRemove.length) {
6631 $animate.removeClass(this.$$element, toRemove);
6632 }
6633 },
6634
6635 /**
6636 * Set a normalized attribute on the element in a way such that all directives
6637 * can share the attribute. This function properly handles boolean attributes.
6638 * @param {string} key Normalized key. (ie ngAttribute)
6639 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
6640 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
6641 * Defaults to true.
6642 * @param {string=} attrName Optional none normalized name. Defaults to key.
6643 */
6644 $set: function(key, value, writeAttr, attrName) {
6645 // TODO: decide whether or not to throw an error if "class"
6646 //is set through this function since it may cause $updateClass to
6647 //become unstable.
6648
6649 var node = this.$$element[0],
6650 booleanKey = getBooleanAttrName(node, key),
6651 aliasedKey = getAliasedAttrName(node, key),
6652 observer = key,
6653 nodeName;
6654
6655 if (booleanKey) {
6656 this.$$element.prop(key, value);
6657 attrName = booleanKey;
6658 } else if (aliasedKey) {
6659 this[aliasedKey] = value;
6660 observer = aliasedKey;
6661 }
6662
6663 this[key] = value;
6664
6665 // translate normalized key to actual key
6666 if (attrName) {
6667 this.$attr[key] = attrName;
6668 } else {
6669 attrName = this.$attr[key];
6670 if (!attrName) {
6671 this.$attr[key] = attrName = snake_case(key, '-');
6672 }
6673 }
6674
6675 nodeName = nodeName_(this.$$element);
6676
6677 if ((nodeName === 'a' && key === 'href') ||
6678 (nodeName === 'img' && key === 'src')) {
6679 // sanitize a[href] and img[src] values
6680 this[key] = value = $$sanitizeUri(value, key === 'src');
6681 } else if (nodeName === 'img' && key === 'srcset') {
6682 // sanitize img[srcset] values
6683 var result = "";
6684
6685 // first check if there are spaces because it's not the same pattern
6686 var trimmedSrcset = trim(value);
6687 // ( 999x ,| 999w ,| ,|, )
6688 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
6689 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
6690
6691 // split srcset into tuple of uri and descriptor except for the last item
6692 var rawUris = trimmedSrcset.split(pattern);
6693
6694 // for each tuples
6695 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
6696 for (var i = 0; i < nbrUrisWith2parts; i++) {
6697 var innerIdx = i * 2;
6698 // sanitize the uri
6699 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
6700 // add the descriptor
6701 result += (" " + trim(rawUris[innerIdx + 1]));
6702 }
6703
6704 // split the last item into uri and descriptor
6705 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
6706
6707 // sanitize the last uri
6708 result += $$sanitizeUri(trim(lastTuple[0]), true);
6709
6710 // and add the last descriptor if any
6711 if (lastTuple.length === 2) {
6712 result += (" " + trim(lastTuple[1]));
6713 }
6714 this[key] = value = result;
6715 }
6716
6717 if (writeAttr !== false) {
6718 if (value === null || value === undefined) {
6719 this.$$element.removeAttr(attrName);
6720 } else {
6721 this.$$element.attr(attrName, value);
6722 }
6723 }
6724
6725 // fire observers
6726 var $$observers = this.$$observers;
6727 $$observers && forEach($$observers[observer], function(fn) {
6728 try {
6729 fn(value);
6730 } catch (e) {
6731 $exceptionHandler(e);
6732 }
6733 });
6734 },
6735
6736
6737 /**
6738 * @ngdoc method
6739 * @name $compile.directive.Attributes#$observe
6740 * @kind function
6741 *
6742 * @description
6743 * Observes an interpolated attribute.
6744 *
6745 * The observer function will be invoked once during the next `$digest` following
6746 * compilation. The observer is then invoked whenever the interpolated value
6747 * changes.
6748 *
6749 * @param {string} key Normalized key. (ie ngAttribute) .
6750 * @param {function(interpolatedValue)} fn Function that will be called whenever
6751 the interpolated value of the attribute changes.
6752 * See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
6753 * @returns {function()} Returns a deregistration function for this observer.
6754 */
6755 $observe: function(key, fn) {
6756 var attrs = this,
6757 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
6758 listeners = ($$observers[key] || ($$observers[key] = []));
6759
6760 listeners.push(fn);
6761 $rootScope.$evalAsync(function() {
6762 if (!listeners.$$inter && attrs.hasOwnProperty(key)) {
6763 // no one registered attribute interpolation function, so lets call it manually
6764 fn(attrs[key]);
6765 }
6766 });
6767
6768 return function() {
6769 arrayRemove(listeners, fn);
6770 };
6771 }
6772 };
6773
6774
6775 function safeAddClass($element, className) {
6776 try {
6777 $element.addClass(className);
6778 } catch (e) {
6779 // ignore, since it means that we are trying to set class on
6780 // SVG element, where class name is read-only.
6781 }
6782 }
6783
6784
6785 var startSymbol = $interpolate.startSymbol(),
6786 endSymbol = $interpolate.endSymbol(),
6787 denormalizeTemplate = (startSymbol == '{{' || endSymbol == '}}')
6788 ? identity
6789 : function denormalizeTemplate(template) {
6790 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
6791 },
6792 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
6793
6794 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
6795 var bindings = $element.data('$binding') || [];
6796
6797 if (isArray(binding)) {
6798 bindings = bindings.concat(binding);
6799 } else {
6800 bindings.push(binding);
6801 }
6802
6803 $element.data('$binding', bindings);
6804 } : noop;
6805
6806 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
6807 safeAddClass($element, 'ng-binding');
6808 } : noop;
6809
6810 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
6811 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
6812 $element.data(dataName, scope);
6813 } : noop;
6814
6815 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
6816 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
6817 } : noop;
6818
6819 return compile;
6820
6821 //================================
6822
6823 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
6824 previousCompileContext) {
6825 if (!($compileNodes instanceof jqLite)) {
6826 // jquery always rewraps, whereas we need to preserve the original selector so that we can
6827 // modify it.
6828 $compileNodes = jqLite($compileNodes);
6829 }
6830 // We can not compile top level text elements since text nodes can be merged and we will
6831 // not be able to attach scope data to them, so we will wrap them in <span>
6832 forEach($compileNodes, function(node, index) {
6833 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
6834 $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
6835 }
6836 });
6837 var compositeLinkFn =
6838 compileNodes($compileNodes, transcludeFn, $compileNodes,
6839 maxPriority, ignoreDirective, previousCompileContext);
6840 compile.$$addScopeClass($compileNodes);
6841 var namespace = null;
6842 return function publicLinkFn(scope, cloneConnectFn, options) {
6843 assertArg(scope, 'scope');
6844
6845 options = options || {};
6846 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
6847 transcludeControllers = options.transcludeControllers,
6848 futureParentElement = options.futureParentElement;
6849
6850 // When `parentBoundTranscludeFn` is passed, it is a
6851 // `controllersBoundTransclude` function (it was previously passed
6852 // as `transclude` to directive.link) so we must unwrap it to get
6853 // its `boundTranscludeFn`
6854 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
6855 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
6856 }
6857
6858 if (!namespace) {
6859 namespace = detectNamespaceForChildElements(futureParentElement);
6860 }
6861 var $linkNode;
6862 if (namespace !== 'html') {
6863 // When using a directive with replace:true and templateUrl the $compileNodes
6864 // (or a child element inside of them)
6865 // might change, so we need to recreate the namespace adapted compileNodes
6866 // for call to the link function.
6867 // Note: This will already clone the nodes...
6868 $linkNode = jqLite(
6869 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
6870 );
6871 } else if (cloneConnectFn) {
6872 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
6873 // and sometimes changes the structure of the DOM.
6874 $linkNode = JQLitePrototype.clone.call($compileNodes);
6875 } else {
6876 $linkNode = $compileNodes;
6877 }
6878
6879 if (transcludeControllers) {
6880 for (var controllerName in transcludeControllers) {
6881 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
6882 }
6883 }
6884
6885 compile.$$addScopeInfo($linkNode, scope);
6886
6887 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
6888 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
6889 return $linkNode;
6890 };
6891 }
6892
6893 function detectNamespaceForChildElements(parentElement) {
6894 // TODO: Make this detect MathML as well...
6895 var node = parentElement && parentElement[0];
6896 if (!node) {
6897 return 'html';
6898 } else {
6899 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
6900 }
6901 }
6902
6903 /**
6904 * Compile function matches each node in nodeList against the directives. Once all directives
6905 * for a particular node are collected their compile functions are executed. The compile
6906 * functions return values - the linking functions - are combined into a composite linking
6907 * function, which is the a linking function for the node.
6908 *
6909 * @param {NodeList} nodeList an array of nodes or NodeList to compile
6910 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
6911 * scope argument is auto-generated to the new child of the transcluded parent scope.
6912 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
6913 * the rootElement must be set the jqLite collection of the compile root. This is
6914 * needed so that the jqLite collection items can be replaced with widgets.
6915 * @param {number=} maxPriority Max directive priority.
6916 * @returns {Function} A composite linking function of all of the matched directives or null.
6917 */
6918 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
6919 previousCompileContext) {
6920 var linkFns = [],
6921 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
6922
6923 for (var i = 0; i < nodeList.length; i++) {
6924 attrs = new Attributes();
6925
6926 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
6927 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
6928 ignoreDirective);
6929
6930 nodeLinkFn = (directives.length)
6931 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
6932 null, [], [], previousCompileContext)
6933 : null;
6934
6935 if (nodeLinkFn && nodeLinkFn.scope) {
6936 compile.$$addScopeClass(attrs.$$element);
6937 }
6938
6939 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
6940 !(childNodes = nodeList[i].childNodes) ||
6941 !childNodes.length)
6942 ? null
6943 : compileNodes(childNodes,
6944 nodeLinkFn ? (
6945 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
6946 && nodeLinkFn.transclude) : transcludeFn);
6947
6948 if (nodeLinkFn || childLinkFn) {
6949 linkFns.push(i, nodeLinkFn, childLinkFn);
6950 linkFnFound = true;
6951 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
6952 }
6953
6954 //use the previous context only for the first element in the virtual group
6955 previousCompileContext = null;
6956 }
6957
6958 // return a linking function if we have found anything, null otherwise
6959 return linkFnFound ? compositeLinkFn : null;
6960
6961 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
6962 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
6963 var stableNodeList;
6964
6965
6966 if (nodeLinkFnFound) {
6967 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
6968 // offsets don't get screwed up
6969 var nodeListLength = nodeList.length;
6970 stableNodeList = new Array(nodeListLength);
6971
6972 // create a sparse array by only copying the elements which have a linkFn
6973 for (i = 0; i < linkFns.length; i+=3) {
6974 idx = linkFns[i];
6975 stableNodeList[idx] = nodeList[idx];
6976 }
6977 } else {
6978 stableNodeList = nodeList;
6979 }
6980
6981 for (i = 0, ii = linkFns.length; i < ii;) {
6982 node = stableNodeList[linkFns[i++]];
6983 nodeLinkFn = linkFns[i++];
6984 childLinkFn = linkFns[i++];
6985
6986 if (nodeLinkFn) {
6987 if (nodeLinkFn.scope) {
6988 childScope = scope.$new();
6989 compile.$$addScopeInfo(jqLite(node), childScope);
6990 } else {
6991 childScope = scope;
6992 }
6993
6994 if (nodeLinkFn.transcludeOnThisElement) {
6995 childBoundTranscludeFn = createBoundTranscludeFn(
6996 scope, nodeLinkFn.transclude, parentBoundTranscludeFn,
6997 nodeLinkFn.elementTranscludeOnThisElement);
6998
6999 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7000 childBoundTranscludeFn = parentBoundTranscludeFn;
7001
7002 } else if (!parentBoundTranscludeFn && transcludeFn) {
7003 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7004
7005 } else {
7006 childBoundTranscludeFn = null;
7007 }
7008
7009 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7010
7011 } else if (childLinkFn) {
7012 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7013 }
7014 }
7015 }
7016 }
7017
7018 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn, elementTransclusion) {
7019
7020 var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7021
7022 if (!transcludedScope) {
7023 transcludedScope = scope.$new(false, containingScope);
7024 transcludedScope.$$transcluded = true;
7025 }
7026
7027 return transcludeFn(transcludedScope, cloneFn, {
7028 parentBoundTranscludeFn: previousBoundTranscludeFn,
7029 transcludeControllers: controllers,
7030 futureParentElement: futureParentElement
7031 });
7032 };
7033
7034 return boundTranscludeFn;
7035 }
7036
7037 /**
7038 * Looks for directives on the given node and adds them to the directive collection which is
7039 * sorted.
7040 *
7041 * @param node Node to search.
7042 * @param directives An array to which the directives are added to. This array is sorted before
7043 * the function returns.
7044 * @param attrs The shared attrs object which is used to populate the normalized attributes.
7045 * @param {number=} maxPriority Max directive priority.
7046 */
7047 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7048 var nodeType = node.nodeType,
7049 attrsMap = attrs.$attr,
7050 match,
7051 className;
7052
7053 switch (nodeType) {
7054 case NODE_TYPE_ELEMENT: /* Element */
7055 // use the node name: <directive>
7056 addDirective(directives,
7057 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7058
7059 // iterate over the attributes
7060 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7061 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7062 var attrStartName = false;
7063 var attrEndName = false;
7064
7065 attr = nAttrs[j];
7066 name = attr.name;
7067 value = trim(attr.value);
7068
7069 // support ngAttr attribute binding
7070 ngAttrName = directiveNormalize(name);
7071 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7072 name = snake_case(ngAttrName.substr(6), '-');
7073 }
7074
7075 var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
7076 if (directiveIsMultiElement(directiveNName)) {
7077 if (ngAttrName === directiveNName + 'Start') {
7078 attrStartName = name;
7079 attrEndName = name.substr(0, name.length - 5) + 'end';
7080 name = name.substr(0, name.length - 6);
7081 }
7082 }
7083
7084 nName = directiveNormalize(name.toLowerCase());
7085 attrsMap[nName] = name;
7086 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7087 attrs[nName] = value;
7088 if (getBooleanAttrName(node, nName)) {
7089 attrs[nName] = true; // presence means true
7090 }
7091 }
7092 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7093 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7094 attrEndName);
7095 }
7096
7097 // use class as directive
7098 className = node.className;
7099 if (isString(className) && className !== '') {
7100 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7101 nName = directiveNormalize(match[2]);
7102 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7103 attrs[nName] = trim(match[3]);
7104 }
7105 className = className.substr(match.index + match[0].length);
7106 }
7107 }
7108 break;
7109 case NODE_TYPE_TEXT: /* Text Node */
7110 addTextInterpolateDirective(directives, node.nodeValue);
7111 break;
7112 case NODE_TYPE_COMMENT: /* Comment */
7113 try {
7114 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7115 if (match) {
7116 nName = directiveNormalize(match[1]);
7117 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7118 attrs[nName] = trim(match[2]);
7119 }
7120 }
7121 } catch (e) {
7122 // turns out that under some circumstances IE9 throws errors when one attempts to read
7123 // comment's node value.
7124 // Just ignore it and continue. (Can't seem to reproduce in test case.)
7125 }
7126 break;
7127 }
7128
7129 directives.sort(byPriority);
7130 return directives;
7131 }
7132
7133 /**
7134 * Given a node with an directive-start it collects all of the siblings until it finds
7135 * directive-end.
7136 * @param node
7137 * @param attrStart
7138 * @param attrEnd
7139 * @returns {*}
7140 */
7141 function groupScan(node, attrStart, attrEnd) {
7142 var nodes = [];
7143 var depth = 0;
7144 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7145 do {
7146 if (!node) {
7147 throw $compileMinErr('uterdir',
7148 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7149 attrStart, attrEnd);
7150 }
7151 if (node.nodeType == NODE_TYPE_ELEMENT) {
7152 if (node.hasAttribute(attrStart)) depth++;
7153 if (node.hasAttribute(attrEnd)) depth--;
7154 }
7155 nodes.push(node);
7156 node = node.nextSibling;
7157 } while (depth > 0);
7158 } else {
7159 nodes.push(node);
7160 }
7161
7162 return jqLite(nodes);
7163 }
7164
7165 /**
7166 * Wrapper for linking function which converts normal linking function into a grouped
7167 * linking function.
7168 * @param linkFn
7169 * @param attrStart
7170 * @param attrEnd
7171 * @returns {Function}
7172 */
7173 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7174 return function(scope, element, attrs, controllers, transcludeFn) {
7175 element = groupScan(element[0], attrStart, attrEnd);
7176 return linkFn(scope, element, attrs, controllers, transcludeFn);
7177 };
7178 }
7179
7180 /**
7181 * Once the directives have been collected, their compile functions are executed. This method
7182 * is responsible for inlining directive templates as well as terminating the application
7183 * of the directives if the terminal directive has been reached.
7184 *
7185 * @param {Array} directives Array of collected directives to execute their compile function.
7186 * this needs to be pre-sorted by priority order.
7187 * @param {Node} compileNode The raw DOM node to apply the compile functions to
7188 * @param {Object} templateAttrs The shared attribute function
7189 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7190 * scope argument is auto-generated to the new
7191 * child of the transcluded parent scope.
7192 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
7193 * argument has the root jqLite array so that we can replace nodes
7194 * on it.
7195 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
7196 * compiling the transclusion.
7197 * @param {Array.<Function>} preLinkFns
7198 * @param {Array.<Function>} postLinkFns
7199 * @param {Object} previousCompileContext Context used for previous compilation of the current
7200 * node
7201 * @returns {Function} linkFn
7202 */
7203 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
7204 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
7205 previousCompileContext) {
7206 previousCompileContext = previousCompileContext || {};
7207
7208 var terminalPriority = -Number.MAX_VALUE,
7209 newScopeDirective,
7210 controllerDirectives = previousCompileContext.controllerDirectives,
7211 controllers,
7212 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
7213 templateDirective = previousCompileContext.templateDirective,
7214 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
7215 hasTranscludeDirective = false,
7216 hasTemplate = false,
7217 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
7218 $compileNode = templateAttrs.$$element = jqLite(compileNode),
7219 directive,
7220 directiveName,
7221 $template,
7222 replaceDirective = originalReplaceDirective,
7223 childTranscludeFn = transcludeFn,
7224 linkFn,
7225 directiveValue;
7226
7227 // executes all directives on the current element
7228 for (var i = 0, ii = directives.length; i < ii; i++) {
7229 directive = directives[i];
7230 var attrStart = directive.$$start;
7231 var attrEnd = directive.$$end;
7232
7233 // collect multiblock sections
7234 if (attrStart) {
7235 $compileNode = groupScan(compileNode, attrStart, attrEnd);
7236 }
7237 $template = undefined;
7238
7239 if (terminalPriority > directive.priority) {
7240 break; // prevent further processing of directives
7241 }
7242
7243 if (directiveValue = directive.scope) {
7244
7245 // skip the check for directives with async templates, we'll check the derived sync
7246 // directive when the template arrives
7247 if (!directive.templateUrl) {
7248 if (isObject(directiveValue)) {
7249 // This directive is trying to add an isolated scope.
7250 // Check that there is no scope of any kind already
7251 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
7252 directive, $compileNode);
7253 newIsolateScopeDirective = directive;
7254 } else {
7255 // This directive is trying to add a child scope.
7256 // Check that there is no isolated scope already
7257 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
7258 $compileNode);
7259 }
7260 }
7261
7262 newScopeDirective = newScopeDirective || directive;
7263 }
7264
7265 directiveName = directive.name;
7266
7267 if (!directive.templateUrl && directive.controller) {
7268 directiveValue = directive.controller;
7269 controllerDirectives = controllerDirectives || {};
7270 assertNoDuplicate("'" + directiveName + "' controller",
7271 controllerDirectives[directiveName], directive, $compileNode);
7272 controllerDirectives[directiveName] = directive;
7273 }
7274
7275 if (directiveValue = directive.transclude) {
7276 hasTranscludeDirective = true;
7277
7278 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
7279 // This option should only be used by directives that know how to safely handle element transclusion,
7280 // where the transcluded nodes are added or replaced after linking.
7281 if (!directive.$$tlb) {
7282 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
7283 nonTlbTranscludeDirective = directive;
7284 }
7285
7286 if (directiveValue == 'element') {
7287 hasElementTranscludeDirective = true;
7288 terminalPriority = directive.priority;
7289 $template = $compileNode;
7290 $compileNode = templateAttrs.$$element =
7291 jqLite(document.createComment(' ' + directiveName + ': ' +
7292 templateAttrs[directiveName] + ' '));
7293 compileNode = $compileNode[0];
7294 replaceWith(jqCollection, sliceArgs($template), compileNode);
7295
7296 childTranscludeFn = compile($template, transcludeFn, terminalPriority,
7297 replaceDirective && replaceDirective.name, {
7298 // Don't pass in:
7299 // - controllerDirectives - otherwise we'll create duplicates controllers
7300 // - newIsolateScopeDirective or templateDirective - combining templates with
7301 // element transclusion doesn't make sense.
7302 //
7303 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
7304 // on the same element more than once.
7305 nonTlbTranscludeDirective: nonTlbTranscludeDirective
7306 });
7307 } else {
7308 $template = jqLite(jqLiteClone(compileNode)).contents();
7309 $compileNode.empty(); // clear contents
7310 childTranscludeFn = compile($template, transcludeFn);
7311 }
7312 }
7313
7314 if (directive.template) {
7315 hasTemplate = true;
7316 assertNoDuplicate('template', templateDirective, directive, $compileNode);
7317 templateDirective = directive;
7318
7319 directiveValue = (isFunction(directive.template))
7320 ? directive.template($compileNode, templateAttrs)
7321 : directive.template;
7322
7323 directiveValue = denormalizeTemplate(directiveValue);
7324
7325 if (directive.replace) {
7326 replaceDirective = directive;
7327 if (jqLiteIsTextNode(directiveValue)) {
7328 $template = [];
7329 } else {
7330 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
7331 }
7332 compileNode = $template[0];
7333
7334 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7335 throw $compileMinErr('tplrt',
7336 "Template for directive '{0}' must have exactly one root element. {1}",
7337 directiveName, '');
7338 }
7339
7340 replaceWith(jqCollection, $compileNode, compileNode);
7341
7342 var newTemplateAttrs = {$attr: {}};
7343
7344 // combine directives from the original node and from the template:
7345 // - take the array of directives for this element
7346 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
7347 // - collect directives from the template and sort them by priority
7348 // - combine directives as: processed + template + unprocessed
7349 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
7350 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
7351
7352 if (newIsolateScopeDirective) {
7353 markDirectivesAsIsolate(templateDirectives);
7354 }
7355 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
7356 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
7357
7358 ii = directives.length;
7359 } else {
7360 $compileNode.html(directiveValue);
7361 }
7362 }
7363
7364 if (directive.templateUrl) {
7365 hasTemplate = true;
7366 assertNoDuplicate('template', templateDirective, directive, $compileNode);
7367 templateDirective = directive;
7368
7369 if (directive.replace) {
7370 replaceDirective = directive;
7371 }
7372
7373 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
7374 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
7375 controllerDirectives: controllerDirectives,
7376 newIsolateScopeDirective: newIsolateScopeDirective,
7377 templateDirective: templateDirective,
7378 nonTlbTranscludeDirective: nonTlbTranscludeDirective
7379 });
7380 ii = directives.length;
7381 } else if (directive.compile) {
7382 try {
7383 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
7384 if (isFunction(linkFn)) {
7385 addLinkFns(null, linkFn, attrStart, attrEnd);
7386 } else if (linkFn) {
7387 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
7388 }
7389 } catch (e) {
7390 $exceptionHandler(e, startingTag($compileNode));
7391 }
7392 }
7393
7394 if (directive.terminal) {
7395 nodeLinkFn.terminal = true;
7396 terminalPriority = Math.max(terminalPriority, directive.priority);
7397 }
7398
7399 }
7400
7401 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
7402 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
7403 nodeLinkFn.elementTranscludeOnThisElement = hasElementTranscludeDirective;
7404 nodeLinkFn.templateOnThisElement = hasTemplate;
7405 nodeLinkFn.transclude = childTranscludeFn;
7406
7407 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
7408
7409 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
7410 return nodeLinkFn;
7411
7412 ////////////////////
7413
7414 function addLinkFns(pre, post, attrStart, attrEnd) {
7415 if (pre) {
7416 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
7417 pre.require = directive.require;
7418 pre.directiveName = directiveName;
7419 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7420 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
7421 }
7422 preLinkFns.push(pre);
7423 }
7424 if (post) {
7425 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
7426 post.require = directive.require;
7427 post.directiveName = directiveName;
7428 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
7429 post = cloneAndAnnotateFn(post, {isolateScope: true});
7430 }
7431 postLinkFns.push(post);
7432 }
7433 }
7434
7435
7436 function getControllers(directiveName, require, $element, elementControllers) {
7437 var value, retrievalMethod = 'data', optional = false;
7438 var $searchElement = $element;
7439 var match;
7440 if (isString(require)) {
7441 match = require.match(REQUIRE_PREFIX_REGEXP);
7442 require = require.substring(match[0].length);
7443
7444 if (match[3]) {
7445 if (match[1]) match[3] = null;
7446 else match[1] = match[3];
7447 }
7448 if (match[1] === '^') {
7449 retrievalMethod = 'inheritedData';
7450 } else if (match[1] === '^^') {
7451 retrievalMethod = 'inheritedData';
7452 $searchElement = $element.parent();
7453 }
7454 if (match[2] === '?') {
7455 optional = true;
7456 }
7457
7458 value = null;
7459
7460 if (elementControllers && retrievalMethod === 'data') {
7461 if (value = elementControllers[require]) {
7462 value = value.instance;
7463 }
7464 }
7465 value = value || $searchElement[retrievalMethod]('$' + require + 'Controller');
7466
7467 if (!value && !optional) {
7468 throw $compileMinErr('ctreq',
7469 "Controller '{0}', required by directive '{1}', can't be found!",
7470 require, directiveName);
7471 }
7472 return value || null;
7473 } else if (isArray(require)) {
7474 value = [];
7475 forEach(require, function(require) {
7476 value.push(getControllers(directiveName, require, $element, elementControllers));
7477 });
7478 }
7479 return value;
7480 }
7481
7482
7483 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
7484 var i, ii, linkFn, controller, isolateScope, elementControllers, transcludeFn, $element,
7485 attrs;
7486
7487 if (compileNode === linkNode) {
7488 attrs = templateAttrs;
7489 $element = templateAttrs.$$element;
7490 } else {
7491 $element = jqLite(linkNode);
7492 attrs = new Attributes($element, templateAttrs);
7493 }
7494
7495 if (newIsolateScopeDirective) {
7496 isolateScope = scope.$new(true);
7497 }
7498
7499 if (boundTranscludeFn) {
7500 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
7501 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
7502 transcludeFn = controllersBoundTransclude;
7503 transcludeFn.$$boundTransclude = boundTranscludeFn;
7504 }
7505
7506 if (controllerDirectives) {
7507 // TODO: merge `controllers` and `elementControllers` into single object.
7508 controllers = {};
7509 elementControllers = {};
7510 forEach(controllerDirectives, function(directive) {
7511 var locals = {
7512 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
7513 $element: $element,
7514 $attrs: attrs,
7515 $transclude: transcludeFn
7516 }, controllerInstance;
7517
7518 controller = directive.controller;
7519 if (controller == '@') {
7520 controller = attrs[directive.name];
7521 }
7522
7523 controllerInstance = $controller(controller, locals, true, directive.controllerAs);
7524
7525 // For directives with element transclusion the element is a comment,
7526 // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
7527 // clean up (http://bugs.jquery.com/ticket/8335).
7528 // Instead, we save the controllers for the element in a local hash and attach to .data
7529 // later, once we have the actual element.
7530 elementControllers[directive.name] = controllerInstance;
7531 if (!hasElementTranscludeDirective) {
7532 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
7533 }
7534
7535 controllers[directive.name] = controllerInstance;
7536 });
7537 }
7538
7539 if (newIsolateScopeDirective) {
7540 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
7541 templateDirective === newIsolateScopeDirective.$$originalDirective)));
7542 compile.$$addScopeClass($element, true);
7543
7544 var isolateScopeController = controllers && controllers[newIsolateScopeDirective.name];
7545 var isolateBindingContext = isolateScope;
7546 if (isolateScopeController && isolateScopeController.identifier &&
7547 newIsolateScopeDirective.bindToController === true) {
7548 isolateBindingContext = isolateScopeController.instance;
7549 }
7550
7551 forEach(isolateScope.$$isolateBindings = newIsolateScopeDirective.$$isolateBindings, function(definition, scopeName) {
7552 var attrName = definition.attrName,
7553 optional = definition.optional,
7554 mode = definition.mode, // @, =, or &
7555 lastValue,
7556 parentGet, parentSet, compare;
7557
7558 switch (mode) {
7559
7560 case '@':
7561 attrs.$observe(attrName, function(value) {
7562 isolateBindingContext[scopeName] = value;
7563 });
7564 attrs.$$observers[attrName].$$scope = scope;
7565 if (attrs[attrName]) {
7566 // If the attribute has been provided then we trigger an interpolation to ensure
7567 // the value is there for use in the link fn
7568 isolateBindingContext[scopeName] = $interpolate(attrs[attrName])(scope);
7569 }
7570 break;
7571
7572 case '=':
7573 if (optional && !attrs[attrName]) {
7574 return;
7575 }
7576 parentGet = $parse(attrs[attrName]);
7577 if (parentGet.literal) {
7578 compare = equals;
7579 } else {
7580 compare = function(a, b) { return a === b || (a !== a && b !== b); };
7581 }
7582 parentSet = parentGet.assign || function() {
7583 // reset the change, or we will throw this exception on every $digest
7584 lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7585 throw $compileMinErr('nonassign',
7586 "Expression '{0}' used with directive '{1}' is non-assignable!",
7587 attrs[attrName], newIsolateScopeDirective.name);
7588 };
7589 lastValue = isolateBindingContext[scopeName] = parentGet(scope);
7590 var parentValueWatch = function parentValueWatch(parentValue) {
7591 if (!compare(parentValue, isolateBindingContext[scopeName])) {
7592 // we are out of sync and need to copy
7593 if (!compare(parentValue, lastValue)) {
7594 // parent changed and it has precedence
7595 isolateBindingContext[scopeName] = parentValue;
7596 } else {
7597 // if the parent can be assigned then do so
7598 parentSet(scope, parentValue = isolateBindingContext[scopeName]);
7599 }
7600 }
7601 return lastValue = parentValue;
7602 };
7603 parentValueWatch.$stateful = true;
7604 var unwatch;
7605 if (definition.collection) {
7606 unwatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
7607 } else {
7608 unwatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
7609 }
7610 isolateScope.$on('$destroy', unwatch);
7611 break;
7612
7613 case '&':
7614 parentGet = $parse(attrs[attrName]);
7615 isolateBindingContext[scopeName] = function(locals) {
7616 return parentGet(scope, locals);
7617 };
7618 break;
7619 }
7620 });
7621 }
7622 if (controllers) {
7623 forEach(controllers, function(controller) {
7624 controller();
7625 });
7626 controllers = null;
7627 }
7628
7629 // PRELINKING
7630 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
7631 linkFn = preLinkFns[i];
7632 invokeLinkFn(linkFn,
7633 linkFn.isolateScope ? isolateScope : scope,
7634 $element,
7635 attrs,
7636 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7637 transcludeFn
7638 );
7639 }
7640
7641 // RECURSION
7642 // We only pass the isolate scope, if the isolate directive has a template,
7643 // otherwise the child elements do not belong to the isolate directive.
7644 var scopeToChild = scope;
7645 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
7646 scopeToChild = isolateScope;
7647 }
7648 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
7649
7650 // POSTLINKING
7651 for (i = postLinkFns.length - 1; i >= 0; i--) {
7652 linkFn = postLinkFns[i];
7653 invokeLinkFn(linkFn,
7654 linkFn.isolateScope ? isolateScope : scope,
7655 $element,
7656 attrs,
7657 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
7658 transcludeFn
7659 );
7660 }
7661
7662 // This is the function that is injected as `$transclude`.
7663 // Note: all arguments are optional!
7664 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
7665 var transcludeControllers;
7666
7667 // No scope passed in:
7668 if (!isScope(scope)) {
7669 futureParentElement = cloneAttachFn;
7670 cloneAttachFn = scope;
7671 scope = undefined;
7672 }
7673
7674 if (hasElementTranscludeDirective) {
7675 transcludeControllers = elementControllers;
7676 }
7677 if (!futureParentElement) {
7678 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
7679 }
7680 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
7681 }
7682 }
7683 }
7684
7685 function markDirectivesAsIsolate(directives) {
7686 // mark all directives as needing isolate scope.
7687 for (var j = 0, jj = directives.length; j < jj; j++) {
7688 directives[j] = inherit(directives[j], {$$isolateScope: true});
7689 }
7690 }
7691
7692 /**
7693 * looks up the directive and decorates it with exception handling and proper parameters. We
7694 * call this the boundDirective.
7695 *
7696 * @param {string} name name of the directive to look up.
7697 * @param {string} location The directive must be found in specific format.
7698 * String containing any of theses characters:
7699 *
7700 * * `E`: element name
7701 * * `A': attribute
7702 * * `C`: class
7703 * * `M`: comment
7704 * @returns {boolean} true if directive was added.
7705 */
7706 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
7707 endAttrName) {
7708 if (name === ignoreDirective) return null;
7709 var match = null;
7710 if (hasDirectives.hasOwnProperty(name)) {
7711 for (var directive, directives = $injector.get(name + Suffix),
7712 i = 0, ii = directives.length; i < ii; i++) {
7713 try {
7714 directive = directives[i];
7715 if ((maxPriority === undefined || maxPriority > directive.priority) &&
7716 directive.restrict.indexOf(location) != -1) {
7717 if (startAttrName) {
7718 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
7719 }
7720 tDirectives.push(directive);
7721 match = directive;
7722 }
7723 } catch (e) { $exceptionHandler(e); }
7724 }
7725 }
7726 return match;
7727 }
7728
7729
7730 /**
7731 * looks up the directive and returns true if it is a multi-element directive,
7732 * and therefore requires DOM nodes between -start and -end markers to be grouped
7733 * together.
7734 *
7735 * @param {string} name name of the directive to look up.
7736 * @returns true if directive was registered as multi-element.
7737 */
7738 function directiveIsMultiElement(name) {
7739 if (hasDirectives.hasOwnProperty(name)) {
7740 for (var directive, directives = $injector.get(name + Suffix),
7741 i = 0, ii = directives.length; i < ii; i++) {
7742 directive = directives[i];
7743 if (directive.multiElement) {
7744 return true;
7745 }
7746 }
7747 }
7748 return false;
7749 }
7750
7751 /**
7752 * When the element is replaced with HTML template then the new attributes
7753 * on the template need to be merged with the existing attributes in the DOM.
7754 * The desired effect is to have both of the attributes present.
7755 *
7756 * @param {object} dst destination attributes (original DOM)
7757 * @param {object} src source attributes (from the directive template)
7758 */
7759 function mergeTemplateAttributes(dst, src) {
7760 var srcAttr = src.$attr,
7761 dstAttr = dst.$attr,
7762 $element = dst.$$element;
7763
7764 // reapply the old attributes to the new element
7765 forEach(dst, function(value, key) {
7766 if (key.charAt(0) != '$') {
7767 if (src[key] && src[key] !== value) {
7768 value += (key === 'style' ? ';' : ' ') + src[key];
7769 }
7770 dst.$set(key, value, true, srcAttr[key]);
7771 }
7772 });
7773
7774 // copy the new attributes on the old attrs object
7775 forEach(src, function(value, key) {
7776 if (key == 'class') {
7777 safeAddClass($element, value);
7778 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
7779 } else if (key == 'style') {
7780 $element.attr('style', $element.attr('style') + ';' + value);
7781 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
7782 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
7783 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
7784 // have an attribute like "has-own-property" or "data-has-own-property", etc.
7785 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
7786 dst[key] = value;
7787 dstAttr[key] = srcAttr[key];
7788 }
7789 });
7790 }
7791
7792
7793 function compileTemplateUrl(directives, $compileNode, tAttrs,
7794 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
7795 var linkQueue = [],
7796 afterTemplateNodeLinkFn,
7797 afterTemplateChildLinkFn,
7798 beforeTemplateCompileNode = $compileNode[0],
7799 origAsyncDirective = directives.shift(),
7800 // The fact that we have to copy and patch the directive seems wrong!
7801 derivedSyncDirective = extend({}, origAsyncDirective, {
7802 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
7803 }),
7804 templateUrl = (isFunction(origAsyncDirective.templateUrl))
7805 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
7806 : origAsyncDirective.templateUrl,
7807 templateNamespace = origAsyncDirective.templateNamespace;
7808
7809 $compileNode.empty();
7810
7811 $templateRequest($sce.getTrustedResourceUrl(templateUrl))
7812 .then(function(content) {
7813 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
7814
7815 content = denormalizeTemplate(content);
7816
7817 if (origAsyncDirective.replace) {
7818 if (jqLiteIsTextNode(content)) {
7819 $template = [];
7820 } else {
7821 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
7822 }
7823 compileNode = $template[0];
7824
7825 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
7826 throw $compileMinErr('tplrt',
7827 "Template for directive '{0}' must have exactly one root element. {1}",
7828 origAsyncDirective.name, templateUrl);
7829 }
7830
7831 tempTemplateAttrs = {$attr: {}};
7832 replaceWith($rootElement, $compileNode, compileNode);
7833 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
7834
7835 if (isObject(origAsyncDirective.scope)) {
7836 markDirectivesAsIsolate(templateDirectives);
7837 }
7838 directives = templateDirectives.concat(directives);
7839 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
7840 } else {
7841 compileNode = beforeTemplateCompileNode;
7842 $compileNode.html(content);
7843 }
7844
7845 directives.unshift(derivedSyncDirective);
7846
7847 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
7848 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
7849 previousCompileContext);
7850 forEach($rootElement, function(node, i) {
7851 if (node == compileNode) {
7852 $rootElement[i] = $compileNode[0];
7853 }
7854 });
7855 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
7856
7857 while (linkQueue.length) {
7858 var scope = linkQueue.shift(),
7859 beforeTemplateLinkNode = linkQueue.shift(),
7860 linkRootElement = linkQueue.shift(),
7861 boundTranscludeFn = linkQueue.shift(),
7862 linkNode = $compileNode[0];
7863
7864 if (scope.$$destroyed) continue;
7865
7866 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
7867 var oldClasses = beforeTemplateLinkNode.className;
7868
7869 if (!(previousCompileContext.hasElementTranscludeDirective &&
7870 origAsyncDirective.replace)) {
7871 // it was cloned therefore we have to clone as well.
7872 linkNode = jqLiteClone(compileNode);
7873 }
7874 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
7875
7876 // Copy in CSS classes from original node
7877 safeAddClass(jqLite(linkNode), oldClasses);
7878 }
7879 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7880 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
7881 } else {
7882 childBoundTranscludeFn = boundTranscludeFn;
7883 }
7884 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
7885 childBoundTranscludeFn);
7886 }
7887 linkQueue = null;
7888 });
7889
7890 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
7891 var childBoundTranscludeFn = boundTranscludeFn;
7892 if (scope.$$destroyed) return;
7893 if (linkQueue) {
7894 linkQueue.push(scope,
7895 node,
7896 rootElement,
7897 childBoundTranscludeFn);
7898 } else {
7899 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
7900 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
7901 }
7902 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
7903 }
7904 };
7905 }
7906
7907
7908 /**
7909 * Sorting function for bound directives.
7910 */
7911 function byPriority(a, b) {
7912 var diff = b.priority - a.priority;
7913 if (diff !== 0) return diff;
7914 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
7915 return a.index - b.index;
7916 }
7917
7918
7919 function assertNoDuplicate(what, previousDirective, directive, element) {
7920 if (previousDirective) {
7921 throw $compileMinErr('multidir', 'Multiple directives [{0}, {1}] asking for {2} on: {3}',
7922 previousDirective.name, directive.name, what, startingTag(element));
7923 }
7924 }
7925
7926
7927 function addTextInterpolateDirective(directives, text) {
7928 var interpolateFn = $interpolate(text, true);
7929 if (interpolateFn) {
7930 directives.push({
7931 priority: 0,
7932 compile: function textInterpolateCompileFn(templateNode) {
7933 var templateNodeParent = templateNode.parent(),
7934 hasCompileParent = !!templateNodeParent.length;
7935
7936 // When transcluding a template that has bindings in the root
7937 // we don't have a parent and thus need to add the class during linking fn.
7938 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
7939
7940 return function textInterpolateLinkFn(scope, node) {
7941 var parent = node.parent();
7942 if (!hasCompileParent) compile.$$addBindingClass(parent);
7943 compile.$$addBindingInfo(parent, interpolateFn.expressions);
7944 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
7945 node[0].nodeValue = value;
7946 });
7947 };
7948 }
7949 });
7950 }
7951 }
7952
7953
7954 function wrapTemplate(type, template) {
7955 type = lowercase(type || 'html');
7956 switch (type) {
7957 case 'svg':
7958 case 'math':
7959 var wrapper = document.createElement('div');
7960 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
7961 return wrapper.childNodes[0].childNodes;
7962 default:
7963 return template;
7964 }
7965 }
7966
7967
7968 function getTrustedContext(node, attrNormalizedName) {
7969 if (attrNormalizedName == "srcdoc") {
7970 return $sce.HTML;
7971 }
7972 var tag = nodeName_(node);
7973 // maction[xlink:href] can source SVG. It's not limited to <maction>.
7974 if (attrNormalizedName == "xlinkHref" ||
7975 (tag == "form" && attrNormalizedName == "action") ||
7976 (tag != "img" && (attrNormalizedName == "src" ||
7977 attrNormalizedName == "ngSrc"))) {
7978 return $sce.RESOURCE_URL;
7979 }
7980 }
7981
7982
7983 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
7984 var interpolateFn = $interpolate(value, true);
7985
7986 // no interpolation found -> ignore
7987 if (!interpolateFn) return;
7988
7989
7990 if (name === "multiple" && nodeName_(node) === "select") {
7991 throw $compileMinErr("selmulti",
7992 "Binding to the 'multiple' attribute is not supported. Element: {0}",
7993 startingTag(node));
7994 }
7995
7996 directives.push({
7997 priority: 100,
7998 compile: function() {
7999 return {
8000 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8001 var $$observers = (attr.$$observers || (attr.$$observers = {}));
8002
8003 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8004 throw $compileMinErr('nodomevents',
8005 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
8006 "ng- versions (such as ng-click instead of onclick) instead.");
8007 }
8008
8009 // If the attribute was removed, then we are done
8010 if (!attr[name]) {
8011 return;
8012 }
8013
8014 // we need to interpolate again, in case the attribute value has been updated
8015 // (e.g. by another directive's compile function)
8016 interpolateFn = $interpolate(attr[name], true, getTrustedContext(node, name),
8017 ALL_OR_NOTHING_ATTRS[name] || allOrNothing);
8018
8019 // if attribute was updated so that there is no interpolation going on we don't want to
8020 // register any observers
8021 if (!interpolateFn) return;
8022
8023 // initialize attr object so that it's ready in case we need the value for isolate
8024 // scope initialization, otherwise the value would not be available from isolate
8025 // directive's linking fn during linking phase
8026 attr[name] = interpolateFn(scope);
8027
8028 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8029 (attr.$$observers && attr.$$observers[name].$$scope || scope).
8030 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8031 //special case for class attribute addition + removal
8032 //so that class changes can tap into the animation
8033 //hooks provided by the $animate service. Be sure to
8034 //skip animations when the first digest occurs (when
8035 //both the new and the old values are the same) since
8036 //the CSS classes are the non-interpolated values
8037 if (name === 'class' && newValue != oldValue) {
8038 attr.$updateClass(newValue, oldValue);
8039 } else {
8040 attr.$set(name, newValue);
8041 }
8042 });
8043 }
8044 };
8045 }
8046 });
8047 }
8048
8049
8050 /**
8051 * This is a special jqLite.replaceWith, which can replace items which
8052 * have no parents, provided that the containing jqLite collection is provided.
8053 *
8054 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8055 * in the root of the tree.
8056 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8057 * the shell, but replace its DOM node reference.
8058 * @param {Node} newNode The new DOM node.
8059 */
8060 function replaceWith($rootElement, elementsToRemove, newNode) {
8061 var firstElementToRemove = elementsToRemove[0],
8062 removeCount = elementsToRemove.length,
8063 parent = firstElementToRemove.parentNode,
8064 i, ii;
8065
8066 if ($rootElement) {
8067 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8068 if ($rootElement[i] == firstElementToRemove) {
8069 $rootElement[i++] = newNode;
8070 for (var j = i, j2 = j + removeCount - 1,
8071 jj = $rootElement.length;
8072 j < jj; j++, j2++) {
8073 if (j2 < jj) {
8074 $rootElement[j] = $rootElement[j2];
8075 } else {
8076 delete $rootElement[j];
8077 }
8078 }
8079 $rootElement.length -= removeCount - 1;
8080
8081 // If the replaced element is also the jQuery .context then replace it
8082 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8083 // http://api.jquery.com/context/
8084 if ($rootElement.context === firstElementToRemove) {
8085 $rootElement.context = newNode;
8086 }
8087 break;
8088 }
8089 }
8090 }
8091
8092 if (parent) {
8093 parent.replaceChild(newNode, firstElementToRemove);
8094 }
8095
8096 // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8097 var fragment = document.createDocumentFragment();
8098 fragment.appendChild(firstElementToRemove);
8099
8100 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8101 // data here because there's no public interface in jQuery to do that and copying over
8102 // event listeners (which is the main use of private data) wouldn't work anyway.
8103 jqLite(newNode).data(jqLite(firstElementToRemove).data());
8104
8105 // Remove data of the replaced element. We cannot just call .remove()
8106 // on the element it since that would deallocate scope that is needed
8107 // for the new node. Instead, remove the data "manually".
8108 if (!jQuery) {
8109 delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8110 } else {
8111 // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8112 // the replaced element. The cleanData version monkey-patched by Angular would cause
8113 // the scope to be trashed and we do need the very same scope to work with the new
8114 // element. However, we cannot just cache the non-patched version and use it here as
8115 // that would break if another library patches the method after Angular does (one
8116 // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8117 // skipped this one time.
8118 skipDestroyOnNextJQueryCleanData = true;
8119 jQuery.cleanData([firstElementToRemove]);
8120 }
8121
8122 for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8123 var element = elementsToRemove[k];
8124 jqLite(element).remove(); // must do this way to clean up expando
8125 fragment.appendChild(element);
8126 delete elementsToRemove[k];
8127 }
8128
8129 elementsToRemove[0] = newNode;
8130 elementsToRemove.length = 1;
8131 }
8132
8133
8134 function cloneAndAnnotateFn(fn, annotation) {
8135 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8136 }
8137
8138
8139 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8140 try {
8141 linkFn(scope, $element, attrs, controllers, transcludeFn);
8142 } catch (e) {
8143 $exceptionHandler(e, startingTag($element));
8144 }
8145 }
8146 }];
8147}
8148
8149var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
8150/**
8151 * Converts all accepted directives format into proper directive name.
8152 * All of these will become 'myDirective':
8153 * my:Directive
8154 * my-directive
8155 * x-my-directive
8156 * data-my:directive
8157 *
8158 * Also there is special case for Moz prefix starting with upper case letter.
8159 * @param name Name to normalize
8160 */
8161function directiveNormalize(name) {
8162 return camelCase(name.replace(PREFIX_REGEXP, ''));
8163}
8164
8165/**
8166 * @ngdoc type
8167 * @name $compile.directive.Attributes
8168 *
8169 * @description
8170 * A shared object between directive compile / linking functions which contains normalized DOM
8171 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
8172 * needed since all of these are treated as equivalent in Angular:
8173 *
8174 * ```
8175 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
8176 * ```
8177 */
8178
8179/**
8180 * @ngdoc property
8181 * @name $compile.directive.Attributes#$attr
8182 *
8183 * @description
8184 * A map of DOM element attribute names to the normalized name. This is
8185 * needed to do reverse lookup from normalized name back to actual name.
8186 */
8187
8188
8189/**
8190 * @ngdoc method
8191 * @name $compile.directive.Attributes#$set
8192 * @kind function
8193 *
8194 * @description
8195 * Set DOM element attribute value.
8196 *
8197 *
8198 * @param {string} name Normalized element attribute name of the property to modify. The name is
8199 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
8200 * property to the original name.
8201 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
8202 */
8203
8204
8205
8206/**
8207 * Closure compiler type information
8208 */
8209
8210function nodesetLinkingFn(
8211 /* angular.Scope */ scope,
8212 /* NodeList */ nodeList,
8213 /* Element */ rootElement,
8214 /* function(Function) */ boundTranscludeFn
8215) {}
8216
8217function directiveLinkingFn(
8218 /* nodesetLinkingFn */ nodesetLinkingFn,
8219 /* angular.Scope */ scope,
8220 /* Node */ node,
8221 /* Element */ rootElement,
8222 /* function(Function) */ boundTranscludeFn
8223) {}
8224
8225function tokenDifference(str1, str2) {
8226 var values = '',
8227 tokens1 = str1.split(/\s+/),
8228 tokens2 = str2.split(/\s+/);
8229
8230 outer:
8231 for (var i = 0; i < tokens1.length; i++) {
8232 var token = tokens1[i];
8233 for (var j = 0; j < tokens2.length; j++) {
8234 if (token == tokens2[j]) continue outer;
8235 }
8236 values += (values.length > 0 ? ' ' : '') + token;
8237 }
8238 return values;
8239}
8240
8241function removeComments(jqNodes) {
8242 jqNodes = jqLite(jqNodes);
8243 var i = jqNodes.length;
8244
8245 if (i <= 1) {
8246 return jqNodes;
8247 }
8248
8249 while (i--) {
8250 var node = jqNodes[i];
8251 if (node.nodeType === NODE_TYPE_COMMENT) {
8252 splice.call(jqNodes, i, 1);
8253 }
8254 }
8255 return jqNodes;
8256}
8257
8258/**
8259 * @ngdoc provider
8260 * @name $controllerProvider
8261 * @description
8262 * The {@link ng.$controller $controller service} is used by Angular to create new
8263 * controllers.
8264 *
8265 * This provider allows controller registration via the
8266 * {@link ng.$controllerProvider#register register} method.
8267 */
8268function $ControllerProvider() {
8269 var controllers = {},
8270 globals = false,
8271 CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
8272
8273
8274 /**
8275 * @ngdoc method
8276 * @name $controllerProvider#register
8277 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
8278 * the names and the values are the constructors.
8279 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
8280 * annotations in the array notation).
8281 */
8282 this.register = function(name, constructor) {
8283 assertNotHasOwnProperty(name, 'controller');
8284 if (isObject(name)) {
8285 extend(controllers, name);
8286 } else {
8287 controllers[name] = constructor;
8288 }
8289 };
8290
8291 /**
8292 * @ngdoc method
8293 * @name $controllerProvider#allowGlobals
8294 * @description If called, allows `$controller` to find controller constructors on `window`
8295 */
8296 this.allowGlobals = function() {
8297 globals = true;
8298 };
8299
8300
8301 this.$get = ['$injector', '$window', function($injector, $window) {
8302
8303 /**
8304 * @ngdoc service
8305 * @name $controller
8306 * @requires $injector
8307 *
8308 * @param {Function|string} constructor If called with a function then it's considered to be the
8309 * controller constructor function. Otherwise it's considered to be a string which is used
8310 * to retrieve the controller constructor using the following steps:
8311 *
8312 * * check if a controller with given name is registered via `$controllerProvider`
8313 * * check if evaluating the string on the current scope returns a constructor
8314 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
8315 * `window` object (not recommended)
8316 *
8317 * The string can use the `controller as property` syntax, where the controller instance is published
8318 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
8319 * to work correctly.
8320 *
8321 * @param {Object} locals Injection locals for Controller.
8322 * @return {Object} Instance of given controller.
8323 *
8324 * @description
8325 * `$controller` service is responsible for instantiating controllers.
8326 *
8327 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
8328 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
8329 */
8330 return function(expression, locals, later, ident) {
8331 // PRIVATE API:
8332 // param `later` --- indicates that the controller's constructor is invoked at a later time.
8333 // If true, $controller will allocate the object with the correct
8334 // prototype chain, but will not invoke the controller until a returned
8335 // callback is invoked.
8336 // param `ident` --- An optional label which overrides the label parsed from the controller
8337 // expression, if any.
8338 var instance, match, constructor, identifier;
8339 later = later === true;
8340 if (ident && isString(ident)) {
8341 identifier = ident;
8342 }
8343
8344 if (isString(expression)) {
8345 match = expression.match(CNTRL_REG),
8346 constructor = match[1],
8347 identifier = identifier || match[3];
8348 expression = controllers.hasOwnProperty(constructor)
8349 ? controllers[constructor]
8350 : getter(locals.$scope, constructor, true) ||
8351 (globals ? getter($window, constructor, true) : undefined);
8352
8353 assertArgFn(expression, constructor, true);
8354 }
8355
8356 if (later) {
8357 // Instantiate controller later:
8358 // This machinery is used to create an instance of the object before calling the
8359 // controller's constructor itself.
8360 //
8361 // This allows properties to be added to the controller before the constructor is
8362 // invoked. Primarily, this is used for isolate scope bindings in $compile.
8363 //
8364 // This feature is not intended for use by applications, and is thus not documented
8365 // publicly.
8366 // Object creation: http://jsperf.com/create-constructor/2
8367 var controllerPrototype = (isArray(expression) ?
8368 expression[expression.length - 1] : expression).prototype;
8369 instance = Object.create(controllerPrototype);
8370
8371 if (identifier) {
8372 addIdentifier(locals, identifier, instance, constructor || expression.name);
8373 }
8374
8375 return extend(function() {
8376 $injector.invoke(expression, instance, locals, constructor);
8377 return instance;
8378 }, {
8379 instance: instance,
8380 identifier: identifier
8381 });
8382 }
8383
8384 instance = $injector.instantiate(expression, locals, constructor);
8385
8386 if (identifier) {
8387 addIdentifier(locals, identifier, instance, constructor || expression.name);
8388 }
8389
8390 return instance;
8391 };
8392
8393 function addIdentifier(locals, identifier, instance, name) {
8394 if (!(locals && isObject(locals.$scope))) {
8395 throw minErr('$controller')('noscp',
8396 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
8397 name, identifier);
8398 }
8399
8400 locals.$scope[identifier] = instance;
8401 }
8402 }];
8403}
8404
8405/**
8406 * @ngdoc service
8407 * @name $document
8408 * @requires $window
8409 *
8410 * @description
8411 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
8412 *
8413 * @example
8414 <example module="documentExample">
8415 <file name="index.html">
8416 <div ng-controller="ExampleController">
8417 <p>$document title: <b ng-bind="title"></b></p>
8418 <p>window.document title: <b ng-bind="windowTitle"></b></p>
8419 </div>
8420 </file>
8421 <file name="script.js">
8422 angular.module('documentExample', [])
8423 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
8424 $scope.title = $document[0].title;
8425 $scope.windowTitle = angular.element(window.document)[0].title;
8426 }]);
8427 </file>
8428 </example>
8429 */
8430function $DocumentProvider() {
8431 this.$get = ['$window', function(window) {
8432 return jqLite(window.document);
8433 }];
8434}
8435
8436/**
8437 * @ngdoc service
8438 * @name $exceptionHandler
8439 * @requires ng.$log
8440 *
8441 * @description
8442 * Any uncaught exception in angular expressions is delegated to this service.
8443 * The default implementation simply delegates to `$log.error` which logs it into
8444 * the browser console.
8445 *
8446 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
8447 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
8448 *
8449 * ## Example:
8450 *
8451 * ```js
8452 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
8453 * return function(exception, cause) {
8454 * exception.message += ' (caused by "' + cause + '")';
8455 * throw exception;
8456 * };
8457 * });
8458 * ```
8459 *
8460 * This example will override the normal action of `$exceptionHandler`, to make angular
8461 * exceptions fail hard when they happen, instead of just logging to the console.
8462 *
8463 * <hr />
8464 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
8465 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
8466 * (unless executed during a digest).
8467 *
8468 * If you wish, you can manually delegate exceptions, e.g.
8469 * `try { ... } catch(e) { $exceptionHandler(e); }`
8470 *
8471 * @param {Error} exception Exception associated with the error.
8472 * @param {string=} cause optional information about the context in which
8473 * the error was thrown.
8474 *
8475 */
8476function $ExceptionHandlerProvider() {
8477 this.$get = ['$log', function($log) {
8478 return function(exception, cause) {
8479 $log.error.apply($log, arguments);
8480 };
8481 }];
8482}
8483
8484var APPLICATION_JSON = 'application/json';
8485var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
8486var JSON_START = /^\s*(\[|\{[^\{])/;
8487var JSON_END = /[\}\]]\s*$/;
8488var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
8489
8490function defaultHttpResponseTransform(data, headers) {
8491 if (isString(data)) {
8492 // strip json vulnerability protection prefix
8493 data = data.replace(JSON_PROTECTION_PREFIX, '');
8494 var contentType = headers('Content-Type');
8495 if ((contentType && contentType.indexOf(APPLICATION_JSON) === 0 && data.trim()) ||
8496 (JSON_START.test(data) && JSON_END.test(data))) {
8497 data = fromJson(data);
8498 }
8499 }
8500 return data;
8501}
8502
8503/**
8504 * Parse headers into key value object
8505 *
8506 * @param {string} headers Raw headers as a string
8507 * @returns {Object} Parsed headers as key value object
8508 */
8509function parseHeaders(headers) {
8510 var parsed = createMap(), key, val, i;
8511
8512 if (!headers) return parsed;
8513
8514 forEach(headers.split('\n'), function(line) {
8515 i = line.indexOf(':');
8516 key = lowercase(trim(line.substr(0, i)));
8517 val = trim(line.substr(i + 1));
8518
8519 if (key) {
8520 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
8521 }
8522 });
8523
8524 return parsed;
8525}
8526
8527
8528/**
8529 * Returns a function that provides access to parsed headers.
8530 *
8531 * Headers are lazy parsed when first requested.
8532 * @see parseHeaders
8533 *
8534 * @param {(string|Object)} headers Headers to provide access to.
8535 * @returns {function(string=)} Returns a getter function which if called with:
8536 *
8537 * - if called with single an argument returns a single header value or null
8538 * - if called with no arguments returns an object containing all headers.
8539 */
8540function headersGetter(headers) {
8541 var headersObj = isObject(headers) ? headers : undefined;
8542
8543 return function(name) {
8544 if (!headersObj) headersObj = parseHeaders(headers);
8545
8546 if (name) {
8547 var value = headersObj[lowercase(name)];
8548 if (value === void 0) {
8549 value = null;
8550 }
8551 return value;
8552 }
8553
8554 return headersObj;
8555 };
8556}
8557
8558
8559/**
8560 * Chain all given functions
8561 *
8562 * This function is used for both request and response transforming
8563 *
8564 * @param {*} data Data to transform.
8565 * @param {function(string=)} headers Http headers getter fn.
8566 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
8567 * @returns {*} Transformed data.
8568 */
8569function transformData(data, headers, fns) {
8570 if (isFunction(fns))
8571 return fns(data, headers);
8572
8573 forEach(fns, function(fn) {
8574 data = fn(data, headers);
8575 });
8576
8577 return data;
8578}
8579
8580
8581function isSuccess(status) {
8582 return 200 <= status && status < 300;
8583}
8584
8585
8586/**
8587 * @ngdoc provider
8588 * @name $httpProvider
8589 * @description
8590 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
8591 * */
8592function $HttpProvider() {
8593 /**
8594 * @ngdoc property
8595 * @name $httpProvider#defaults
8596 * @description
8597 *
8598 * Object containing default values for all {@link ng.$http $http} requests.
8599 *
8600 * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
8601 * that will provide the cache for all requests who set their `cache` property to `true`.
8602 * If you set the `default.cache = false` then only requests that specify their own custom
8603 * cache object will be cached. See {@link $http#caching $http Caching} for more information.
8604 *
8605 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
8606 * Defaults value is `'XSRF-TOKEN'`.
8607 *
8608 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
8609 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
8610 *
8611 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
8612 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
8613 * setting default headers.
8614 * - **`defaults.headers.common`**
8615 * - **`defaults.headers.post`**
8616 * - **`defaults.headers.put`**
8617 * - **`defaults.headers.patch`**
8618 *
8619 **/
8620 var defaults = this.defaults = {
8621 // transform incoming response data
8622 transformResponse: [defaultHttpResponseTransform],
8623
8624 // transform outgoing request data
8625 transformRequest: [function(d) {
8626 return isObject(d) && !isFile(d) && !isBlob(d) ? toJson(d) : d;
8627 }],
8628
8629 // default headers
8630 headers: {
8631 common: {
8632 'Accept': 'application/json, text/plain, */*'
8633 },
8634 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8635 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
8636 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
8637 },
8638
8639 xsrfCookieName: 'XSRF-TOKEN',
8640 xsrfHeaderName: 'X-XSRF-TOKEN'
8641 };
8642
8643 var useApplyAsync = false;
8644 /**
8645 * @ngdoc method
8646 * @name $httpProvider#useApplyAsync
8647 * @description
8648 *
8649 * Configure $http service to combine processing of multiple http responses received at around
8650 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
8651 * significant performance improvement for bigger applications that make many HTTP requests
8652 * concurrently (common during application bootstrap).
8653 *
8654 * Defaults to false. If no value is specifed, returns the current configured value.
8655 *
8656 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
8657 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
8658 * to load and share the same digest cycle.
8659 *
8660 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
8661 * otherwise, returns the current configured value.
8662 **/
8663 this.useApplyAsync = function(value) {
8664 if (isDefined(value)) {
8665 useApplyAsync = !!value;
8666 return this;
8667 }
8668 return useApplyAsync;
8669 };
8670
8671 /**
8672 * @ngdoc property
8673 * @name $httpProvider#interceptors
8674 * @description
8675 *
8676 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
8677 * pre-processing of request or postprocessing of responses.
8678 *
8679 * These service factories are ordered by request, i.e. they are applied in the same order as the
8680 * array, on request, but reverse order, on response.
8681 *
8682 * {@link ng.$http#interceptors Interceptors detailed info}
8683 **/
8684 var interceptorFactories = this.interceptors = [];
8685
8686 this.$get = ['$httpBackend', '$browser', '$cacheFactory', '$rootScope', '$q', '$injector',
8687 function($httpBackend, $browser, $cacheFactory, $rootScope, $q, $injector) {
8688
8689 var defaultCache = $cacheFactory('$http');
8690
8691 /**
8692 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
8693 * The reversal is needed so that we can build up the interception chain around the
8694 * server request.
8695 */
8696 var reversedInterceptors = [];
8697
8698 forEach(interceptorFactories, function(interceptorFactory) {
8699 reversedInterceptors.unshift(isString(interceptorFactory)
8700 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
8701 });
8702
8703 /**
8704 * @ngdoc service
8705 * @kind function
8706 * @name $http
8707 * @requires ng.$httpBackend
8708 * @requires $cacheFactory
8709 * @requires $rootScope
8710 * @requires $q
8711 * @requires $injector
8712 *
8713 * @description
8714 * The `$http` service is a core Angular service that facilitates communication with the remote
8715 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
8716 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
8717 *
8718 * For unit testing applications that use `$http` service, see
8719 * {@link ngMock.$httpBackend $httpBackend mock}.
8720 *
8721 * For a higher level of abstraction, please check out the {@link ngResource.$resource
8722 * $resource} service.
8723 *
8724 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
8725 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
8726 * it is important to familiarize yourself with these APIs and the guarantees they provide.
8727 *
8728 *
8729 * ## General usage
8730 * The `$http` service is a function which takes a single argument — a configuration object —
8731 * that is used to generate an HTTP request and returns a {@link ng.$q promise}
8732 * with two $http specific methods: `success` and `error`.
8733 *
8734 * ```js
8735 * // Simple GET request example :
8736 * $http.get('/someUrl').
8737 * success(function(data, status, headers, config) {
8738 * // this callback will be called asynchronously
8739 * // when the response is available
8740 * }).
8741 * error(function(data, status, headers, config) {
8742 * // called asynchronously if an error occurs
8743 * // or server returns response with an error status.
8744 * });
8745 * ```
8746 *
8747 * ```js
8748 * // Simple POST request example (passing data) :
8749 * $http.post('/someUrl', {msg:'hello word!'}).
8750 * success(function(data, status, headers, config) {
8751 * // this callback will be called asynchronously
8752 * // when the response is available
8753 * }).
8754 * error(function(data, status, headers, config) {
8755 * // called asynchronously if an error occurs
8756 * // or server returns response with an error status.
8757 * });
8758 * ```
8759 *
8760 *
8761 * Since the returned value of calling the $http function is a `promise`, you can also use
8762 * the `then` method to register callbacks, and these callbacks will receive a single argument –
8763 * an object representing the response. See the API signature and type info below for more
8764 * details.
8765 *
8766 * A response status code between 200 and 299 is considered a success status and
8767 * will result in the success callback being called. Note that if the response is a redirect,
8768 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
8769 * called for such responses.
8770 *
8771 * ## Writing Unit Tests that use $http
8772 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
8773 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
8774 * request using trained responses.
8775 *
8776 * ```
8777 * $httpBackend.expectGET(...);
8778 * $http.get(...);
8779 * $httpBackend.flush();
8780 * ```
8781 *
8782 * ## Shortcut methods
8783 *
8784 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
8785 * request data must be passed in for POST/PUT requests.
8786 *
8787 * ```js
8788 * $http.get('/someUrl').success(successCallback);
8789 * $http.post('/someUrl', data).success(successCallback);
8790 * ```
8791 *
8792 * Complete list of shortcut methods:
8793 *
8794 * - {@link ng.$http#get $http.get}
8795 * - {@link ng.$http#head $http.head}
8796 * - {@link ng.$http#post $http.post}
8797 * - {@link ng.$http#put $http.put}
8798 * - {@link ng.$http#delete $http.delete}
8799 * - {@link ng.$http#jsonp $http.jsonp}
8800 * - {@link ng.$http#patch $http.patch}
8801 *
8802 *
8803 * ## Setting HTTP Headers
8804 *
8805 * The $http service will automatically add certain HTTP headers to all requests. These defaults
8806 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
8807 * object, which currently contains this default configuration:
8808 *
8809 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
8810 * - `Accept: application/json, text/plain, * / *`
8811 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
8812 * - `Content-Type: application/json`
8813 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
8814 * - `Content-Type: application/json`
8815 *
8816 * To add or overwrite these defaults, simply add or remove a property from these configuration
8817 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
8818 * with the lowercased HTTP method name as the key, e.g.
8819 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }.
8820 *
8821 * The defaults can also be set at runtime via the `$http.defaults` object in the same
8822 * fashion. For example:
8823 *
8824 * ```
8825 * module.run(function($http) {
8826 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
8827 * });
8828 * ```
8829 *
8830 * In addition, you can supply a `headers` property in the config object passed when
8831 * calling `$http(config)`, which overrides the defaults without changing them globally.
8832 *
8833 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
8834 * Use the `headers` property, setting the desired header to `undefined`. For example:
8835 *
8836 * ```js
8837 * var req = {
8838 * method: 'POST',
8839 * url: 'http://example.com',
8840 * headers: {
8841 * 'Content-Type': undefined
8842 * },
8843 * data: { test: 'test' },
8844 * }
8845 *
8846 * $http(req).success(function(){...}).error(function(){...});
8847 * ```
8848 *
8849 * ## Transforming Requests and Responses
8850 *
8851 * Both requests and responses can be transformed using transformation functions: `transformRequest`
8852 * and `transformResponse`. These properties can be a single function that returns
8853 * the transformed value (`{function(data, headersGetter)`) or an array of such transformation functions,
8854 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
8855 *
8856 * ### Default Transformations
8857 *
8858 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
8859 * `defaults.transformResponse` properties. If a request does not provide its own transformations
8860 * then these will be applied.
8861 *
8862 * You can augment or replace the default transformations by modifying these properties by adding to or
8863 * replacing the array.
8864 *
8865 * Angular provides the following default transformations:
8866 *
8867 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
8868 *
8869 * - If the `data` property of the request configuration object contains an object, serialize it
8870 * into JSON format.
8871 *
8872 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
8873 *
8874 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
8875 * - If JSON response is detected, deserialize it using a JSON parser.
8876 *
8877 *
8878 * ### Overriding the Default Transformations Per Request
8879 *
8880 * If you wish override the request/response transformations only for a single request then provide
8881 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
8882 * into `$http`.
8883 *
8884 * Note that if you provide these properties on the config object the default transformations will be
8885 * overwritten. If you wish to augment the default transformations then you must include them in your
8886 * local transformation array.
8887 *
8888 * The following code demonstrates adding a new response transformation to be run after the default response
8889 * transformations have been run.
8890 *
8891 * ```js
8892 * function appendTransform(defaults, transform) {
8893 *
8894 * // We can't guarantee that the default transformation is an array
8895 * defaults = angular.isArray(defaults) ? defaults : [defaults];
8896 *
8897 * // Append the new transformation to the defaults
8898 * return defaults.concat(transform);
8899 * }
8900 *
8901 * $http({
8902 * url: '...',
8903 * method: 'GET',
8904 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
8905 * return doTransform(value);
8906 * })
8907 * });
8908 * ```
8909 *
8910 *
8911 * ## Caching
8912 *
8913 * To enable caching, set the request configuration `cache` property to `true` (to use default
8914 * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
8915 * When the cache is enabled, `$http` stores the response from the server in the specified
8916 * cache. The next time the same request is made, the response is served from the cache without
8917 * sending a request to the server.
8918 *
8919 * Note that even if the response is served from cache, delivery of the data is asynchronous in
8920 * the same way that real requests are.
8921 *
8922 * If there are multiple GET requests for the same URL that should be cached using the same
8923 * cache, but the cache is not populated yet, only one request to the server will be made and
8924 * the remaining requests will be fulfilled using the response from the first request.
8925 *
8926 * You can change the default cache to a new object (built with
8927 * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
8928 * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
8929 * their `cache` property to `true` will now use this cache object.
8930 *
8931 * If you set the default cache to `false` then only requests that specify their own custom
8932 * cache object will be cached.
8933 *
8934 * ## Interceptors
8935 *
8936 * Before you start creating interceptors, be sure to understand the
8937 * {@link ng.$q $q and deferred/promise APIs}.
8938 *
8939 * For purposes of global error handling, authentication, or any kind of synchronous or
8940 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
8941 * able to intercept requests before they are handed to the server and
8942 * responses before they are handed over to the application code that
8943 * initiated these requests. The interceptors leverage the {@link ng.$q
8944 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
8945 *
8946 * The interceptors are service factories that are registered with the `$httpProvider` by
8947 * adding them to the `$httpProvider.interceptors` array. The factory is called and
8948 * injected with dependencies (if specified) and returns the interceptor.
8949 *
8950 * There are two kinds of interceptors (and two kinds of rejection interceptors):
8951 *
8952 * * `request`: interceptors get called with a http `config` object. The function is free to
8953 * modify the `config` object or create a new one. The function needs to return the `config`
8954 * object directly, or a promise containing the `config` or a new `config` object.
8955 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
8956 * resolved with a rejection.
8957 * * `response`: interceptors get called with http `response` object. The function is free to
8958 * modify the `response` object or create a new one. The function needs to return the `response`
8959 * object directly, or as a promise containing the `response` or a new `response` object.
8960 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
8961 * resolved with a rejection.
8962 *
8963 *
8964 * ```js
8965 * // register the interceptor as a service
8966 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
8967 * return {
8968 * // optional method
8969 * 'request': function(config) {
8970 * // do something on success
8971 * return config;
8972 * },
8973 *
8974 * // optional method
8975 * 'requestError': function(rejection) {
8976 * // do something on error
8977 * if (canRecover(rejection)) {
8978 * return responseOrNewPromise
8979 * }
8980 * return $q.reject(rejection);
8981 * },
8982 *
8983 *
8984 *
8985 * // optional method
8986 * 'response': function(response) {
8987 * // do something on success
8988 * return response;
8989 * },
8990 *
8991 * // optional method
8992 * 'responseError': function(rejection) {
8993 * // do something on error
8994 * if (canRecover(rejection)) {
8995 * return responseOrNewPromise
8996 * }
8997 * return $q.reject(rejection);
8998 * }
8999 * };
9000 * });
9001 *
9002 * $httpProvider.interceptors.push('myHttpInterceptor');
9003 *
9004 *
9005 * // alternatively, register the interceptor via an anonymous factory
9006 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
9007 * return {
9008 * 'request': function(config) {
9009 * // same as above
9010 * },
9011 *
9012 * 'response': function(response) {
9013 * // same as above
9014 * }
9015 * };
9016 * });
9017 * ```
9018 *
9019 * ## Security Considerations
9020 *
9021 * When designing web applications, consider security threats from:
9022 *
9023 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9024 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
9025 *
9026 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
9027 * pre-configured with strategies that address these issues, but for this to work backend server
9028 * cooperation is required.
9029 *
9030 * ### JSON Vulnerability Protection
9031 *
9032 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
9033 * allows third party website to turn your JSON resource URL into
9034 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
9035 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
9036 * Angular will automatically strip the prefix before processing it as JSON.
9037 *
9038 * For example if your server needs to return:
9039 * ```js
9040 * ['one','two']
9041 * ```
9042 *
9043 * which is vulnerable to attack, your server can return:
9044 * ```js
9045 * )]}',
9046 * ['one','two']
9047 * ```
9048 *
9049 * Angular will strip the prefix, before processing the JSON.
9050 *
9051 *
9052 * ### Cross Site Request Forgery (XSRF) Protection
9053 *
9054 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
9055 * an unauthorized site can gain your user's private data. Angular provides a mechanism
9056 * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
9057 * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
9058 * JavaScript that runs on your domain could read the cookie, your server can be assured that
9059 * the XHR came from JavaScript running on your domain. The header will not be set for
9060 * cross-domain requests.
9061 *
9062 * To take advantage of this, your server needs to set a token in a JavaScript readable session
9063 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
9064 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
9065 * that only JavaScript running on your domain could have sent the request. The token must be
9066 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
9067 * making up its own tokens). We recommend that the token is a digest of your site's
9068 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
9069 * for added security.
9070 *
9071 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
9072 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
9073 * or the per-request config object.
9074 *
9075 *
9076 * @param {object} config Object describing the request to be made and how it should be
9077 * processed. The object has following properties:
9078 *
9079 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
9080 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
9081 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be turned
9082 * to `?key1=value1&key2=value2` after the url. If the value is not a string, it will be
9083 * JSONified.
9084 * - **data** – `{string|Object}` – Data to be sent as the request message data.
9085 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
9086 * HTTP headers to send to the server. If the return value of a function is null, the
9087 * header will not be sent.
9088 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
9089 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
9090 * - **transformRequest** –
9091 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
9092 * transform function or an array of such functions. The transform function takes the http
9093 * request body and headers and returns its transformed (typically serialized) version.
9094 * See {@link ng.$http#overriding-the-default-transformations-per-request
9095 * Overriding the Default Transformations}
9096 * - **transformResponse** –
9097 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
9098 * transform function or an array of such functions. The transform function takes the http
9099 * response body and headers and returns its transformed (typically deserialized) version.
9100 * See {@link ng.$http#overriding-the-default-transformations-per-request
9101 * Overriding the Default Transformations}
9102 * - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
9103 * GET request, otherwise if a cache instance built with
9104 * {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
9105 * caching.
9106 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
9107 * that should abort the request when resolved.
9108 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
9109 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
9110 * for more information.
9111 * - **responseType** - `{string}` - see
9112 * [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
9113 *
9114 * @returns {HttpPromise} Returns a {@link ng.$q promise} object with the
9115 * standard `then` method and two http specific methods: `success` and `error`. The `then`
9116 * method takes two arguments a success and an error callback which will be called with a
9117 * response object. The `success` and `error` methods take a single argument - a function that
9118 * will be called when the request succeeds or fails respectively. The arguments passed into
9119 * these functions are destructured representation of the response object passed into the
9120 * `then` method. The response object has these properties:
9121 *
9122 * - **data** – `{string|Object}` – The response body transformed with the transform
9123 * functions.
9124 * - **status** – `{number}` – HTTP status code of the response.
9125 * - **headers** – `{function([headerName])}` – Header getter function.
9126 * - **config** – `{Object}` – The configuration object that was used to generate the request.
9127 * - **statusText** – `{string}` – HTTP status text of the response.
9128 *
9129 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
9130 * requests. This is primarily meant to be used for debugging purposes.
9131 *
9132 *
9133 * @example
9134<example module="httpExample">
9135<file name="index.html">
9136 <div ng-controller="FetchController">
9137 <select ng-model="method">
9138 <option>GET</option>
9139 <option>JSONP</option>
9140 </select>
9141 <input type="text" ng-model="url" size="80"/>
9142 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
9143 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
9144 <button id="samplejsonpbtn"
9145 ng-click="updateModel('JSONP',
9146 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
9147 Sample JSONP
9148 </button>
9149 <button id="invalidjsonpbtn"
9150 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
9151 Invalid JSONP
9152 </button>
9153 <pre>http status code: {{status}}</pre>
9154 <pre>http response data: {{data}}</pre>
9155 </div>
9156</file>
9157<file name="script.js">
9158 angular.module('httpExample', [])
9159 .controller('FetchController', ['$scope', '$http', '$templateCache',
9160 function($scope, $http, $templateCache) {
9161 $scope.method = 'GET';
9162 $scope.url = 'http-hello.html';
9163
9164 $scope.fetch = function() {
9165 $scope.code = null;
9166 $scope.response = null;
9167
9168 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
9169 success(function(data, status) {
9170 $scope.status = status;
9171 $scope.data = data;
9172 }).
9173 error(function(data, status) {
9174 $scope.data = data || "Request failed";
9175 $scope.status = status;
9176 });
9177 };
9178
9179 $scope.updateModel = function(method, url) {
9180 $scope.method = method;
9181 $scope.url = url;
9182 };
9183 }]);
9184</file>
9185<file name="http-hello.html">
9186 Hello, $http!
9187</file>
9188<file name="protractor.js" type="protractor">
9189 var status = element(by.binding('status'));
9190 var data = element(by.binding('data'));
9191 var fetchBtn = element(by.id('fetchbtn'));
9192 var sampleGetBtn = element(by.id('samplegetbtn'));
9193 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
9194 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
9195
9196 it('should make an xhr GET request', function() {
9197 sampleGetBtn.click();
9198 fetchBtn.click();
9199 expect(status.getText()).toMatch('200');
9200 expect(data.getText()).toMatch(/Hello, \$http!/);
9201 });
9202
9203// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
9204// it('should make a JSONP request to angularjs.org', function() {
9205// sampleJsonpBtn.click();
9206// fetchBtn.click();
9207// expect(status.getText()).toMatch('200');
9208// expect(data.getText()).toMatch(/Super Hero!/);
9209// });
9210
9211 it('should make JSONP request to invalid URL and invoke the error handler',
9212 function() {
9213 invalidJsonpBtn.click();
9214 fetchBtn.click();
9215 expect(status.getText()).toMatch('0');
9216 expect(data.getText()).toMatch('Request failed');
9217 });
9218</file>
9219</example>
9220 */
9221 function $http(requestConfig) {
9222 var config = {
9223 method: 'get',
9224 transformRequest: defaults.transformRequest,
9225 transformResponse: defaults.transformResponse
9226 };
9227 var headers = mergeHeaders(requestConfig);
9228
9229 if (!angular.isObject(requestConfig)) {
9230 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
9231 }
9232
9233 extend(config, requestConfig);
9234 config.headers = headers;
9235 config.method = uppercase(config.method);
9236
9237 var serverRequest = function(config) {
9238 headers = config.headers;
9239 var reqData = transformData(config.data, headersGetter(headers), config.transformRequest);
9240
9241 // strip content-type if data is undefined
9242 if (isUndefined(reqData)) {
9243 forEach(headers, function(value, header) {
9244 if (lowercase(header) === 'content-type') {
9245 delete headers[header];
9246 }
9247 });
9248 }
9249
9250 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
9251 config.withCredentials = defaults.withCredentials;
9252 }
9253
9254 // send request
9255 return sendReq(config, reqData, headers).then(transformResponse, transformResponse);
9256 };
9257
9258 var chain = [serverRequest, undefined];
9259 var promise = $q.when(config);
9260
9261 // apply interceptors
9262 forEach(reversedInterceptors, function(interceptor) {
9263 if (interceptor.request || interceptor.requestError) {
9264 chain.unshift(interceptor.request, interceptor.requestError);
9265 }
9266 if (interceptor.response || interceptor.responseError) {
9267 chain.push(interceptor.response, interceptor.responseError);
9268 }
9269 });
9270
9271 while (chain.length) {
9272 var thenFn = chain.shift();
9273 var rejectFn = chain.shift();
9274
9275 promise = promise.then(thenFn, rejectFn);
9276 }
9277
9278 promise.success = function(fn) {
9279 promise.then(function(response) {
9280 fn(response.data, response.status, response.headers, config);
9281 });
9282 return promise;
9283 };
9284
9285 promise.error = function(fn) {
9286 promise.then(null, function(response) {
9287 fn(response.data, response.status, response.headers, config);
9288 });
9289 return promise;
9290 };
9291
9292 return promise;
9293
9294 function transformResponse(response) {
9295 // make a copy since the response must be cacheable
9296 var resp = extend({}, response);
9297 if (!response.data) {
9298 resp.data = response.data;
9299 } else {
9300 resp.data = transformData(response.data, response.headers, config.transformResponse);
9301 }
9302 return (isSuccess(response.status))
9303 ? resp
9304 : $q.reject(resp);
9305 }
9306
9307 function mergeHeaders(config) {
9308 var defHeaders = defaults.headers,
9309 reqHeaders = extend({}, config.headers),
9310 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
9311
9312 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
9313
9314 // using for-in instead of forEach to avoid unecessary iteration after header has been found
9315 defaultHeadersIteration:
9316 for (defHeaderName in defHeaders) {
9317 lowercaseDefHeaderName = lowercase(defHeaderName);
9318
9319 for (reqHeaderName in reqHeaders) {
9320 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
9321 continue defaultHeadersIteration;
9322 }
9323 }
9324
9325 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
9326 }
9327
9328 // execute if header value is a function for merged headers
9329 execHeaders(reqHeaders);
9330 return reqHeaders;
9331
9332 function execHeaders(headers) {
9333 var headerContent;
9334
9335 forEach(headers, function(headerFn, header) {
9336 if (isFunction(headerFn)) {
9337 headerContent = headerFn();
9338 if (headerContent != null) {
9339 headers[header] = headerContent;
9340 } else {
9341 delete headers[header];
9342 }
9343 }
9344 });
9345 }
9346 }
9347 }
9348
9349 $http.pendingRequests = [];
9350
9351 /**
9352 * @ngdoc method
9353 * @name $http#get
9354 *
9355 * @description
9356 * Shortcut method to perform `GET` request.
9357 *
9358 * @param {string} url Relative or absolute URL specifying the destination of the request
9359 * @param {Object=} config Optional configuration object
9360 * @returns {HttpPromise} Future object
9361 */
9362
9363 /**
9364 * @ngdoc method
9365 * @name $http#delete
9366 *
9367 * @description
9368 * Shortcut method to perform `DELETE` request.
9369 *
9370 * @param {string} url Relative or absolute URL specifying the destination of the request
9371 * @param {Object=} config Optional configuration object
9372 * @returns {HttpPromise} Future object
9373 */
9374
9375 /**
9376 * @ngdoc method
9377 * @name $http#head
9378 *
9379 * @description
9380 * Shortcut method to perform `HEAD` request.
9381 *
9382 * @param {string} url Relative or absolute URL specifying the destination of the request
9383 * @param {Object=} config Optional configuration object
9384 * @returns {HttpPromise} Future object
9385 */
9386
9387 /**
9388 * @ngdoc method
9389 * @name $http#jsonp
9390 *
9391 * @description
9392 * Shortcut method to perform `JSONP` request.
9393 *
9394 * @param {string} url Relative or absolute URL specifying the destination of the request.
9395 * The name of the callback should be the string `JSON_CALLBACK`.
9396 * @param {Object=} config Optional configuration object
9397 * @returns {HttpPromise} Future object
9398 */
9399 createShortMethods('get', 'delete', 'head', 'jsonp');
9400
9401 /**
9402 * @ngdoc method
9403 * @name $http#post
9404 *
9405 * @description
9406 * Shortcut method to perform `POST` request.
9407 *
9408 * @param {string} url Relative or absolute URL specifying the destination of the request
9409 * @param {*} data Request content
9410 * @param {Object=} config Optional configuration object
9411 * @returns {HttpPromise} Future object
9412 */
9413
9414 /**
9415 * @ngdoc method
9416 * @name $http#put
9417 *
9418 * @description
9419 * Shortcut method to perform `PUT` request.
9420 *
9421 * @param {string} url Relative or absolute URL specifying the destination of the request
9422 * @param {*} data Request content
9423 * @param {Object=} config Optional configuration object
9424 * @returns {HttpPromise} Future object
9425 */
9426
9427 /**
9428 * @ngdoc method
9429 * @name $http#patch
9430 *
9431 * @description
9432 * Shortcut method to perform `PATCH` request.
9433 *
9434 * @param {string} url Relative or absolute URL specifying the destination of the request
9435 * @param {*} data Request content
9436 * @param {Object=} config Optional configuration object
9437 * @returns {HttpPromise} Future object
9438 */
9439 createShortMethodsWithData('post', 'put', 'patch');
9440
9441 /**
9442 * @ngdoc property
9443 * @name $http#defaults
9444 *
9445 * @description
9446 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
9447 * default headers, withCredentials as well as request and response transformations.
9448 *
9449 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
9450 */
9451 $http.defaults = defaults;
9452
9453
9454 return $http;
9455
9456
9457 function createShortMethods(names) {
9458 forEach(arguments, function(name) {
9459 $http[name] = function(url, config) {
9460 return $http(extend(config || {}, {
9461 method: name,
9462 url: url
9463 }));
9464 };
9465 });
9466 }
9467
9468
9469 function createShortMethodsWithData(name) {
9470 forEach(arguments, function(name) {
9471 $http[name] = function(url, data, config) {
9472 return $http(extend(config || {}, {
9473 method: name,
9474 url: url,
9475 data: data
9476 }));
9477 };
9478 });
9479 }
9480
9481
9482 /**
9483 * Makes the request.
9484 *
9485 * !!! ACCESSES CLOSURE VARS:
9486 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
9487 */
9488 function sendReq(config, reqData, reqHeaders) {
9489 var deferred = $q.defer(),
9490 promise = deferred.promise,
9491 cache,
9492 cachedResp,
9493 url = buildUrl(config.url, config.params);
9494
9495 $http.pendingRequests.push(config);
9496 promise.then(removePendingReq, removePendingReq);
9497
9498
9499 if ((config.cache || defaults.cache) && config.cache !== false &&
9500 (config.method === 'GET' || config.method === 'JSONP')) {
9501 cache = isObject(config.cache) ? config.cache
9502 : isObject(defaults.cache) ? defaults.cache
9503 : defaultCache;
9504 }
9505
9506 if (cache) {
9507 cachedResp = cache.get(url);
9508 if (isDefined(cachedResp)) {
9509 if (isPromiseLike(cachedResp)) {
9510 // cached request has already been sent, but there is no response yet
9511 cachedResp.then(removePendingReq, removePendingReq);
9512 return cachedResp;
9513 } else {
9514 // serving from cache
9515 if (isArray(cachedResp)) {
9516 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
9517 } else {
9518 resolvePromise(cachedResp, 200, {}, 'OK');
9519 }
9520 }
9521 } else {
9522 // put the promise for the non-transformed response into cache as a placeholder
9523 cache.put(url, promise);
9524 }
9525 }
9526
9527
9528 // if we won't have the response in cache, set the xsrf headers and
9529 // send the request to the backend
9530 if (isUndefined(cachedResp)) {
9531 var xsrfValue = urlIsSameOrigin(config.url)
9532 ? $browser.cookies()[config.xsrfCookieName || defaults.xsrfCookieName]
9533 : undefined;
9534 if (xsrfValue) {
9535 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
9536 }
9537
9538 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
9539 config.withCredentials, config.responseType);
9540 }
9541
9542 return promise;
9543
9544
9545 /**
9546 * Callback registered to $httpBackend():
9547 * - caches the response if desired
9548 * - resolves the raw $http promise
9549 * - calls $apply
9550 */
9551 function done(status, response, headersString, statusText) {
9552 if (cache) {
9553 if (isSuccess(status)) {
9554 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
9555 } else {
9556 // remove promise from the cache
9557 cache.remove(url);
9558 }
9559 }
9560
9561 function resolveHttpPromise() {
9562 resolvePromise(response, status, headersString, statusText);
9563 }
9564
9565 if (useApplyAsync) {
9566 $rootScope.$applyAsync(resolveHttpPromise);
9567 } else {
9568 resolveHttpPromise();
9569 if (!$rootScope.$$phase) $rootScope.$apply();
9570 }
9571 }
9572
9573
9574 /**
9575 * Resolves the raw $http promise.
9576 */
9577 function resolvePromise(response, status, headers, statusText) {
9578 // normalize internal statuses to 0
9579 status = Math.max(status, 0);
9580
9581 (isSuccess(status) ? deferred.resolve : deferred.reject)({
9582 data: response,
9583 status: status,
9584 headers: headersGetter(headers),
9585 config: config,
9586 statusText: statusText
9587 });
9588 }
9589
9590
9591 function removePendingReq() {
9592 var idx = $http.pendingRequests.indexOf(config);
9593 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
9594 }
9595 }
9596
9597
9598 function buildUrl(url, params) {
9599 if (!params) return url;
9600 var parts = [];
9601 forEachSorted(params, function(value, key) {
9602 if (value === null || isUndefined(value)) return;
9603 if (!isArray(value)) value = [value];
9604
9605 forEach(value, function(v) {
9606 if (isObject(v)) {
9607 if (isDate(v)) {
9608 v = v.toISOString();
9609 } else {
9610 v = toJson(v);
9611 }
9612 }
9613 parts.push(encodeUriQuery(key) + '=' +
9614 encodeUriQuery(v));
9615 });
9616 });
9617 if (parts.length > 0) {
9618 url += ((url.indexOf('?') == -1) ? '?' : '&') + parts.join('&');
9619 }
9620 return url;
9621 }
9622 }];
9623}
9624
9625function createXhr() {
9626 return new window.XMLHttpRequest();
9627}
9628
9629/**
9630 * @ngdoc service
9631 * @name $httpBackend
9632 * @requires $window
9633 * @requires $document
9634 *
9635 * @description
9636 * HTTP backend used by the {@link ng.$http service} that delegates to
9637 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
9638 *
9639 * You should never need to use this service directly, instead use the higher-level abstractions:
9640 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
9641 *
9642 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
9643 * $httpBackend} which can be trained with responses.
9644 */
9645function $HttpBackendProvider() {
9646 this.$get = ['$browser', '$window', '$document', function($browser, $window, $document) {
9647 return createHttpBackend($browser, createXhr, $browser.defer, $window.angular.callbacks, $document[0]);
9648 }];
9649}
9650
9651function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
9652 // TODO(vojta): fix the signature
9653 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
9654 $browser.$$incOutstandingRequestCount();
9655 url = url || $browser.url();
9656
9657 if (lowercase(method) == 'jsonp') {
9658 var callbackId = '_' + (callbacks.counter++).toString(36);
9659 callbacks[callbackId] = function(data) {
9660 callbacks[callbackId].data = data;
9661 callbacks[callbackId].called = true;
9662 };
9663
9664 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
9665 callbackId, function(status, text) {
9666 completeRequest(callback, status, callbacks[callbackId].data, "", text);
9667 callbacks[callbackId] = noop;
9668 });
9669 } else {
9670
9671 var xhr = createXhr();
9672
9673 xhr.open(method, url, true);
9674 forEach(headers, function(value, key) {
9675 if (isDefined(value)) {
9676 xhr.setRequestHeader(key, value);
9677 }
9678 });
9679
9680 xhr.onload = function requestLoaded() {
9681 var statusText = xhr.statusText || '';
9682
9683 // responseText is the old-school way of retrieving response (supported by IE8 & 9)
9684 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
9685 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
9686
9687 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
9688 var status = xhr.status === 1223 ? 204 : xhr.status;
9689
9690 // fix status code when it is 0 (0 status is undocumented).
9691 // Occurs when accessing file resources or on Android 4.1 stock browser
9692 // while retrieving files from application cache.
9693 if (status === 0) {
9694 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
9695 }
9696
9697 completeRequest(callback,
9698 status,
9699 response,
9700 xhr.getAllResponseHeaders(),
9701 statusText);
9702 };
9703
9704 var requestError = function() {
9705 // The response is always empty
9706 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
9707 completeRequest(callback, -1, null, null, '');
9708 };
9709
9710 xhr.onerror = requestError;
9711 xhr.onabort = requestError;
9712
9713 if (withCredentials) {
9714 xhr.withCredentials = true;
9715 }
9716
9717 if (responseType) {
9718 try {
9719 xhr.responseType = responseType;
9720 } catch (e) {
9721 // WebKit added support for the json responseType value on 09/03/2013
9722 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
9723 // known to throw when setting the value "json" as the response type. Other older
9724 // browsers implementing the responseType
9725 //
9726 // The json response type can be ignored if not supported, because JSON payloads are
9727 // parsed on the client-side regardless.
9728 if (responseType !== 'json') {
9729 throw e;
9730 }
9731 }
9732 }
9733
9734 xhr.send(post || null);
9735 }
9736
9737 if (timeout > 0) {
9738 var timeoutId = $browserDefer(timeoutRequest, timeout);
9739 } else if (isPromiseLike(timeout)) {
9740 timeout.then(timeoutRequest);
9741 }
9742
9743
9744 function timeoutRequest() {
9745 jsonpDone && jsonpDone();
9746 xhr && xhr.abort();
9747 }
9748
9749 function completeRequest(callback, status, response, headersString, statusText) {
9750 // cancel timeout and subsequent timeout promise resolution
9751 if (timeoutId !== undefined) {
9752 $browserDefer.cancel(timeoutId);
9753 }
9754 jsonpDone = xhr = null;
9755
9756 callback(status, response, headersString, statusText);
9757 $browser.$$completeOutstandingRequest(noop);
9758 }
9759 };
9760
9761 function jsonpReq(url, callbackId, done) {
9762 // we can't use jQuery/jqLite here because jQuery does crazy shit with script elements, e.g.:
9763 // - fetches local scripts via XHR and evals them
9764 // - adds and immediately removes script elements from the document
9765 var script = rawDocument.createElement('script'), callback = null;
9766 script.type = "text/javascript";
9767 script.src = url;
9768 script.async = true;
9769
9770 callback = function(event) {
9771 removeEventListenerFn(script, "load", callback);
9772 removeEventListenerFn(script, "error", callback);
9773 rawDocument.body.removeChild(script);
9774 script = null;
9775 var status = -1;
9776 var text = "unknown";
9777
9778 if (event) {
9779 if (event.type === "load" && !callbacks[callbackId].called) {
9780 event = { type: "error" };
9781 }
9782 text = event.type;
9783 status = event.type === "error" ? 404 : 200;
9784 }
9785
9786 if (done) {
9787 done(status, text);
9788 }
9789 };
9790
9791 addEventListenerFn(script, "load", callback);
9792 addEventListenerFn(script, "error", callback);
9793 rawDocument.body.appendChild(script);
9794 return callback;
9795 }
9796}
9797
9798var $interpolateMinErr = minErr('$interpolate');
9799
9800/**
9801 * @ngdoc provider
9802 * @name $interpolateProvider
9803 *
9804 * @description
9805 *
9806 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
9807 *
9808 * @example
9809<example module="customInterpolationApp">
9810<file name="index.html">
9811<script>
9812 var customInterpolationApp = angular.module('customInterpolationApp', []);
9813
9814 customInterpolationApp.config(function($interpolateProvider) {
9815 $interpolateProvider.startSymbol('//');
9816 $interpolateProvider.endSymbol('//');
9817 });
9818
9819
9820 customInterpolationApp.controller('DemoController', function() {
9821 this.label = "This binding is brought you by // interpolation symbols.";
9822 });
9823</script>
9824<div ng-app="App" ng-controller="DemoController as demo">
9825 //demo.label//
9826</div>
9827</file>
9828<file name="protractor.js" type="protractor">
9829 it('should interpolate binding with custom symbols', function() {
9830 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
9831 });
9832</file>
9833</example>
9834 */
9835function $InterpolateProvider() {
9836 var startSymbol = '{{';
9837 var endSymbol = '}}';
9838
9839 /**
9840 * @ngdoc method
9841 * @name $interpolateProvider#startSymbol
9842 * @description
9843 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
9844 *
9845 * @param {string=} value new value to set the starting symbol to.
9846 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9847 */
9848 this.startSymbol = function(value) {
9849 if (value) {
9850 startSymbol = value;
9851 return this;
9852 } else {
9853 return startSymbol;
9854 }
9855 };
9856
9857 /**
9858 * @ngdoc method
9859 * @name $interpolateProvider#endSymbol
9860 * @description
9861 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
9862 *
9863 * @param {string=} value new value to set the ending symbol to.
9864 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
9865 */
9866 this.endSymbol = function(value) {
9867 if (value) {
9868 endSymbol = value;
9869 return this;
9870 } else {
9871 return endSymbol;
9872 }
9873 };
9874
9875
9876 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
9877 var startSymbolLength = startSymbol.length,
9878 endSymbolLength = endSymbol.length,
9879 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
9880 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
9881
9882 function escape(ch) {
9883 return '\\\\\\' + ch;
9884 }
9885
9886 /**
9887 * @ngdoc service
9888 * @name $interpolate
9889 * @kind function
9890 *
9891 * @requires $parse
9892 * @requires $sce
9893 *
9894 * @description
9895 *
9896 * Compiles a string with markup into an interpolation function. This service is used by the
9897 * HTML {@link ng.$compile $compile} service for data binding. See
9898 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
9899 * interpolation markup.
9900 *
9901 *
9902 * ```js
9903 * var $interpolate = ...; // injected
9904 * var exp = $interpolate('Hello {{name | uppercase}}!');
9905 * expect(exp({name:'Angular'}).toEqual('Hello ANGULAR!');
9906 * ```
9907 *
9908 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
9909 * `true`, the interpolation function will return `undefined` unless all embedded expressions
9910 * evaluate to a value other than `undefined`.
9911 *
9912 * ```js
9913 * var $interpolate = ...; // injected
9914 * var context = {greeting: 'Hello', name: undefined };
9915 *
9916 * // default "forgiving" mode
9917 * var exp = $interpolate('{{greeting}} {{name}}!');
9918 * expect(exp(context)).toEqual('Hello !');
9919 *
9920 * // "allOrNothing" mode
9921 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
9922 * expect(exp(context)).toBeUndefined();
9923 * context.name = 'Angular';
9924 * expect(exp(context)).toEqual('Hello Angular!');
9925 * ```
9926 *
9927 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
9928 *
9929 * ####Escaped Interpolation
9930 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
9931 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
9932 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
9933 * or binding.
9934 *
9935 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
9936 * degree, while also enabling code examples to work without relying on the
9937 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
9938 *
9939 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
9940 * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
9941 * interpolation start/end markers with their escaped counterparts.**
9942 *
9943 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
9944 * output when the $interpolate service processes the text. So, for HTML elements interpolated
9945 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
9946 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
9947 * this is typically useful only when user-data is used in rendering a template from the server, or
9948 * when otherwise untrusted data is used by a directive.
9949 *
9950 * <example>
9951 * <file name="index.html">
9952 * <div ng-init="username='A user'">
9953 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
9954 * </p>
9955 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
9956 * application, but fails to accomplish their task, because the server has correctly
9957 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
9958 * characters.</p>
9959 * <p>Instead, the result of the attempted script injection is visible, and can be removed
9960 * from the database by an administrator.</p>
9961 * </div>
9962 * </file>
9963 * </example>
9964 *
9965 * @param {string} text The text with markup to interpolate.
9966 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
9967 * embedded expression in order to return an interpolation function. Strings with no
9968 * embedded expression will return null for the interpolation function.
9969 * @param {string=} trustedContext when provided, the returned function passes the interpolated
9970 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
9971 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
9972 * provides Strict Contextual Escaping for details.
9973 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
9974 * unless all embedded expressions evaluate to a value other than `undefined`.
9975 * @returns {function(context)} an interpolation function which is used to compute the
9976 * interpolated string. The function has these parameters:
9977 *
9978 * - `context`: evaluation context for all expressions embedded in the interpolated text
9979 */
9980 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
9981 allOrNothing = !!allOrNothing;
9982 var startIndex,
9983 endIndex,
9984 index = 0,
9985 expressions = [],
9986 parseFns = [],
9987 textLength = text.length,
9988 exp,
9989 concat = [],
9990 expressionPositions = [];
9991
9992 while (index < textLength) {
9993 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
9994 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
9995 if (index !== startIndex) {
9996 concat.push(unescapeText(text.substring(index, startIndex)));
9997 }
9998 exp = text.substring(startIndex + startSymbolLength, endIndex);
9999 expressions.push(exp);
10000 parseFns.push($parse(exp, parseStringifyInterceptor));
10001 index = endIndex + endSymbolLength;
10002 expressionPositions.push(concat.length);
10003 concat.push('');
10004 } else {
10005 // we did not find an interpolation, so we have to add the remainder to the separators array
10006 if (index !== textLength) {
10007 concat.push(unescapeText(text.substring(index)));
10008 }
10009 break;
10010 }
10011 }
10012
10013 // Concatenating expressions makes it hard to reason about whether some combination of
10014 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
10015 // single expression be used for iframe[src], object[src], etc., we ensure that the value
10016 // that's used is assigned or constructed by some JS code somewhere that is more testable or
10017 // make it obvious that you bound the value to some user controlled value. This helps reduce
10018 // the load when auditing for XSS issues.
10019 if (trustedContext && concat.length > 1) {
10020 throw $interpolateMinErr('noconcat',
10021 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10022 "interpolations that concatenate multiple expressions when a trusted value is " +
10023 "required. See http://docs.angularjs.org/api/ng.$sce", text);
10024 }
10025
10026 if (!mustHaveExpression || expressions.length) {
10027 var compute = function(values) {
10028 for (var i = 0, ii = expressions.length; i < ii; i++) {
10029 if (allOrNothing && isUndefined(values[i])) return;
10030 concat[expressionPositions[i]] = values[i];
10031 }
10032 return concat.join('');
10033 };
10034
10035 var getValue = function(value) {
10036 return trustedContext ?
10037 $sce.getTrusted(trustedContext, value) :
10038 $sce.valueOf(value);
10039 };
10040
10041 var stringify = function(value) {
10042 if (value == null) { // null || undefined
10043 return '';
10044 }
10045 switch (typeof value) {
10046 case 'string':
10047 break;
10048 case 'number':
10049 value = '' + value;
10050 break;
10051 default:
10052 value = toJson(value);
10053 }
10054
10055 return value;
10056 };
10057
10058 return extend(function interpolationFn(context) {
10059 var i = 0;
10060 var ii = expressions.length;
10061 var values = new Array(ii);
10062
10063 try {
10064 for (; i < ii; i++) {
10065 values[i] = parseFns[i](context);
10066 }
10067
10068 return compute(values);
10069 } catch (err) {
10070 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10071 err.toString());
10072 $exceptionHandler(newErr);
10073 }
10074
10075 }, {
10076 // all of these properties are undocumented for now
10077 exp: text, //just for compatibility with regular watchers created via $watch
10078 expressions: expressions,
10079 $$watchDelegate: function(scope, listener, objectEquality) {
10080 var lastValue;
10081 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
10082 var currValue = compute(values);
10083 if (isFunction(listener)) {
10084 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
10085 }
10086 lastValue = currValue;
10087 }, objectEquality);
10088 }
10089 });
10090 }
10091
10092 function unescapeText(text) {
10093 return text.replace(escapedStartRegexp, startSymbol).
10094 replace(escapedEndRegexp, endSymbol);
10095 }
10096
10097 function parseStringifyInterceptor(value) {
10098 try {
10099 value = getValue(value);
10100 return allOrNothing && !isDefined(value) ? value : stringify(value);
10101 } catch (err) {
10102 var newErr = $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text,
10103 err.toString());
10104 $exceptionHandler(newErr);
10105 }
10106 }
10107 }
10108
10109
10110 /**
10111 * @ngdoc method
10112 * @name $interpolate#startSymbol
10113 * @description
10114 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
10115 *
10116 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
10117 * the symbol.
10118 *
10119 * @returns {string} start symbol.
10120 */
10121 $interpolate.startSymbol = function() {
10122 return startSymbol;
10123 };
10124
10125
10126 /**
10127 * @ngdoc method
10128 * @name $interpolate#endSymbol
10129 * @description
10130 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10131 *
10132 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
10133 * the symbol.
10134 *
10135 * @returns {string} end symbol.
10136 */
10137 $interpolate.endSymbol = function() {
10138 return endSymbol;
10139 };
10140
10141 return $interpolate;
10142 }];
10143}
10144
10145function $IntervalProvider() {
10146 this.$get = ['$rootScope', '$window', '$q', '$$q',
10147 function($rootScope, $window, $q, $$q) {
10148 var intervals = {};
10149
10150
10151 /**
10152 * @ngdoc service
10153 * @name $interval
10154 *
10155 * @description
10156 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
10157 * milliseconds.
10158 *
10159 * The return value of registering an interval function is a promise. This promise will be
10160 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
10161 * run indefinitely if `count` is not defined. The value of the notification will be the
10162 * number of iterations that have run.
10163 * To cancel an interval, call `$interval.cancel(promise)`.
10164 *
10165 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
10166 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
10167 * time.
10168 *
10169 * <div class="alert alert-warning">
10170 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
10171 * with them. In particular they are not automatically destroyed when a controller's scope or a
10172 * directive's element are destroyed.
10173 * You should take this into consideration and make sure to always cancel the interval at the
10174 * appropriate moment. See the example below for more details on how and when to do this.
10175 * </div>
10176 *
10177 * @param {function()} fn A function that should be called repeatedly.
10178 * @param {number} delay Number of milliseconds between each function call.
10179 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
10180 * indefinitely.
10181 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
10182 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
10183 * @returns {promise} A promise which will be notified on each iteration.
10184 *
10185 * @example
10186 * <example module="intervalExample">
10187 * <file name="index.html">
10188 * <script>
10189 * angular.module('intervalExample', [])
10190 * .controller('ExampleController', ['$scope', '$interval',
10191 * function($scope, $interval) {
10192 * $scope.format = 'M/d/yy h:mm:ss a';
10193 * $scope.blood_1 = 100;
10194 * $scope.blood_2 = 120;
10195 *
10196 * var stop;
10197 * $scope.fight = function() {
10198 * // Don't start a new fight if we are already fighting
10199 * if ( angular.isDefined(stop) ) return;
10200 *
10201 * stop = $interval(function() {
10202 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
10203 * $scope.blood_1 = $scope.blood_1 - 3;
10204 * $scope.blood_2 = $scope.blood_2 - 4;
10205 * } else {
10206 * $scope.stopFight();
10207 * }
10208 * }, 100);
10209 * };
10210 *
10211 * $scope.stopFight = function() {
10212 * if (angular.isDefined(stop)) {
10213 * $interval.cancel(stop);
10214 * stop = undefined;
10215 * }
10216 * };
10217 *
10218 * $scope.resetFight = function() {
10219 * $scope.blood_1 = 100;
10220 * $scope.blood_2 = 120;
10221 * };
10222 *
10223 * $scope.$on('$destroy', function() {
10224 * // Make sure that the interval is destroyed too
10225 * $scope.stopFight();
10226 * });
10227 * }])
10228 * // Register the 'myCurrentTime' directive factory method.
10229 * // We inject $interval and dateFilter service since the factory method is DI.
10230 * .directive('myCurrentTime', ['$interval', 'dateFilter',
10231 * function($interval, dateFilter) {
10232 * // return the directive link function. (compile function not needed)
10233 * return function(scope, element, attrs) {
10234 * var format, // date format
10235 * stopTime; // so that we can cancel the time updates
10236 *
10237 * // used to update the UI
10238 * function updateTime() {
10239 * element.text(dateFilter(new Date(), format));
10240 * }
10241 *
10242 * // watch the expression, and update the UI on change.
10243 * scope.$watch(attrs.myCurrentTime, function(value) {
10244 * format = value;
10245 * updateTime();
10246 * });
10247 *
10248 * stopTime = $interval(updateTime, 1000);
10249 *
10250 * // listen on DOM destroy (removal) event, and cancel the next UI update
10251 * // to prevent updating time after the DOM element was removed.
10252 * element.on('$destroy', function() {
10253 * $interval.cancel(stopTime);
10254 * });
10255 * }
10256 * }]);
10257 * </script>
10258 *
10259 * <div>
10260 * <div ng-controller="ExampleController">
10261 * Date format: <input ng-model="format"> <hr/>
10262 * Current time is: <span my-current-time="format"></span>
10263 * <hr/>
10264 * Blood 1 : <font color='red'>{{blood_1}}</font>
10265 * Blood 2 : <font color='red'>{{blood_2}}</font>
10266 * <button type="button" data-ng-click="fight()">Fight</button>
10267 * <button type="button" data-ng-click="stopFight()">StopFight</button>
10268 * <button type="button" data-ng-click="resetFight()">resetFight</button>
10269 * </div>
10270 * </div>
10271 *
10272 * </file>
10273 * </example>
10274 */
10275 function interval(fn, delay, count, invokeApply) {
10276 var setInterval = $window.setInterval,
10277 clearInterval = $window.clearInterval,
10278 iteration = 0,
10279 skipApply = (isDefined(invokeApply) && !invokeApply),
10280 deferred = (skipApply ? $$q : $q).defer(),
10281 promise = deferred.promise;
10282
10283 count = isDefined(count) ? count : 0;
10284
10285 promise.then(null, null, fn);
10286
10287 promise.$$intervalId = setInterval(function tick() {
10288 deferred.notify(iteration++);
10289
10290 if (count > 0 && iteration >= count) {
10291 deferred.resolve(iteration);
10292 clearInterval(promise.$$intervalId);
10293 delete intervals[promise.$$intervalId];
10294 }
10295
10296 if (!skipApply) $rootScope.$apply();
10297
10298 }, delay);
10299
10300 intervals[promise.$$intervalId] = deferred;
10301
10302 return promise;
10303 }
10304
10305
10306 /**
10307 * @ngdoc method
10308 * @name $interval#cancel
10309 *
10310 * @description
10311 * Cancels a task associated with the `promise`.
10312 *
10313 * @param {promise} promise returned by the `$interval` function.
10314 * @returns {boolean} Returns `true` if the task was successfully canceled.
10315 */
10316 interval.cancel = function(promise) {
10317 if (promise && promise.$$intervalId in intervals) {
10318 intervals[promise.$$intervalId].reject('canceled');
10319 $window.clearInterval(promise.$$intervalId);
10320 delete intervals[promise.$$intervalId];
10321 return true;
10322 }
10323 return false;
10324 };
10325
10326 return interval;
10327 }];
10328}
10329
10330/**
10331 * @ngdoc service
10332 * @name $locale
10333 *
10334 * @description
10335 * $locale service provides localization rules for various Angular components. As of right now the
10336 * only public api is:
10337 *
10338 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
10339 */
10340function $LocaleProvider() {
10341 this.$get = function() {
10342 return {
10343 id: 'en-us',
10344
10345 NUMBER_FORMATS: {
10346 DECIMAL_SEP: '.',
10347 GROUP_SEP: ',',
10348 PATTERNS: [
10349 { // Decimal Pattern
10350 minInt: 1,
10351 minFrac: 0,
10352 maxFrac: 3,
10353 posPre: '',
10354 posSuf: '',
10355 negPre: '-',
10356 negSuf: '',
10357 gSize: 3,
10358 lgSize: 3
10359 },{ //Currency Pattern
10360 minInt: 1,
10361 minFrac: 2,
10362 maxFrac: 2,
10363 posPre: '\u00A4',
10364 posSuf: '',
10365 negPre: '(\u00A4',
10366 negSuf: ')',
10367 gSize: 3,
10368 lgSize: 3
10369 }
10370 ],
10371 CURRENCY_SYM: '$'
10372 },
10373
10374 DATETIME_FORMATS: {
10375 MONTH:
10376 'January,February,March,April,May,June,July,August,September,October,November,December'
10377 .split(','),
10378 SHORTMONTH: 'Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec'.split(','),
10379 DAY: 'Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday'.split(','),
10380 SHORTDAY: 'Sun,Mon,Tue,Wed,Thu,Fri,Sat'.split(','),
10381 AMPMS: ['AM','PM'],
10382 medium: 'MMM d, y h:mm:ss a',
10383 'short': 'M/d/yy h:mm a',
10384 fullDate: 'EEEE, MMMM d, y',
10385 longDate: 'MMMM d, y',
10386 mediumDate: 'MMM d, y',
10387 shortDate: 'M/d/yy',
10388 mediumTime: 'h:mm:ss a',
10389 shortTime: 'h:mm a'
10390 },
10391
10392 pluralCat: function(num) {
10393 if (num === 1) {
10394 return 'one';
10395 }
10396 return 'other';
10397 }
10398 };
10399 };
10400}
10401
10402var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
10403 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
10404var $locationMinErr = minErr('$location');
10405
10406
10407/**
10408 * Encode path using encodeUriSegment, ignoring forward slashes
10409 *
10410 * @param {string} path Path to encode
10411 * @returns {string}
10412 */
10413function encodePath(path) {
10414 var segments = path.split('/'),
10415 i = segments.length;
10416
10417 while (i--) {
10418 segments[i] = encodeUriSegment(segments[i]);
10419 }
10420
10421 return segments.join('/');
10422}
10423
10424function parseAbsoluteUrl(absoluteUrl, locationObj) {
10425 var parsedUrl = urlResolve(absoluteUrl);
10426
10427 locationObj.$$protocol = parsedUrl.protocol;
10428 locationObj.$$host = parsedUrl.hostname;
10429 locationObj.$$port = int(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
10430}
10431
10432
10433function parseAppUrl(relativeUrl, locationObj) {
10434 var prefixed = (relativeUrl.charAt(0) !== '/');
10435 if (prefixed) {
10436 relativeUrl = '/' + relativeUrl;
10437 }
10438 var match = urlResolve(relativeUrl);
10439 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
10440 match.pathname.substring(1) : match.pathname);
10441 locationObj.$$search = parseKeyValue(match.search);
10442 locationObj.$$hash = decodeURIComponent(match.hash);
10443
10444 // make sure path starts with '/';
10445 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
10446 locationObj.$$path = '/' + locationObj.$$path;
10447 }
10448}
10449
10450
10451/**
10452 *
10453 * @param {string} begin
10454 * @param {string} whole
10455 * @returns {string} returns text from whole after begin or undefined if it does not begin with
10456 * expected string.
10457 */
10458function beginsWith(begin, whole) {
10459 if (whole.indexOf(begin) === 0) {
10460 return whole.substr(begin.length);
10461 }
10462}
10463
10464
10465function stripHash(url) {
10466 var index = url.indexOf('#');
10467 return index == -1 ? url : url.substr(0, index);
10468}
10469
10470
10471function stripFile(url) {
10472 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
10473}
10474
10475/* return the server only (scheme://host:port) */
10476function serverBase(url) {
10477 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
10478}
10479
10480
10481/**
10482 * LocationHtml5Url represents an url
10483 * This object is exposed as $location service when HTML5 mode is enabled and supported
10484 *
10485 * @constructor
10486 * @param {string} appBase application base URL
10487 * @param {string} basePrefix url path prefix
10488 */
10489function LocationHtml5Url(appBase, basePrefix) {
10490 this.$$html5 = true;
10491 basePrefix = basePrefix || '';
10492 var appBaseNoFile = stripFile(appBase);
10493 parseAbsoluteUrl(appBase, this);
10494
10495
10496 /**
10497 * Parse given html5 (regular) url string into properties
10498 * @param {string} url HTML5 url
10499 * @private
10500 */
10501 this.$$parse = function(url) {
10502 var pathUrl = beginsWith(appBaseNoFile, url);
10503 if (!isString(pathUrl)) {
10504 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
10505 appBaseNoFile);
10506 }
10507
10508 parseAppUrl(pathUrl, this);
10509
10510 if (!this.$$path) {
10511 this.$$path = '/';
10512 }
10513
10514 this.$$compose();
10515 };
10516
10517 /**
10518 * Compose url and update `absUrl` property
10519 * @private
10520 */
10521 this.$$compose = function() {
10522 var search = toKeyValue(this.$$search),
10523 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10524
10525 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10526 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
10527 };
10528
10529 this.$$parseLinkUrl = function(url, relHref) {
10530 if (relHref && relHref[0] === '#') {
10531 // special case for links to hash fragments:
10532 // keep the old url and only replace the hash fragment
10533 this.hash(relHref.slice(1));
10534 return true;
10535 }
10536 var appUrl, prevAppUrl;
10537 var rewrittenUrl;
10538
10539 if ((appUrl = beginsWith(appBase, url)) !== undefined) {
10540 prevAppUrl = appUrl;
10541 if ((appUrl = beginsWith(basePrefix, appUrl)) !== undefined) {
10542 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
10543 } else {
10544 rewrittenUrl = appBase + prevAppUrl;
10545 }
10546 } else if ((appUrl = beginsWith(appBaseNoFile, url)) !== undefined) {
10547 rewrittenUrl = appBaseNoFile + appUrl;
10548 } else if (appBaseNoFile == url + '/') {
10549 rewrittenUrl = appBaseNoFile;
10550 }
10551 if (rewrittenUrl) {
10552 this.$$parse(rewrittenUrl);
10553 }
10554 return !!rewrittenUrl;
10555 };
10556}
10557
10558
10559/**
10560 * LocationHashbangUrl represents url
10561 * This object is exposed as $location service when developer doesn't opt into html5 mode.
10562 * It also serves as the base class for html5 mode fallback on legacy browsers.
10563 *
10564 * @constructor
10565 * @param {string} appBase application base URL
10566 * @param {string} hashPrefix hashbang prefix
10567 */
10568function LocationHashbangUrl(appBase, hashPrefix) {
10569 var appBaseNoFile = stripFile(appBase);
10570
10571 parseAbsoluteUrl(appBase, this);
10572
10573
10574 /**
10575 * Parse given hashbang url into properties
10576 * @param {string} url Hashbang url
10577 * @private
10578 */
10579 this.$$parse = function(url) {
10580 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
10581 var withoutHashUrl = withoutBaseUrl.charAt(0) == '#'
10582 ? beginsWith(hashPrefix, withoutBaseUrl)
10583 : (this.$$html5)
10584 ? withoutBaseUrl
10585 : '';
10586
10587 if (!isString(withoutHashUrl)) {
10588 throw $locationMinErr('ihshprfx', 'Invalid url "{0}", missing hash prefix "{1}".', url,
10589 hashPrefix);
10590 }
10591 parseAppUrl(withoutHashUrl, this);
10592
10593 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
10594
10595 this.$$compose();
10596
10597 /*
10598 * In Windows, on an anchor node on documents loaded from
10599 * the filesystem, the browser will return a pathname
10600 * prefixed with the drive name ('/C:/path') when a
10601 * pathname without a drive is set:
10602 * * a.setAttribute('href', '/foo')
10603 * * a.pathname === '/C:/foo' //true
10604 *
10605 * Inside of Angular, we're always using pathnames that
10606 * do not include drive names for routing.
10607 */
10608 function removeWindowsDriveName(path, url, base) {
10609 /*
10610 Matches paths for file protocol on windows,
10611 such as /C:/foo/bar, and captures only /foo/bar.
10612 */
10613 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
10614
10615 var firstPathSegmentMatch;
10616
10617 //Get the relative path from the input URL.
10618 if (url.indexOf(base) === 0) {
10619 url = url.replace(base, '');
10620 }
10621
10622 // The input URL intentionally contains a first path segment that ends with a colon.
10623 if (windowsFilePathExp.exec(url)) {
10624 return path;
10625 }
10626
10627 firstPathSegmentMatch = windowsFilePathExp.exec(path);
10628 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
10629 }
10630 };
10631
10632 /**
10633 * Compose hashbang url and update `absUrl` property
10634 * @private
10635 */
10636 this.$$compose = function() {
10637 var search = toKeyValue(this.$$search),
10638 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10639
10640 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10641 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
10642 };
10643
10644 this.$$parseLinkUrl = function(url, relHref) {
10645 if (stripHash(appBase) == stripHash(url)) {
10646 this.$$parse(url);
10647 return true;
10648 }
10649 return false;
10650 };
10651}
10652
10653
10654/**
10655 * LocationHashbangUrl represents url
10656 * This object is exposed as $location service when html5 history api is enabled but the browser
10657 * does not support it.
10658 *
10659 * @constructor
10660 * @param {string} appBase application base URL
10661 * @param {string} hashPrefix hashbang prefix
10662 */
10663function LocationHashbangInHtml5Url(appBase, hashPrefix) {
10664 this.$$html5 = true;
10665 LocationHashbangUrl.apply(this, arguments);
10666
10667 var appBaseNoFile = stripFile(appBase);
10668
10669 this.$$parseLinkUrl = function(url, relHref) {
10670 if (relHref && relHref[0] === '#') {
10671 // special case for links to hash fragments:
10672 // keep the old url and only replace the hash fragment
10673 this.hash(relHref.slice(1));
10674 return true;
10675 }
10676
10677 var rewrittenUrl;
10678 var appUrl;
10679
10680 if (appBase == stripHash(url)) {
10681 rewrittenUrl = url;
10682 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
10683 rewrittenUrl = appBase + hashPrefix + appUrl;
10684 } else if (appBaseNoFile === url + '/') {
10685 rewrittenUrl = appBaseNoFile;
10686 }
10687 if (rewrittenUrl) {
10688 this.$$parse(rewrittenUrl);
10689 }
10690 return !!rewrittenUrl;
10691 };
10692
10693 this.$$compose = function() {
10694 var search = toKeyValue(this.$$search),
10695 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
10696
10697 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
10698 // include hashPrefix in $$absUrl when $$url is empty so IE8 & 9 do not reload page because of removal of '#'
10699 this.$$absUrl = appBase + hashPrefix + this.$$url;
10700 };
10701
10702}
10703
10704
10705var locationPrototype = {
10706
10707 /**
10708 * Are we in html5 mode?
10709 * @private
10710 */
10711 $$html5: false,
10712
10713 /**
10714 * Has any change been replacing?
10715 * @private
10716 */
10717 $$replace: false,
10718
10719 /**
10720 * @ngdoc method
10721 * @name $location#absUrl
10722 *
10723 * @description
10724 * This method is getter only.
10725 *
10726 * Return full url representation with all segments encoded according to rules specified in
10727 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
10728 *
10729 *
10730 * ```js
10731 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10732 * var absUrl = $location.absUrl();
10733 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
10734 * ```
10735 *
10736 * @return {string} full url
10737 */
10738 absUrl: locationGetter('$$absUrl'),
10739
10740 /**
10741 * @ngdoc method
10742 * @name $location#url
10743 *
10744 * @description
10745 * This method is getter / setter.
10746 *
10747 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
10748 *
10749 * Change path, search and hash, when called with parameter and return `$location`.
10750 *
10751 *
10752 * ```js
10753 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10754 * var url = $location.url();
10755 * // => "/some/path?foo=bar&baz=xoxo"
10756 * ```
10757 *
10758 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
10759 * @return {string} url
10760 */
10761 url: function(url) {
10762 if (isUndefined(url))
10763 return this.$$url;
10764
10765 var match = PATH_MATCH.exec(url);
10766 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
10767 if (match[2] || match[1] || url === '') this.search(match[3] || '');
10768 this.hash(match[5] || '');
10769
10770 return this;
10771 },
10772
10773 /**
10774 * @ngdoc method
10775 * @name $location#protocol
10776 *
10777 * @description
10778 * This method is getter only.
10779 *
10780 * Return protocol of current url.
10781 *
10782 *
10783 * ```js
10784 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10785 * var protocol = $location.protocol();
10786 * // => "http"
10787 * ```
10788 *
10789 * @return {string} protocol of current url
10790 */
10791 protocol: locationGetter('$$protocol'),
10792
10793 /**
10794 * @ngdoc method
10795 * @name $location#host
10796 *
10797 * @description
10798 * This method is getter only.
10799 *
10800 * Return host of current url.
10801 *
10802 *
10803 * ```js
10804 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10805 * var host = $location.host();
10806 * // => "example.com"
10807 * ```
10808 *
10809 * @return {string} host of current url.
10810 */
10811 host: locationGetter('$$host'),
10812
10813 /**
10814 * @ngdoc method
10815 * @name $location#port
10816 *
10817 * @description
10818 * This method is getter only.
10819 *
10820 * Return port of current url.
10821 *
10822 *
10823 * ```js
10824 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10825 * var port = $location.port();
10826 * // => 80
10827 * ```
10828 *
10829 * @return {Number} port
10830 */
10831 port: locationGetter('$$port'),
10832
10833 /**
10834 * @ngdoc method
10835 * @name $location#path
10836 *
10837 * @description
10838 * This method is getter / setter.
10839 *
10840 * Return path of current url when called without any parameter.
10841 *
10842 * Change path when called with parameter and return `$location`.
10843 *
10844 * Note: Path should always begin with forward slash (/), this method will add the forward slash
10845 * if it is missing.
10846 *
10847 *
10848 * ```js
10849 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10850 * var path = $location.path();
10851 * // => "/some/path"
10852 * ```
10853 *
10854 * @param {(string|number)=} path New path
10855 * @return {string} path
10856 */
10857 path: locationGetterSetter('$$path', function(path) {
10858 path = path !== null ? path.toString() : '';
10859 return path.charAt(0) == '/' ? path : '/' + path;
10860 }),
10861
10862 /**
10863 * @ngdoc method
10864 * @name $location#search
10865 *
10866 * @description
10867 * This method is getter / setter.
10868 *
10869 * Return search part (as object) of current url when called without any parameter.
10870 *
10871 * Change search part when called with parameter and return `$location`.
10872 *
10873 *
10874 * ```js
10875 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
10876 * var searchObject = $location.search();
10877 * // => {foo: 'bar', baz: 'xoxo'}
10878 *
10879 * // set foo to 'yipee'
10880 * $location.search('foo', 'yipee');
10881 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
10882 * ```
10883 *
10884 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
10885 * hash object.
10886 *
10887 * When called with a single argument the method acts as a setter, setting the `search` component
10888 * of `$location` to the specified value.
10889 *
10890 * If the argument is a hash object containing an array of values, these values will be encoded
10891 * as duplicate search parameters in the url.
10892 *
10893 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
10894 * will override only a single search property.
10895 *
10896 * If `paramValue` is an array, it will override the property of the `search` component of
10897 * `$location` specified via the first argument.
10898 *
10899 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
10900 *
10901 * If `paramValue` is `true`, the property specified via the first argument will be added with no
10902 * value nor trailing equal sign.
10903 *
10904 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
10905 * one or more arguments returns `$location` object itself.
10906 */
10907 search: function(search, paramValue) {
10908 switch (arguments.length) {
10909 case 0:
10910 return this.$$search;
10911 case 1:
10912 if (isString(search) || isNumber(search)) {
10913 search = search.toString();
10914 this.$$search = parseKeyValue(search);
10915 } else if (isObject(search)) {
10916 search = copy(search, {});
10917 // remove object undefined or null properties
10918 forEach(search, function(value, key) {
10919 if (value == null) delete search[key];
10920 });
10921
10922 this.$$search = search;
10923 } else {
10924 throw $locationMinErr('isrcharg',
10925 'The first argument of the `$location#search()` call must be a string or an object.');
10926 }
10927 break;
10928 default:
10929 if (isUndefined(paramValue) || paramValue === null) {
10930 delete this.$$search[search];
10931 } else {
10932 this.$$search[search] = paramValue;
10933 }
10934 }
10935
10936 this.$$compose();
10937 return this;
10938 },
10939
10940 /**
10941 * @ngdoc method
10942 * @name $location#hash
10943 *
10944 * @description
10945 * This method is getter / setter.
10946 *
10947 * Return hash fragment when called without any parameter.
10948 *
10949 * Change hash fragment when called with parameter and return `$location`.
10950 *
10951 *
10952 * ```js
10953 * // given url http://example.com/some/path?foo=bar&baz=xoxo#hashValue
10954 * var hash = $location.hash();
10955 * // => "hashValue"
10956 * ```
10957 *
10958 * @param {(string|number)=} hash New hash fragment
10959 * @return {string} hash
10960 */
10961 hash: locationGetterSetter('$$hash', function(hash) {
10962 return hash !== null ? hash.toString() : '';
10963 }),
10964
10965 /**
10966 * @ngdoc method
10967 * @name $location#replace
10968 *
10969 * @description
10970 * If called, all changes to $location during current `$digest` will be replacing current history
10971 * record, instead of adding new one.
10972 */
10973 replace: function() {
10974 this.$$replace = true;
10975 return this;
10976 }
10977};
10978
10979forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
10980 Location.prototype = Object.create(locationPrototype);
10981
10982 /**
10983 * @ngdoc method
10984 * @name $location#state
10985 *
10986 * @description
10987 * This method is getter / setter.
10988 *
10989 * Return the history state object when called without any parameter.
10990 *
10991 * Change the history state object when called with one parameter and return `$location`.
10992 * The state object is later passed to `pushState` or `replaceState`.
10993 *
10994 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
10995 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
10996 * older browsers (like IE9 or Android < 4.0), don't use this method.
10997 *
10998 * @param {object=} state State object for pushState or replaceState
10999 * @return {object} state
11000 */
11001 Location.prototype.state = function(state) {
11002 if (!arguments.length)
11003 return this.$$state;
11004
11005 if (Location !== LocationHtml5Url || !this.$$html5) {
11006 throw $locationMinErr('nostate', 'History API state support is available only ' +
11007 'in HTML5 mode and only in browsers supporting HTML5 History API');
11008 }
11009 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
11010 // but we're changing the $$state reference to $browser.state() during the $digest
11011 // so the modification window is narrow.
11012 this.$$state = isUndefined(state) ? null : state;
11013
11014 return this;
11015 };
11016});
11017
11018
11019function locationGetter(property) {
11020 return function() {
11021 return this[property];
11022 };
11023}
11024
11025
11026function locationGetterSetter(property, preprocess) {
11027 return function(value) {
11028 if (isUndefined(value))
11029 return this[property];
11030
11031 this[property] = preprocess(value);
11032 this.$$compose();
11033
11034 return this;
11035 };
11036}
11037
11038
11039/**
11040 * @ngdoc service
11041 * @name $location
11042 *
11043 * @requires $rootElement
11044 *
11045 * @description
11046 * The $location service parses the URL in the browser address bar (based on the
11047 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
11048 * available to your application. Changes to the URL in the address bar are reflected into
11049 * $location service and changes to $location are reflected into the browser address bar.
11050 *
11051 * **The $location service:**
11052 *
11053 * - Exposes the current URL in the browser address bar, so you can
11054 * - Watch and observe the URL.
11055 * - Change the URL.
11056 * - Synchronizes the URL with the browser when the user
11057 * - Changes the address bar.
11058 * - Clicks the back or forward button (or clicks a History link).
11059 * - Clicks on a link.
11060 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
11061 *
11062 * For more information see {@link guide/$location Developer Guide: Using $location}
11063 */
11064
11065/**
11066 * @ngdoc provider
11067 * @name $locationProvider
11068 * @description
11069 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
11070 */
11071function $LocationProvider() {
11072 var hashPrefix = '',
11073 html5Mode = {
11074 enabled: false,
11075 requireBase: true,
11076 rewriteLinks: true
11077 };
11078
11079 /**
11080 * @ngdoc method
11081 * @name $locationProvider#hashPrefix
11082 * @description
11083 * @param {string=} prefix Prefix for hash part (containing path and search)
11084 * @returns {*} current value if used as getter or itself (chaining) if used as setter
11085 */
11086 this.hashPrefix = function(prefix) {
11087 if (isDefined(prefix)) {
11088 hashPrefix = prefix;
11089 return this;
11090 } else {
11091 return hashPrefix;
11092 }
11093 };
11094
11095 /**
11096 * @ngdoc method
11097 * @name $locationProvider#html5Mode
11098 * @description
11099 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
11100 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
11101 * properties:
11102 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
11103 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
11104 * support `pushState`.
11105 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
11106 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
11107 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
11108 * See the {@link guide/$location $location guide for more information}
11109 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
11110 * enables/disables url rewriting for relative links.
11111 *
11112 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
11113 */
11114 this.html5Mode = function(mode) {
11115 if (isBoolean(mode)) {
11116 html5Mode.enabled = mode;
11117 return this;
11118 } else if (isObject(mode)) {
11119
11120 if (isBoolean(mode.enabled)) {
11121 html5Mode.enabled = mode.enabled;
11122 }
11123
11124 if (isBoolean(mode.requireBase)) {
11125 html5Mode.requireBase = mode.requireBase;
11126 }
11127
11128 if (isBoolean(mode.rewriteLinks)) {
11129 html5Mode.rewriteLinks = mode.rewriteLinks;
11130 }
11131
11132 return this;
11133 } else {
11134 return html5Mode;
11135 }
11136 };
11137
11138 /**
11139 * @ngdoc event
11140 * @name $location#$locationChangeStart
11141 * @eventType broadcast on root scope
11142 * @description
11143 * Broadcasted before a URL will change.
11144 *
11145 * This change can be prevented by calling
11146 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
11147 * details about event object. Upon successful change
11148 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
11149 *
11150 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11151 * the browser supports the HTML5 History API.
11152 *
11153 * @param {Object} angularEvent Synthetic event object.
11154 * @param {string} newUrl New URL
11155 * @param {string=} oldUrl URL that was before it was changed.
11156 * @param {string=} newState New history state object
11157 * @param {string=} oldState History state object that was before it was changed.
11158 */
11159
11160 /**
11161 * @ngdoc event
11162 * @name $location#$locationChangeSuccess
11163 * @eventType broadcast on root scope
11164 * @description
11165 * Broadcasted after a URL was changed.
11166 *
11167 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
11168 * the browser supports the HTML5 History API.
11169 *
11170 * @param {Object} angularEvent Synthetic event object.
11171 * @param {string} newUrl New URL
11172 * @param {string=} oldUrl URL that was before it was changed.
11173 * @param {string=} newState New history state object
11174 * @param {string=} oldState History state object that was before it was changed.
11175 */
11176
11177 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement',
11178 function($rootScope, $browser, $sniffer, $rootElement) {
11179 var $location,
11180 LocationMode,
11181 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
11182 initialUrl = $browser.url(),
11183 appBase;
11184
11185 if (html5Mode.enabled) {
11186 if (!baseHref && html5Mode.requireBase) {
11187 throw $locationMinErr('nobase',
11188 "$location in HTML5 mode requires a <base> tag to be present!");
11189 }
11190 appBase = serverBase(initialUrl) + (baseHref || '/');
11191 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
11192 } else {
11193 appBase = stripHash(initialUrl);
11194 LocationMode = LocationHashbangUrl;
11195 }
11196 $location = new LocationMode(appBase, '#' + hashPrefix);
11197 $location.$$parseLinkUrl(initialUrl, initialUrl);
11198
11199 $location.$$state = $browser.state();
11200
11201 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
11202
11203 function setBrowserUrlWithFallback(url, replace, state) {
11204 var oldUrl = $location.url();
11205 var oldState = $location.$$state;
11206 try {
11207 $browser.url(url, replace, state);
11208
11209 // Make sure $location.state() returns referentially identical (not just deeply equal)
11210 // state object; this makes possible quick checking if the state changed in the digest
11211 // loop. Checking deep equality would be too expensive.
11212 $location.$$state = $browser.state();
11213 } catch (e) {
11214 // Restore old values if pushState fails
11215 $location.url(oldUrl);
11216 $location.$$state = oldState;
11217
11218 throw e;
11219 }
11220 }
11221
11222 $rootElement.on('click', function(event) {
11223 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
11224 // currently we open nice url link and redirect then
11225
11226 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.which == 2) return;
11227
11228 var elm = jqLite(event.target);
11229
11230 // traverse the DOM up to find first A tag
11231 while (nodeName_(elm[0]) !== 'a') {
11232 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
11233 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
11234 }
11235
11236 var absHref = elm.prop('href');
11237 // get the actual href attribute - see
11238 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
11239 var relHref = elm.attr('href') || elm.attr('xlink:href');
11240
11241 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
11242 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
11243 // an animation.
11244 absHref = urlResolve(absHref.animVal).href;
11245 }
11246
11247 // Ignore when url is started with javascript: or mailto:
11248 if (IGNORE_URI_REGEXP.test(absHref)) return;
11249
11250 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
11251 if ($location.$$parseLinkUrl(absHref, relHref)) {
11252 // We do a preventDefault for all urls that are part of the angular application,
11253 // in html5mode and also without, so that we are able to abort navigation without
11254 // getting double entries in the location history.
11255 event.preventDefault();
11256 // update location manually
11257 if ($location.absUrl() != $browser.url()) {
11258 $rootScope.$apply();
11259 // hack to work around FF6 bug 684208 when scenario runner clicks on links
11260 window.angular['ff-684208-preventDefault'] = true;
11261 }
11262 }
11263 }
11264 });
11265
11266
11267 // rewrite hashbang url <> html5 url
11268 if ($location.absUrl() != initialUrl) {
11269 $browser.url($location.absUrl(), true);
11270 }
11271
11272 var initializing = true;
11273
11274 // update $location when $browser url changes
11275 $browser.onUrlChange(function(newUrl, newState) {
11276 $rootScope.$evalAsync(function() {
11277 var oldUrl = $location.absUrl();
11278 var oldState = $location.$$state;
11279 var defaultPrevented;
11280
11281 $location.$$parse(newUrl);
11282 $location.$$state = newState;
11283
11284 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11285 newState, oldState).defaultPrevented;
11286
11287 // if the location was changed by a `$locationChangeStart` handler then stop
11288 // processing this location change
11289 if ($location.absUrl() !== newUrl) return;
11290
11291 if (defaultPrevented) {
11292 $location.$$parse(oldUrl);
11293 $location.$$state = oldState;
11294 setBrowserUrlWithFallback(oldUrl, false, oldState);
11295 } else {
11296 initializing = false;
11297 afterLocationChange(oldUrl, oldState);
11298 }
11299 });
11300 if (!$rootScope.$$phase) $rootScope.$digest();
11301 });
11302
11303 // update browser
11304 $rootScope.$watch(function $locationWatch() {
11305 var oldUrl = $browser.url();
11306 var oldState = $browser.state();
11307 var currentReplace = $location.$$replace;
11308 var urlOrStateChanged = oldUrl !== $location.absUrl() ||
11309 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
11310
11311 if (initializing || urlOrStateChanged) {
11312 initializing = false;
11313
11314 $rootScope.$evalAsync(function() {
11315 var newUrl = $location.absUrl();
11316 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
11317 $location.$$state, oldState).defaultPrevented;
11318
11319 // if the location was changed by a `$locationChangeStart` handler then stop
11320 // processing this location change
11321 if ($location.absUrl() !== newUrl) return;
11322
11323 if (defaultPrevented) {
11324 $location.$$parse(oldUrl);
11325 $location.$$state = oldState;
11326 } else {
11327 if (urlOrStateChanged) {
11328 setBrowserUrlWithFallback(newUrl, currentReplace,
11329 oldState === $location.$$state ? null : $location.$$state);
11330 }
11331 afterLocationChange(oldUrl, oldState);
11332 }
11333 });
11334 }
11335
11336 $location.$$replace = false;
11337
11338 // we don't need to return anything because $evalAsync will make the digest loop dirty when
11339 // there is a change
11340 });
11341
11342 return $location;
11343
11344 function afterLocationChange(oldUrl, oldState) {
11345 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
11346 $location.$$state, oldState);
11347 }
11348}];
11349}
11350
11351/**
11352 * @ngdoc service
11353 * @name $log
11354 * @requires $window
11355 *
11356 * @description
11357 * Simple service for logging. Default implementation safely writes the message
11358 * into the browser's console (if present).
11359 *
11360 * The main purpose of this service is to simplify debugging and troubleshooting.
11361 *
11362 * The default is to log `debug` messages. You can use
11363 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
11364 *
11365 * @example
11366 <example module="logExample">
11367 <file name="script.js">
11368 angular.module('logExample', [])
11369 .controller('LogController', ['$scope', '$log', function($scope, $log) {
11370 $scope.$log = $log;
11371 $scope.message = 'Hello World!';
11372 }]);
11373 </file>
11374 <file name="index.html">
11375 <div ng-controller="LogController">
11376 <p>Reload this page with open console, enter text and hit the log button...</p>
11377 Message:
11378 <input type="text" ng-model="message"/>
11379 <button ng-click="$log.log(message)">log</button>
11380 <button ng-click="$log.warn(message)">warn</button>
11381 <button ng-click="$log.info(message)">info</button>
11382 <button ng-click="$log.error(message)">error</button>
11383 </div>
11384 </file>
11385 </example>
11386 */
11387
11388/**
11389 * @ngdoc provider
11390 * @name $logProvider
11391 * @description
11392 * Use the `$logProvider` to configure how the application logs messages
11393 */
11394function $LogProvider() {
11395 var debug = true,
11396 self = this;
11397
11398 /**
11399 * @ngdoc method
11400 * @name $logProvider#debugEnabled
11401 * @description
11402 * @param {boolean=} flag enable or disable debug level messages
11403 * @returns {*} current value if used as getter or itself (chaining) if used as setter
11404 */
11405 this.debugEnabled = function(flag) {
11406 if (isDefined(flag)) {
11407 debug = flag;
11408 return this;
11409 } else {
11410 return debug;
11411 }
11412 };
11413
11414 this.$get = ['$window', function($window) {
11415 return {
11416 /**
11417 * @ngdoc method
11418 * @name $log#log
11419 *
11420 * @description
11421 * Write a log message
11422 */
11423 log: consoleLog('log'),
11424
11425 /**
11426 * @ngdoc method
11427 * @name $log#info
11428 *
11429 * @description
11430 * Write an information message
11431 */
11432 info: consoleLog('info'),
11433
11434 /**
11435 * @ngdoc method
11436 * @name $log#warn
11437 *
11438 * @description
11439 * Write a warning message
11440 */
11441 warn: consoleLog('warn'),
11442
11443 /**
11444 * @ngdoc method
11445 * @name $log#error
11446 *
11447 * @description
11448 * Write an error message
11449 */
11450 error: consoleLog('error'),
11451
11452 /**
11453 * @ngdoc method
11454 * @name $log#debug
11455 *
11456 * @description
11457 * Write a debug message
11458 */
11459 debug: (function() {
11460 var fn = consoleLog('debug');
11461
11462 return function() {
11463 if (debug) {
11464 fn.apply(self, arguments);
11465 }
11466 };
11467 }())
11468 };
11469
11470 function formatError(arg) {
11471 if (arg instanceof Error) {
11472 if (arg.stack) {
11473 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
11474 ? 'Error: ' + arg.message + '\n' + arg.stack
11475 : arg.stack;
11476 } else if (arg.sourceURL) {
11477 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
11478 }
11479 }
11480 return arg;
11481 }
11482
11483 function consoleLog(type) {
11484 var console = $window.console || {},
11485 logFn = console[type] || console.log || noop,
11486 hasApply = false;
11487
11488 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
11489 // The reason behind this is that console.log has type "object" in IE8...
11490 try {
11491 hasApply = !!logFn.apply;
11492 } catch (e) {}
11493
11494 if (hasApply) {
11495 return function() {
11496 var args = [];
11497 forEach(arguments, function(arg) {
11498 args.push(formatError(arg));
11499 });
11500 return logFn.apply(console, args);
11501 };
11502 }
11503
11504 // we are IE which either doesn't have window.console => this is noop and we do nothing,
11505 // or we are IE where console.log doesn't have apply so we log at least first 2 args
11506 return function(arg1, arg2) {
11507 logFn(arg1, arg2 == null ? '' : arg2);
11508 };
11509 }
11510 }];
11511}
11512
11513var $parseMinErr = minErr('$parse');
11514
11515// Sandboxing Angular Expressions
11516// ------------------------------
11517// Angular expressions are generally considered safe because these expressions only have direct
11518// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
11519// obtaining a reference to native JS functions such as the Function constructor.
11520//
11521// As an example, consider the following Angular expression:
11522//
11523// {}.toString.constructor('alert("evil JS code")')
11524//
11525// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
11526// against the expression language, but not to prevent exploits that were enabled by exposing
11527// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
11528// practice and therefore we are not even trying to protect against interaction with an object
11529// explicitly exposed in this way.
11530//
11531// In general, it is not possible to access a Window object from an angular expression unless a
11532// window or some DOM object that has a reference to window is published onto a Scope.
11533// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
11534// native objects.
11535//
11536// See https://docs.angularjs.org/guide/security
11537
11538
11539function ensureSafeMemberName(name, fullExpression) {
11540 if (name === "__defineGetter__" || name === "__defineSetter__"
11541 || name === "__lookupGetter__" || name === "__lookupSetter__"
11542 || name === "__proto__") {
11543 throw $parseMinErr('isecfld',
11544 'Attempting to access a disallowed field in Angular expressions! '
11545 + 'Expression: {0}', fullExpression);
11546 }
11547 return name;
11548}
11549
11550function ensureSafeObject(obj, fullExpression) {
11551 // nifty check if obj is Function that is fast and works across iframes and other contexts
11552 if (obj) {
11553 if (obj.constructor === obj) {
11554 throw $parseMinErr('isecfn',
11555 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11556 fullExpression);
11557 } else if (// isWindow(obj)
11558 obj.window === obj) {
11559 throw $parseMinErr('isecwindow',
11560 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
11561 fullExpression);
11562 } else if (// isElement(obj)
11563 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
11564 throw $parseMinErr('isecdom',
11565 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
11566 fullExpression);
11567 } else if (// block Object so that we can't get hold of dangerous Object.* methods
11568 obj === Object) {
11569 throw $parseMinErr('isecobj',
11570 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
11571 fullExpression);
11572 }
11573 }
11574 return obj;
11575}
11576
11577var CALL = Function.prototype.call;
11578var APPLY = Function.prototype.apply;
11579var BIND = Function.prototype.bind;
11580
11581function ensureSafeFunction(obj, fullExpression) {
11582 if (obj) {
11583 if (obj.constructor === obj) {
11584 throw $parseMinErr('isecfn',
11585 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
11586 fullExpression);
11587 } else if (obj === CALL || obj === APPLY || obj === BIND) {
11588 throw $parseMinErr('isecff',
11589 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
11590 fullExpression);
11591 }
11592 }
11593}
11594
11595//Keyword constants
11596var CONSTANTS = createMap();
11597forEach({
11598 'null': function() { return null; },
11599 'true': function() { return true; },
11600 'false': function() { return false; },
11601 'undefined': function() {}
11602}, function(constantGetter, name) {
11603 constantGetter.constant = constantGetter.literal = constantGetter.sharedGetter = true;
11604 CONSTANTS[name] = constantGetter;
11605});
11606
11607//Not quite a constant, but can be lex/parsed the same
11608CONSTANTS['this'] = function(self) { return self; };
11609CONSTANTS['this'].sharedGetter = true;
11610
11611
11612//Operators - will be wrapped by binaryFn/unaryFn/assignment/filter
11613var OPERATORS = extend(createMap(), {
11614 '+':function(self, locals, a, b) {
11615 a=a(self, locals); b=b(self, locals);
11616 if (isDefined(a)) {
11617 if (isDefined(b)) {
11618 return a + b;
11619 }
11620 return a;
11621 }
11622 return isDefined(b) ? b : undefined;},
11623 '-':function(self, locals, a, b) {
11624 a=a(self, locals); b=b(self, locals);
11625 return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
11626 },
11627 '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);},
11628 '/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);},
11629 '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);},
11630 '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);},
11631 '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);},
11632 '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);},
11633 '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);},
11634 '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);},
11635 '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);},
11636 '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);},
11637 '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);},
11638 '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);},
11639 '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);},
11640 '!':function(self, locals, a) {return !a(self, locals);},
11641
11642 //Tokenized as operators but parsed as assignment/filters
11643 '=':true,
11644 '|':true
11645});
11646var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
11647
11648
11649/////////////////////////////////////////
11650
11651
11652/**
11653 * @constructor
11654 */
11655var Lexer = function(options) {
11656 this.options = options;
11657};
11658
11659Lexer.prototype = {
11660 constructor: Lexer,
11661
11662 lex: function(text) {
11663 this.text = text;
11664 this.index = 0;
11665 this.tokens = [];
11666
11667 while (this.index < this.text.length) {
11668 var ch = this.text.charAt(this.index);
11669 if (ch === '"' || ch === "'") {
11670 this.readString(ch);
11671 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
11672 this.readNumber();
11673 } else if (this.isIdent(ch)) {
11674 this.readIdent();
11675 } else if (this.is(ch, '(){}[].,;:?')) {
11676 this.tokens.push({index: this.index, text: ch});
11677 this.index++;
11678 } else if (this.isWhitespace(ch)) {
11679 this.index++;
11680 } else {
11681 var ch2 = ch + this.peek();
11682 var ch3 = ch2 + this.peek(2);
11683 var op1 = OPERATORS[ch];
11684 var op2 = OPERATORS[ch2];
11685 var op3 = OPERATORS[ch3];
11686 if (op1 || op2 || op3) {
11687 var token = op3 ? ch3 : (op2 ? ch2 : ch);
11688 this.tokens.push({index: this.index, text: token, operator: true});
11689 this.index += token.length;
11690 } else {
11691 this.throwError('Unexpected next character ', this.index, this.index + 1);
11692 }
11693 }
11694 }
11695 return this.tokens;
11696 },
11697
11698 is: function(ch, chars) {
11699 return chars.indexOf(ch) !== -1;
11700 },
11701
11702 peek: function(i) {
11703 var num = i || 1;
11704 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
11705 },
11706
11707 isNumber: function(ch) {
11708 return ('0' <= ch && ch <= '9') && typeof ch === "string";
11709 },
11710
11711 isWhitespace: function(ch) {
11712 // IE treats non-breaking space as \u00A0
11713 return (ch === ' ' || ch === '\r' || ch === '\t' ||
11714 ch === '\n' || ch === '\v' || ch === '\u00A0');
11715 },
11716
11717 isIdent: function(ch) {
11718 return ('a' <= ch && ch <= 'z' ||
11719 'A' <= ch && ch <= 'Z' ||
11720 '_' === ch || ch === '$');
11721 },
11722
11723 isExpOperator: function(ch) {
11724 return (ch === '-' || ch === '+' || this.isNumber(ch));
11725 },
11726
11727 throwError: function(error, start, end) {
11728 end = end || this.index;
11729 var colStr = (isDefined(start)
11730 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
11731 : ' ' + end);
11732 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
11733 error, colStr, this.text);
11734 },
11735
11736 readNumber: function() {
11737 var number = '';
11738 var start = this.index;
11739 while (this.index < this.text.length) {
11740 var ch = lowercase(this.text.charAt(this.index));
11741 if (ch == '.' || this.isNumber(ch)) {
11742 number += ch;
11743 } else {
11744 var peekCh = this.peek();
11745 if (ch == 'e' && this.isExpOperator(peekCh)) {
11746 number += ch;
11747 } else if (this.isExpOperator(ch) &&
11748 peekCh && this.isNumber(peekCh) &&
11749 number.charAt(number.length - 1) == 'e') {
11750 number += ch;
11751 } else if (this.isExpOperator(ch) &&
11752 (!peekCh || !this.isNumber(peekCh)) &&
11753 number.charAt(number.length - 1) == 'e') {
11754 this.throwError('Invalid exponent');
11755 } else {
11756 break;
11757 }
11758 }
11759 this.index++;
11760 }
11761 this.tokens.push({
11762 index: start,
11763 text: number,
11764 constant: true,
11765 value: Number(number)
11766 });
11767 },
11768
11769 readIdent: function() {
11770 var start = this.index;
11771 while (this.index < this.text.length) {
11772 var ch = this.text.charAt(this.index);
11773 if (!(this.isIdent(ch) || this.isNumber(ch))) {
11774 break;
11775 }
11776 this.index++;
11777 }
11778 this.tokens.push({
11779 index: start,
11780 text: this.text.slice(start, this.index),
11781 identifier: true
11782 });
11783 },
11784
11785 readString: function(quote) {
11786 var start = this.index;
11787 this.index++;
11788 var string = '';
11789 var rawString = quote;
11790 var escape = false;
11791 while (this.index < this.text.length) {
11792 var ch = this.text.charAt(this.index);
11793 rawString += ch;
11794 if (escape) {
11795 if (ch === 'u') {
11796 var hex = this.text.substring(this.index + 1, this.index + 5);
11797 if (!hex.match(/[\da-f]{4}/i))
11798 this.throwError('Invalid unicode escape [\\u' + hex + ']');
11799 this.index += 4;
11800 string += String.fromCharCode(parseInt(hex, 16));
11801 } else {
11802 var rep = ESCAPE[ch];
11803 string = string + (rep || ch);
11804 }
11805 escape = false;
11806 } else if (ch === '\\') {
11807 escape = true;
11808 } else if (ch === quote) {
11809 this.index++;
11810 this.tokens.push({
11811 index: start,
11812 text: rawString,
11813 constant: true,
11814 value: string
11815 });
11816 return;
11817 } else {
11818 string += ch;
11819 }
11820 this.index++;
11821 }
11822 this.throwError('Unterminated quote', start);
11823 }
11824};
11825
11826
11827function isConstant(exp) {
11828 return exp.constant;
11829}
11830
11831/**
11832 * @constructor
11833 */
11834var Parser = function(lexer, $filter, options) {
11835 this.lexer = lexer;
11836 this.$filter = $filter;
11837 this.options = options;
11838};
11839
11840Parser.ZERO = extend(function() {
11841 return 0;
11842}, {
11843 sharedGetter: true,
11844 constant: true
11845});
11846
11847Parser.prototype = {
11848 constructor: Parser,
11849
11850 parse: function(text) {
11851 this.text = text;
11852 this.tokens = this.lexer.lex(text);
11853
11854 var value = this.statements();
11855
11856 if (this.tokens.length !== 0) {
11857 this.throwError('is an unexpected token', this.tokens[0]);
11858 }
11859
11860 value.literal = !!value.literal;
11861 value.constant = !!value.constant;
11862
11863 return value;
11864 },
11865
11866 primary: function() {
11867 var primary;
11868 if (this.expect('(')) {
11869 primary = this.filterChain();
11870 this.consume(')');
11871 } else if (this.expect('[')) {
11872 primary = this.arrayDeclaration();
11873 } else if (this.expect('{')) {
11874 primary = this.object();
11875 } else if (this.peek().identifier) {
11876 primary = this.identifier();
11877 } else if (this.peek().constant) {
11878 primary = this.constant();
11879 } else {
11880 this.throwError('not a primary expression', this.peek());
11881 }
11882
11883 var next, context;
11884 while ((next = this.expect('(', '[', '.'))) {
11885 if (next.text === '(') {
11886 primary = this.functionCall(primary, context);
11887 context = null;
11888 } else if (next.text === '[') {
11889 context = primary;
11890 primary = this.objectIndex(primary);
11891 } else if (next.text === '.') {
11892 context = primary;
11893 primary = this.fieldAccess(primary);
11894 } else {
11895 this.throwError('IMPOSSIBLE');
11896 }
11897 }
11898 return primary;
11899 },
11900
11901 throwError: function(msg, token) {
11902 throw $parseMinErr('syntax',
11903 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
11904 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
11905 },
11906
11907 peekToken: function() {
11908 if (this.tokens.length === 0)
11909 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
11910 return this.tokens[0];
11911 },
11912
11913 peek: function(e1, e2, e3, e4) {
11914 return this.peekAhead(0, e1, e2, e3, e4);
11915 },
11916 peekAhead: function(i, e1, e2, e3, e4) {
11917 if (this.tokens.length > i) {
11918 var token = this.tokens[i];
11919 var t = token.text;
11920 if (t === e1 || t === e2 || t === e3 || t === e4 ||
11921 (!e1 && !e2 && !e3 && !e4)) {
11922 return token;
11923 }
11924 }
11925 return false;
11926 },
11927
11928 expect: function(e1, e2, e3, e4) {
11929 var token = this.peek(e1, e2, e3, e4);
11930 if (token) {
11931 this.tokens.shift();
11932 return token;
11933 }
11934 return false;
11935 },
11936
11937 consume: function(e1) {
11938 if (this.tokens.length === 0) {
11939 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
11940 }
11941
11942 var token = this.expect(e1);
11943 if (!token) {
11944 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
11945 }
11946 return token;
11947 },
11948
11949 unaryFn: function(op, right) {
11950 var fn = OPERATORS[op];
11951 return extend(function $parseUnaryFn(self, locals) {
11952 return fn(self, locals, right);
11953 }, {
11954 constant:right.constant,
11955 inputs: [right]
11956 });
11957 },
11958
11959 binaryFn: function(left, op, right, isBranching) {
11960 var fn = OPERATORS[op];
11961 return extend(function $parseBinaryFn(self, locals) {
11962 return fn(self, locals, left, right);
11963 }, {
11964 constant: left.constant && right.constant,
11965 inputs: !isBranching && [left, right]
11966 });
11967 },
11968
11969 identifier: function() {
11970 var id = this.consume().text;
11971
11972 //Continue reading each `.identifier` unless it is a method invocation
11973 while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
11974 id += this.consume().text + this.consume().text;
11975 }
11976
11977 return CONSTANTS[id] || getterFn(id, this.options, this.text);
11978 },
11979
11980 constant: function() {
11981 var value = this.consume().value;
11982
11983 return extend(function $parseConstant() {
11984 return value;
11985 }, {
11986 constant: true,
11987 literal: true
11988 });
11989 },
11990
11991 statements: function() {
11992 var statements = [];
11993 while (true) {
11994 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
11995 statements.push(this.filterChain());
11996 if (!this.expect(';')) {
11997 // optimize for the common case where there is only one statement.
11998 // TODO(size): maybe we should not support multiple statements?
11999 return (statements.length === 1)
12000 ? statements[0]
12001 : function $parseStatements(self, locals) {
12002 var value;
12003 for (var i = 0, ii = statements.length; i < ii; i++) {
12004 value = statements[i](self, locals);
12005 }
12006 return value;
12007 };
12008 }
12009 }
12010 },
12011
12012 filterChain: function() {
12013 var left = this.expression();
12014 var token;
12015 while ((token = this.expect('|'))) {
12016 left = this.filter(left);
12017 }
12018 return left;
12019 },
12020
12021 filter: function(inputFn) {
12022 var fn = this.$filter(this.consume().text);
12023 var argsFn;
12024 var args;
12025
12026 if (this.peek(':')) {
12027 argsFn = [];
12028 args = []; // we can safely reuse the array
12029 while (this.expect(':')) {
12030 argsFn.push(this.expression());
12031 }
12032 }
12033
12034 var inputs = [inputFn].concat(argsFn || []);
12035
12036 return extend(function $parseFilter(self, locals) {
12037 var input = inputFn(self, locals);
12038 if (args) {
12039 args[0] = input;
12040
12041 var i = argsFn.length;
12042 while (i--) {
12043 args[i + 1] = argsFn[i](self, locals);
12044 }
12045
12046 return fn.apply(undefined, args);
12047 }
12048
12049 return fn(input);
12050 }, {
12051 constant: !fn.$stateful && inputs.every(isConstant),
12052 inputs: !fn.$stateful && inputs
12053 });
12054 },
12055
12056 expression: function() {
12057 return this.assignment();
12058 },
12059
12060 assignment: function() {
12061 var left = this.ternary();
12062 var right;
12063 var token;
12064 if ((token = this.expect('='))) {
12065 if (!left.assign) {
12066 this.throwError('implies assignment but [' +
12067 this.text.substring(0, token.index) + '] can not be assigned to', token);
12068 }
12069 right = this.ternary();
12070 return extend(function $parseAssignment(scope, locals) {
12071 return left.assign(scope, right(scope, locals), locals);
12072 }, {
12073 inputs: [left, right]
12074 });
12075 }
12076 return left;
12077 },
12078
12079 ternary: function() {
12080 var left = this.logicalOR();
12081 var middle;
12082 var token;
12083 if ((token = this.expect('?'))) {
12084 middle = this.assignment();
12085 if (this.consume(':')) {
12086 var right = this.assignment();
12087
12088 return extend(function $parseTernary(self, locals) {
12089 return left(self, locals) ? middle(self, locals) : right(self, locals);
12090 }, {
12091 constant: left.constant && middle.constant && right.constant
12092 });
12093 }
12094 }
12095
12096 return left;
12097 },
12098
12099 logicalOR: function() {
12100 var left = this.logicalAND();
12101 var token;
12102 while ((token = this.expect('||'))) {
12103 left = this.binaryFn(left, token.text, this.logicalAND(), true);
12104 }
12105 return left;
12106 },
12107
12108 logicalAND: function() {
12109 var left = this.equality();
12110 var token;
12111 if ((token = this.expect('&&'))) {
12112 left = this.binaryFn(left, token.text, this.logicalAND(), true);
12113 }
12114 return left;
12115 },
12116
12117 equality: function() {
12118 var left = this.relational();
12119 var token;
12120 if ((token = this.expect('==','!=','===','!=='))) {
12121 left = this.binaryFn(left, token.text, this.equality());
12122 }
12123 return left;
12124 },
12125
12126 relational: function() {
12127 var left = this.additive();
12128 var token;
12129 if ((token = this.expect('<', '>', '<=', '>='))) {
12130 left = this.binaryFn(left, token.text, this.relational());
12131 }
12132 return left;
12133 },
12134
12135 additive: function() {
12136 var left = this.multiplicative();
12137 var token;
12138 while ((token = this.expect('+','-'))) {
12139 left = this.binaryFn(left, token.text, this.multiplicative());
12140 }
12141 return left;
12142 },
12143
12144 multiplicative: function() {
12145 var left = this.unary();
12146 var token;
12147 while ((token = this.expect('*','/','%'))) {
12148 left = this.binaryFn(left, token.text, this.unary());
12149 }
12150 return left;
12151 },
12152
12153 unary: function() {
12154 var token;
12155 if (this.expect('+')) {
12156 return this.primary();
12157 } else if ((token = this.expect('-'))) {
12158 return this.binaryFn(Parser.ZERO, token.text, this.unary());
12159 } else if ((token = this.expect('!'))) {
12160 return this.unaryFn(token.text, this.unary());
12161 } else {
12162 return this.primary();
12163 }
12164 },
12165
12166 fieldAccess: function(object) {
12167 var expression = this.text;
12168 var field = this.consume().text;
12169 var getter = getterFn(field, this.options, expression);
12170
12171 return extend(function $parseFieldAccess(scope, locals, self) {
12172 return getter(self || object(scope, locals));
12173 }, {
12174 assign: function(scope, value, locals) {
12175 var o = object(scope, locals);
12176 if (!o) object.assign(scope, o = {});
12177 return setter(o, field, value, expression);
12178 }
12179 });
12180 },
12181
12182 objectIndex: function(obj) {
12183 var expression = this.text;
12184
12185 var indexFn = this.expression();
12186 this.consume(']');
12187
12188 return extend(function $parseObjectIndex(self, locals) {
12189 var o = obj(self, locals),
12190 i = indexFn(self, locals),
12191 v;
12192
12193 ensureSafeMemberName(i, expression);
12194 if (!o) return undefined;
12195 v = ensureSafeObject(o[i], expression);
12196 return v;
12197 }, {
12198 assign: function(self, value, locals) {
12199 var key = ensureSafeMemberName(indexFn(self, locals), expression);
12200 // prevent overwriting of Function.constructor which would break ensureSafeObject check
12201 var o = ensureSafeObject(obj(self, locals), expression);
12202 if (!o) obj.assign(self, o = {});
12203 return o[key] = value;
12204 }
12205 });
12206 },
12207
12208 functionCall: function(fnGetter, contextGetter) {
12209 var argsFn = [];
12210 if (this.peekToken().text !== ')') {
12211 do {
12212 argsFn.push(this.expression());
12213 } while (this.expect(','));
12214 }
12215 this.consume(')');
12216
12217 var expressionText = this.text;
12218 // we can safely reuse the array across invocations
12219 var args = argsFn.length ? [] : null;
12220
12221 return function $parseFunctionCall(scope, locals) {
12222 var context = contextGetter ? contextGetter(scope, locals) : scope;
12223 var fn = fnGetter(scope, locals, context) || noop;
12224
12225 if (args) {
12226 var i = argsFn.length;
12227 while (i--) {
12228 args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
12229 }
12230 }
12231
12232 ensureSafeObject(context, expressionText);
12233 ensureSafeFunction(fn, expressionText);
12234
12235 // IE doesn't have apply for some native functions
12236 var v = fn.apply
12237 ? fn.apply(context, args)
12238 : fn(args[0], args[1], args[2], args[3], args[4]);
12239
12240 return ensureSafeObject(v, expressionText);
12241 };
12242 },
12243
12244 // This is used with json array declaration
12245 arrayDeclaration: function() {
12246 var elementFns = [];
12247 if (this.peekToken().text !== ']') {
12248 do {
12249 if (this.peek(']')) {
12250 // Support trailing commas per ES5.1.
12251 break;
12252 }
12253 elementFns.push(this.expression());
12254 } while (this.expect(','));
12255 }
12256 this.consume(']');
12257
12258 return extend(function $parseArrayLiteral(self, locals) {
12259 var array = [];
12260 for (var i = 0, ii = elementFns.length; i < ii; i++) {
12261 array.push(elementFns[i](self, locals));
12262 }
12263 return array;
12264 }, {
12265 literal: true,
12266 constant: elementFns.every(isConstant),
12267 inputs: elementFns
12268 });
12269 },
12270
12271 object: function() {
12272 var keys = [], valueFns = [];
12273 if (this.peekToken().text !== '}') {
12274 do {
12275 if (this.peek('}')) {
12276 // Support trailing commas per ES5.1.
12277 break;
12278 }
12279 var token = this.consume();
12280 if (token.constant) {
12281 keys.push(token.value);
12282 } else if (token.identifier) {
12283 keys.push(token.text);
12284 } else {
12285 this.throwError("invalid key", token);
12286 }
12287 this.consume(':');
12288 valueFns.push(this.expression());
12289 } while (this.expect(','));
12290 }
12291 this.consume('}');
12292
12293 return extend(function $parseObjectLiteral(self, locals) {
12294 var object = {};
12295 for (var i = 0, ii = valueFns.length; i < ii; i++) {
12296 object[keys[i]] = valueFns[i](self, locals);
12297 }
12298 return object;
12299 }, {
12300 literal: true,
12301 constant: valueFns.every(isConstant),
12302 inputs: valueFns
12303 });
12304 }
12305};
12306
12307
12308//////////////////////////////////////////////////
12309// Parser helper functions
12310//////////////////////////////////////////////////
12311
12312function setter(obj, path, setValue, fullExp) {
12313 ensureSafeObject(obj, fullExp);
12314
12315 var element = path.split('.'), key;
12316 for (var i = 0; element.length > 1; i++) {
12317 key = ensureSafeMemberName(element.shift(), fullExp);
12318 var propertyObj = ensureSafeObject(obj[key], fullExp);
12319 if (!propertyObj) {
12320 propertyObj = {};
12321 obj[key] = propertyObj;
12322 }
12323 obj = propertyObj;
12324 }
12325 key = ensureSafeMemberName(element.shift(), fullExp);
12326 ensureSafeObject(obj[key], fullExp);
12327 obj[key] = setValue;
12328 return setValue;
12329}
12330
12331var getterFnCacheDefault = createMap();
12332var getterFnCacheExpensive = createMap();
12333
12334function isPossiblyDangerousMemberName(name) {
12335 return name == 'constructor';
12336}
12337
12338/**
12339 * Implementation of the "Black Hole" variant from:
12340 * - http://jsperf.com/angularjs-parse-getter/4
12341 * - http://jsperf.com/path-evaluation-simplified/7
12342 */
12343function cspSafeGetterFn(key0, key1, key2, key3, key4, fullExp, expensiveChecks) {
12344 ensureSafeMemberName(key0, fullExp);
12345 ensureSafeMemberName(key1, fullExp);
12346 ensureSafeMemberName(key2, fullExp);
12347 ensureSafeMemberName(key3, fullExp);
12348 ensureSafeMemberName(key4, fullExp);
12349 var eso = function(o) {
12350 return ensureSafeObject(o, fullExp);
12351 };
12352 var eso0 = (expensiveChecks || isPossiblyDangerousMemberName(key0)) ? eso : identity;
12353 var eso1 = (expensiveChecks || isPossiblyDangerousMemberName(key1)) ? eso : identity;
12354 var eso2 = (expensiveChecks || isPossiblyDangerousMemberName(key2)) ? eso : identity;
12355 var eso3 = (expensiveChecks || isPossiblyDangerousMemberName(key3)) ? eso : identity;
12356 var eso4 = (expensiveChecks || isPossiblyDangerousMemberName(key4)) ? eso : identity;
12357
12358 return function cspSafeGetter(scope, locals) {
12359 var pathVal = (locals && locals.hasOwnProperty(key0)) ? locals : scope;
12360
12361 if (pathVal == null) return pathVal;
12362 pathVal = eso0(pathVal[key0]);
12363
12364 if (!key1) return pathVal;
12365 if (pathVal == null) return undefined;
12366 pathVal = eso1(pathVal[key1]);
12367
12368 if (!key2) return pathVal;
12369 if (pathVal == null) return undefined;
12370 pathVal = eso2(pathVal[key2]);
12371
12372 if (!key3) return pathVal;
12373 if (pathVal == null) return undefined;
12374 pathVal = eso3(pathVal[key3]);
12375
12376 if (!key4) return pathVal;
12377 if (pathVal == null) return undefined;
12378 pathVal = eso4(pathVal[key4]);
12379
12380 return pathVal;
12381 };
12382}
12383
12384function getterFnWithEnsureSafeObject(fn, fullExpression) {
12385 return function(s, l) {
12386 return fn(s, l, ensureSafeObject, fullExpression);
12387 };
12388}
12389
12390function getterFn(path, options, fullExp) {
12391 var expensiveChecks = options.expensiveChecks;
12392 var getterFnCache = (expensiveChecks ? getterFnCacheExpensive : getterFnCacheDefault);
12393 var fn = getterFnCache[path];
12394 if (fn) return fn;
12395
12396
12397 var pathKeys = path.split('.'),
12398 pathKeysLength = pathKeys.length;
12399
12400 // http://jsperf.com/angularjs-parse-getter/6
12401 if (options.csp) {
12402 if (pathKeysLength < 6) {
12403 fn = cspSafeGetterFn(pathKeys[0], pathKeys[1], pathKeys[2], pathKeys[3], pathKeys[4], fullExp, expensiveChecks);
12404 } else {
12405 fn = function cspSafeGetter(scope, locals) {
12406 var i = 0, val;
12407 do {
12408 val = cspSafeGetterFn(pathKeys[i++], pathKeys[i++], pathKeys[i++], pathKeys[i++],
12409 pathKeys[i++], fullExp, expensiveChecks)(scope, locals);
12410
12411 locals = undefined; // clear after first iteration
12412 scope = val;
12413 } while (i < pathKeysLength);
12414 return val;
12415 };
12416 }
12417 } else {
12418 var code = '';
12419 if (expensiveChecks) {
12420 code += 's = eso(s, fe);\nl = eso(l, fe);\n';
12421 }
12422 var needsEnsureSafeObject = expensiveChecks;
12423 forEach(pathKeys, function(key, index) {
12424 ensureSafeMemberName(key, fullExp);
12425 var lookupJs = (index
12426 // we simply dereference 's' on any .dot notation
12427 ? 's'
12428 // but if we are first then we check locals first, and if so read it first
12429 : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key;
12430 if (expensiveChecks || isPossiblyDangerousMemberName(key)) {
12431 lookupJs = 'eso(' + lookupJs + ', fe)';
12432 needsEnsureSafeObject = true;
12433 }
12434 code += 'if(s == null) return undefined;\n' +
12435 's=' + lookupJs + ';\n';
12436 });
12437 code += 'return s;';
12438
12439 /* jshint -W054 */
12440 var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject
12441 /* jshint +W054 */
12442 evaledFnGetter.toString = valueFn(code);
12443 if (needsEnsureSafeObject) {
12444 evaledFnGetter = getterFnWithEnsureSafeObject(evaledFnGetter, fullExp);
12445 }
12446 fn = evaledFnGetter;
12447 }
12448
12449 fn.sharedGetter = true;
12450 fn.assign = function(self, value) {
12451 return setter(self, path, value, path);
12452 };
12453 getterFnCache[path] = fn;
12454 return fn;
12455}
12456
12457var objectValueOf = Object.prototype.valueOf;
12458
12459function getValueOf(value) {
12460 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
12461}
12462
12463///////////////////////////////////
12464
12465/**
12466 * @ngdoc service
12467 * @name $parse
12468 * @kind function
12469 *
12470 * @description
12471 *
12472 * Converts Angular {@link guide/expression expression} into a function.
12473 *
12474 * ```js
12475 * var getter = $parse('user.name');
12476 * var setter = getter.assign;
12477 * var context = {user:{name:'angular'}};
12478 * var locals = {user:{name:'local'}};
12479 *
12480 * expect(getter(context)).toEqual('angular');
12481 * setter(context, 'newValue');
12482 * expect(context.user.name).toEqual('newValue');
12483 * expect(getter(context, locals)).toEqual('local');
12484 * ```
12485 *
12486 *
12487 * @param {string} expression String expression to compile.
12488 * @returns {function(context, locals)} a function which represents the compiled expression:
12489 *
12490 * * `context` – `{object}` – an object against which any expressions embedded in the strings
12491 * are evaluated against (typically a scope object).
12492 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
12493 * `context`.
12494 *
12495 * The returned function also has the following properties:
12496 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
12497 * literal.
12498 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
12499 * constant literals.
12500 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
12501 * set to a function to change its value on the given context.
12502 *
12503 */
12504
12505
12506/**
12507 * @ngdoc provider
12508 * @name $parseProvider
12509 *
12510 * @description
12511 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
12512 * service.
12513 */
12514function $ParseProvider() {
12515 var cacheDefault = createMap();
12516 var cacheExpensive = createMap();
12517
12518
12519
12520 this.$get = ['$filter', '$sniffer', function($filter, $sniffer) {
12521 var $parseOptions = {
12522 csp: $sniffer.csp,
12523 expensiveChecks: false
12524 },
12525 $parseOptionsExpensive = {
12526 csp: $sniffer.csp,
12527 expensiveChecks: true
12528 };
12529
12530 function wrapSharedExpression(exp) {
12531 var wrapped = exp;
12532
12533 if (exp.sharedGetter) {
12534 wrapped = function $parseWrapper(self, locals) {
12535 return exp(self, locals);
12536 };
12537 wrapped.literal = exp.literal;
12538 wrapped.constant = exp.constant;
12539 wrapped.assign = exp.assign;
12540 }
12541
12542 return wrapped;
12543 }
12544
12545 return function $parse(exp, interceptorFn, expensiveChecks) {
12546 var parsedExpression, oneTime, cacheKey;
12547
12548 switch (typeof exp) {
12549 case 'string':
12550 cacheKey = exp = exp.trim();
12551
12552 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
12553 parsedExpression = cache[cacheKey];
12554
12555 if (!parsedExpression) {
12556 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
12557 oneTime = true;
12558 exp = exp.substring(2);
12559 }
12560
12561 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
12562 var lexer = new Lexer(parseOptions);
12563 var parser = new Parser(lexer, $filter, parseOptions);
12564 parsedExpression = parser.parse(exp);
12565
12566 if (parsedExpression.constant) {
12567 parsedExpression.$$watchDelegate = constantWatchDelegate;
12568 } else if (oneTime) {
12569 //oneTime is not part of the exp passed to the Parser so we may have to
12570 //wrap the parsedExpression before adding a $$watchDelegate
12571 parsedExpression = wrapSharedExpression(parsedExpression);
12572 parsedExpression.$$watchDelegate = parsedExpression.literal ?
12573 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
12574 } else if (parsedExpression.inputs) {
12575 parsedExpression.$$watchDelegate = inputsWatchDelegate;
12576 }
12577
12578 cache[cacheKey] = parsedExpression;
12579 }
12580 return addInterceptor(parsedExpression, interceptorFn);
12581
12582 case 'function':
12583 return addInterceptor(exp, interceptorFn);
12584
12585 default:
12586 return addInterceptor(noop, interceptorFn);
12587 }
12588 };
12589
12590 function collectExpressionInputs(inputs, list) {
12591 for (var i = 0, ii = inputs.length; i < ii; i++) {
12592 var input = inputs[i];
12593 if (!input.constant) {
12594 if (input.inputs) {
12595 collectExpressionInputs(input.inputs, list);
12596 } else if (list.indexOf(input) === -1) { // TODO(perf) can we do better?
12597 list.push(input);
12598 }
12599 }
12600 }
12601
12602 return list;
12603 }
12604
12605 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
12606
12607 if (newValue == null || oldValueOfValue == null) { // null/undefined
12608 return newValue === oldValueOfValue;
12609 }
12610
12611 if (typeof newValue === 'object') {
12612
12613 // attempt to convert the value to a primitive type
12614 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
12615 // be cheaply dirty-checked
12616 newValue = getValueOf(newValue);
12617
12618 if (typeof newValue === 'object') {
12619 // objects/arrays are not supported - deep-watching them would be too expensive
12620 return false;
12621 }
12622
12623 // fall-through to the primitive equality check
12624 }
12625
12626 //Primitive or NaN
12627 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
12628 }
12629
12630 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12631 var inputExpressions = parsedExpression.$$inputs ||
12632 (parsedExpression.$$inputs = collectExpressionInputs(parsedExpression.inputs, []));
12633
12634 var lastResult;
12635
12636 if (inputExpressions.length === 1) {
12637 var oldInputValue = expressionInputDirtyCheck; // init to something unique so that equals check fails
12638 inputExpressions = inputExpressions[0];
12639 return scope.$watch(function expressionInputWatch(scope) {
12640 var newInputValue = inputExpressions(scope);
12641 if (!expressionInputDirtyCheck(newInputValue, oldInputValue)) {
12642 lastResult = parsedExpression(scope);
12643 oldInputValue = newInputValue && getValueOf(newInputValue);
12644 }
12645 return lastResult;
12646 }, listener, objectEquality);
12647 }
12648
12649 var oldInputValueOfValues = [];
12650 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12651 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
12652 }
12653
12654 return scope.$watch(function expressionInputsWatch(scope) {
12655 var changed = false;
12656
12657 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
12658 var newInputValue = inputExpressions[i](scope);
12659 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
12660 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
12661 }
12662 }
12663
12664 if (changed) {
12665 lastResult = parsedExpression(scope);
12666 }
12667
12668 return lastResult;
12669 }, listener, objectEquality);
12670 }
12671
12672 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12673 var unwatch, lastValue;
12674 return unwatch = scope.$watch(function oneTimeWatch(scope) {
12675 return parsedExpression(scope);
12676 }, function oneTimeListener(value, old, scope) {
12677 lastValue = value;
12678 if (isFunction(listener)) {
12679 listener.apply(this, arguments);
12680 }
12681 if (isDefined(value)) {
12682 scope.$$postDigest(function() {
12683 if (isDefined(lastValue)) {
12684 unwatch();
12685 }
12686 });
12687 }
12688 }, objectEquality);
12689 }
12690
12691 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12692 var unwatch, lastValue;
12693 return unwatch = scope.$watch(function oneTimeWatch(scope) {
12694 return parsedExpression(scope);
12695 }, function oneTimeListener(value, old, scope) {
12696 lastValue = value;
12697 if (isFunction(listener)) {
12698 listener.call(this, value, old, scope);
12699 }
12700 if (isAllDefined(value)) {
12701 scope.$$postDigest(function() {
12702 if (isAllDefined(lastValue)) unwatch();
12703 });
12704 }
12705 }, objectEquality);
12706
12707 function isAllDefined(value) {
12708 var allDefined = true;
12709 forEach(value, function(val) {
12710 if (!isDefined(val)) allDefined = false;
12711 });
12712 return allDefined;
12713 }
12714 }
12715
12716 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
12717 var unwatch;
12718 return unwatch = scope.$watch(function constantWatch(scope) {
12719 return parsedExpression(scope);
12720 }, function constantListener(value, old, scope) {
12721 if (isFunction(listener)) {
12722 listener.apply(this, arguments);
12723 }
12724 unwatch();
12725 }, objectEquality);
12726 }
12727
12728 function addInterceptor(parsedExpression, interceptorFn) {
12729 if (!interceptorFn) return parsedExpression;
12730 var watchDelegate = parsedExpression.$$watchDelegate;
12731
12732 var regularWatch =
12733 watchDelegate !== oneTimeLiteralWatchDelegate &&
12734 watchDelegate !== oneTimeWatchDelegate;
12735
12736 var fn = regularWatch ? function regularInterceptedExpression(scope, locals) {
12737 var value = parsedExpression(scope, locals);
12738 return interceptorFn(value, scope, locals);
12739 } : function oneTimeInterceptedExpression(scope, locals) {
12740 var value = parsedExpression(scope, locals);
12741 var result = interceptorFn(value, scope, locals);
12742 // we only return the interceptor's result if the
12743 // initial value is defined (for bind-once)
12744 return isDefined(value) ? result : value;
12745 };
12746
12747 // Propagate $$watchDelegates other then inputsWatchDelegate
12748 if (parsedExpression.$$watchDelegate &&
12749 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
12750 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
12751 } else if (!interceptorFn.$stateful) {
12752 // If there is an interceptor, but no watchDelegate then treat the interceptor like
12753 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
12754 fn.$$watchDelegate = inputsWatchDelegate;
12755 fn.inputs = [parsedExpression];
12756 }
12757
12758 return fn;
12759 }
12760 }];
12761}
12762
12763/**
12764 * @ngdoc service
12765 * @name $q
12766 * @requires $rootScope
12767 *
12768 * @description
12769 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
12770 * when they are done processing.
12771 *
12772 * This is an implementation of promises/deferred objects inspired by
12773 * [Kris Kowal's Q](https://github.com/kriskowal/q).
12774 *
12775 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
12776 * implementations, and the other which resembles ES6 promises to some degree.
12777 *
12778 * # $q constructor
12779 *
12780 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
12781 * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
12782 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
12783 *
12784 * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
12785 * available yet.
12786 *
12787 * It can be used like so:
12788 *
12789 * ```js
12790 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
12791 * // are available in the current lexical scope (they could have been injected or passed in).
12792 *
12793 * function asyncGreet(name) {
12794 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
12795 * return $q(function(resolve, reject) {
12796 * setTimeout(function() {
12797 * if (okToGreet(name)) {
12798 * resolve('Hello, ' + name + '!');
12799 * } else {
12800 * reject('Greeting ' + name + ' is not allowed.');
12801 * }
12802 * }, 1000);
12803 * });
12804 * }
12805 *
12806 * var promise = asyncGreet('Robin Hood');
12807 * promise.then(function(greeting) {
12808 * alert('Success: ' + greeting);
12809 * }, function(reason) {
12810 * alert('Failed: ' + reason);
12811 * });
12812 * ```
12813 *
12814 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
12815 *
12816 * However, the more traditional CommonJS-style usage is still available, and documented below.
12817 *
12818 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
12819 * interface for interacting with an object that represents the result of an action that is
12820 * performed asynchronously, and may or may not be finished at any given point in time.
12821 *
12822 * From the perspective of dealing with error handling, deferred and promise APIs are to
12823 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
12824 *
12825 * ```js
12826 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
12827 * // are available in the current lexical scope (they could have been injected or passed in).
12828 *
12829 * function asyncGreet(name) {
12830 * var deferred = $q.defer();
12831 *
12832 * setTimeout(function() {
12833 * deferred.notify('About to greet ' + name + '.');
12834 *
12835 * if (okToGreet(name)) {
12836 * deferred.resolve('Hello, ' + name + '!');
12837 * } else {
12838 * deferred.reject('Greeting ' + name + ' is not allowed.');
12839 * }
12840 * }, 1000);
12841 *
12842 * return deferred.promise;
12843 * }
12844 *
12845 * var promise = asyncGreet('Robin Hood');
12846 * promise.then(function(greeting) {
12847 * alert('Success: ' + greeting);
12848 * }, function(reason) {
12849 * alert('Failed: ' + reason);
12850 * }, function(update) {
12851 * alert('Got notification: ' + update);
12852 * });
12853 * ```
12854 *
12855 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
12856 * comes in the way of guarantees that promise and deferred APIs make, see
12857 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
12858 *
12859 * Additionally the promise api allows for composition that is very hard to do with the
12860 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
12861 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
12862 * section on serial or parallel joining of promises.
12863 *
12864 * # The Deferred API
12865 *
12866 * A new instance of deferred is constructed by calling `$q.defer()`.
12867 *
12868 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
12869 * that can be used for signaling the successful or unsuccessful completion, as well as the status
12870 * of the task.
12871 *
12872 * **Methods**
12873 *
12874 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
12875 * constructed via `$q.reject`, the promise will be rejected instead.
12876 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
12877 * resolving it with a rejection constructed via `$q.reject`.
12878 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
12879 * multiple times before the promise is either resolved or rejected.
12880 *
12881 * **Properties**
12882 *
12883 * - promise – `{Promise}` – promise object associated with this deferred.
12884 *
12885 *
12886 * # The Promise API
12887 *
12888 * A new promise instance is created when a deferred instance is created and can be retrieved by
12889 * calling `deferred.promise`.
12890 *
12891 * The purpose of the promise object is to allow for interested parties to get access to the result
12892 * of the deferred task when it completes.
12893 *
12894 * **Methods**
12895 *
12896 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
12897 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
12898 * as soon as the result is available. The callbacks are called with a single argument: the result
12899 * or rejection reason. Additionally, the notify callback may be called zero or more times to
12900 * provide a progress indication, before the promise is resolved or rejected.
12901 *
12902 * This method *returns a new promise* which is resolved or rejected via the return value of the
12903 * `successCallback`, `errorCallback`. It also notifies via the return value of the
12904 * `notifyCallback` method. The promise cannot be resolved or rejected from the notifyCallback
12905 * method.
12906 *
12907 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
12908 *
12909 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
12910 * but to do so without modifying the final value. This is useful to release resources or do some
12911 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
12912 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
12913 * more information.
12914 *
12915 * # Chaining promises
12916 *
12917 * Because calling the `then` method of a promise returns a new derived promise, it is easily
12918 * possible to create a chain of promises:
12919 *
12920 * ```js
12921 * promiseB = promiseA.then(function(result) {
12922 * return result + 1;
12923 * });
12924 *
12925 * // promiseB will be resolved immediately after promiseA is resolved and its value
12926 * // will be the result of promiseA incremented by 1
12927 * ```
12928 *
12929 * It is possible to create chains of any length and since a promise can be resolved with another
12930 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
12931 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
12932 * $http's response interceptors.
12933 *
12934 *
12935 * # Differences between Kris Kowal's Q and $q
12936 *
12937 * There are two main differences:
12938 *
12939 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
12940 * mechanism in angular, which means faster propagation of resolution or rejection into your
12941 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
12942 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
12943 * all the important functionality needed for common async tasks.
12944 *
12945 * # Testing
12946 *
12947 * ```js
12948 * it('should simulate promise', inject(function($q, $rootScope) {
12949 * var deferred = $q.defer();
12950 * var promise = deferred.promise;
12951 * var resolvedValue;
12952 *
12953 * promise.then(function(value) { resolvedValue = value; });
12954 * expect(resolvedValue).toBeUndefined();
12955 *
12956 * // Simulate resolving of promise
12957 * deferred.resolve(123);
12958 * // Note that the 'then' function does not get called synchronously.
12959 * // This is because we want the promise API to always be async, whether or not
12960 * // it got called synchronously or asynchronously.
12961 * expect(resolvedValue).toBeUndefined();
12962 *
12963 * // Propagate promise resolution to 'then' functions using $apply().
12964 * $rootScope.$apply();
12965 * expect(resolvedValue).toEqual(123);
12966 * }));
12967 * ```
12968 *
12969 * @param {function(function, function)} resolver Function which is responsible for resolving or
12970 * rejecting the newly created promise. The first parameter is a function which resolves the
12971 * promise, the second parameter is a function which rejects the promise.
12972 *
12973 * @returns {Promise} The newly created promise.
12974 */
12975function $QProvider() {
12976
12977 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
12978 return qFactory(function(callback) {
12979 $rootScope.$evalAsync(callback);
12980 }, $exceptionHandler);
12981 }];
12982}
12983
12984function $$QProvider() {
12985 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
12986 return qFactory(function(callback) {
12987 $browser.defer(callback);
12988 }, $exceptionHandler);
12989 }];
12990}
12991
12992/**
12993 * Constructs a promise manager.
12994 *
12995 * @param {function(function)} nextTick Function for executing functions in the next turn.
12996 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
12997 * debugging purposes.
12998 * @returns {object} Promise manager.
12999 */
13000function qFactory(nextTick, exceptionHandler) {
13001 var $qMinErr = minErr('$q', TypeError);
13002 function callOnce(self, resolveFn, rejectFn) {
13003 var called = false;
13004 function wrap(fn) {
13005 return function(value) {
13006 if (called) return;
13007 called = true;
13008 fn.call(self, value);
13009 };
13010 }
13011
13012 return [wrap(resolveFn), wrap(rejectFn)];
13013 }
13014
13015 /**
13016 * @ngdoc method
13017 * @name ng.$q#defer
13018 * @kind function
13019 *
13020 * @description
13021 * Creates a `Deferred` object which represents a task which will finish in the future.
13022 *
13023 * @returns {Deferred} Returns a new instance of deferred.
13024 */
13025 var defer = function() {
13026 return new Deferred();
13027 };
13028
13029 function Promise() {
13030 this.$$state = { status: 0 };
13031 }
13032
13033 Promise.prototype = {
13034 then: function(onFulfilled, onRejected, progressBack) {
13035 var result = new Deferred();
13036
13037 this.$$state.pending = this.$$state.pending || [];
13038 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
13039 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
13040
13041 return result.promise;
13042 },
13043
13044 "catch": function(callback) {
13045 return this.then(null, callback);
13046 },
13047
13048 "finally": function(callback, progressBack) {
13049 return this.then(function(value) {
13050 return handleCallback(value, true, callback);
13051 }, function(error) {
13052 return handleCallback(error, false, callback);
13053 }, progressBack);
13054 }
13055 };
13056
13057 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
13058 function simpleBind(context, fn) {
13059 return function(value) {
13060 fn.call(context, value);
13061 };
13062 }
13063
13064 function processQueue(state) {
13065 var fn, promise, pending;
13066
13067 pending = state.pending;
13068 state.processScheduled = false;
13069 state.pending = undefined;
13070 for (var i = 0, ii = pending.length; i < ii; ++i) {
13071 promise = pending[i][0];
13072 fn = pending[i][state.status];
13073 try {
13074 if (isFunction(fn)) {
13075 promise.resolve(fn(state.value));
13076 } else if (state.status === 1) {
13077 promise.resolve(state.value);
13078 } else {
13079 promise.reject(state.value);
13080 }
13081 } catch (e) {
13082 promise.reject(e);
13083 exceptionHandler(e);
13084 }
13085 }
13086 }
13087
13088 function scheduleProcessQueue(state) {
13089 if (state.processScheduled || !state.pending) return;
13090 state.processScheduled = true;
13091 nextTick(function() { processQueue(state); });
13092 }
13093
13094 function Deferred() {
13095 this.promise = new Promise();
13096 //Necessary to support unbound execution :/
13097 this.resolve = simpleBind(this, this.resolve);
13098 this.reject = simpleBind(this, this.reject);
13099 this.notify = simpleBind(this, this.notify);
13100 }
13101
13102 Deferred.prototype = {
13103 resolve: function(val) {
13104 if (this.promise.$$state.status) return;
13105 if (val === this.promise) {
13106 this.$$reject($qMinErr(
13107 'qcycle',
13108 "Expected promise to be resolved with value other than itself '{0}'",
13109 val));
13110 }
13111 else {
13112 this.$$resolve(val);
13113 }
13114
13115 },
13116
13117 $$resolve: function(val) {
13118 var then, fns;
13119
13120 fns = callOnce(this, this.$$resolve, this.$$reject);
13121 try {
13122 if ((isObject(val) || isFunction(val))) then = val && val.then;
13123 if (isFunction(then)) {
13124 this.promise.$$state.status = -1;
13125 then.call(val, fns[0], fns[1], this.notify);
13126 } else {
13127 this.promise.$$state.value = val;
13128 this.promise.$$state.status = 1;
13129 scheduleProcessQueue(this.promise.$$state);
13130 }
13131 } catch (e) {
13132 fns[1](e);
13133 exceptionHandler(e);
13134 }
13135 },
13136
13137 reject: function(reason) {
13138 if (this.promise.$$state.status) return;
13139 this.$$reject(reason);
13140 },
13141
13142 $$reject: function(reason) {
13143 this.promise.$$state.value = reason;
13144 this.promise.$$state.status = 2;
13145 scheduleProcessQueue(this.promise.$$state);
13146 },
13147
13148 notify: function(progress) {
13149 var callbacks = this.promise.$$state.pending;
13150
13151 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
13152 nextTick(function() {
13153 var callback, result;
13154 for (var i = 0, ii = callbacks.length; i < ii; i++) {
13155 result = callbacks[i][0];
13156 callback = callbacks[i][3];
13157 try {
13158 result.notify(isFunction(callback) ? callback(progress) : progress);
13159 } catch (e) {
13160 exceptionHandler(e);
13161 }
13162 }
13163 });
13164 }
13165 }
13166 };
13167
13168 /**
13169 * @ngdoc method
13170 * @name $q#reject
13171 * @kind function
13172 *
13173 * @description
13174 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
13175 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
13176 * a promise chain, you don't need to worry about it.
13177 *
13178 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
13179 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
13180 * a promise error callback and you want to forward the error to the promise derived from the
13181 * current promise, you have to "rethrow" the error by returning a rejection constructed via
13182 * `reject`.
13183 *
13184 * ```js
13185 * promiseB = promiseA.then(function(result) {
13186 * // success: do something and resolve promiseB
13187 * // with the old or a new result
13188 * return result;
13189 * }, function(reason) {
13190 * // error: handle the error if possible and
13191 * // resolve promiseB with newPromiseOrValue,
13192 * // otherwise forward the rejection to promiseB
13193 * if (canHandle(reason)) {
13194 * // handle the error and recover
13195 * return newPromiseOrValue;
13196 * }
13197 * return $q.reject(reason);
13198 * });
13199 * ```
13200 *
13201 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
13202 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
13203 */
13204 var reject = function(reason) {
13205 var result = new Deferred();
13206 result.reject(reason);
13207 return result.promise;
13208 };
13209
13210 var makePromise = function makePromise(value, resolved) {
13211 var result = new Deferred();
13212 if (resolved) {
13213 result.resolve(value);
13214 } else {
13215 result.reject(value);
13216 }
13217 return result.promise;
13218 };
13219
13220 var handleCallback = function handleCallback(value, isResolved, callback) {
13221 var callbackOutput = null;
13222 try {
13223 if (isFunction(callback)) callbackOutput = callback();
13224 } catch (e) {
13225 return makePromise(e, false);
13226 }
13227 if (isPromiseLike(callbackOutput)) {
13228 return callbackOutput.then(function() {
13229 return makePromise(value, isResolved);
13230 }, function(error) {
13231 return makePromise(error, false);
13232 });
13233 } else {
13234 return makePromise(value, isResolved);
13235 }
13236 };
13237
13238 /**
13239 * @ngdoc method
13240 * @name $q#when
13241 * @kind function
13242 *
13243 * @description
13244 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
13245 * This is useful when you are dealing with an object that might or might not be a promise, or if
13246 * the promise comes from a source that can't be trusted.
13247 *
13248 * @param {*} value Value or a promise
13249 * @returns {Promise} Returns a promise of the passed value or promise
13250 */
13251
13252
13253 var when = function(value, callback, errback, progressBack) {
13254 var result = new Deferred();
13255 result.resolve(value);
13256 return result.promise.then(callback, errback, progressBack);
13257 };
13258
13259 /**
13260 * @ngdoc method
13261 * @name $q#all
13262 * @kind function
13263 *
13264 * @description
13265 * Combines multiple promises into a single promise that is resolved when all of the input
13266 * promises are resolved.
13267 *
13268 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
13269 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
13270 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
13271 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
13272 * with the same rejection value.
13273 */
13274
13275 function all(promises) {
13276 var deferred = new Deferred(),
13277 counter = 0,
13278 results = isArray(promises) ? [] : {};
13279
13280 forEach(promises, function(promise, key) {
13281 counter++;
13282 when(promise).then(function(value) {
13283 if (results.hasOwnProperty(key)) return;
13284 results[key] = value;
13285 if (!(--counter)) deferred.resolve(results);
13286 }, function(reason) {
13287 if (results.hasOwnProperty(key)) return;
13288 deferred.reject(reason);
13289 });
13290 });
13291
13292 if (counter === 0) {
13293 deferred.resolve(results);
13294 }
13295
13296 return deferred.promise;
13297 }
13298
13299 var $Q = function Q(resolver) {
13300 if (!isFunction(resolver)) {
13301 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
13302 }
13303
13304 if (!(this instanceof Q)) {
13305 // More useful when $Q is the Promise itself.
13306 return new Q(resolver);
13307 }
13308
13309 var deferred = new Deferred();
13310
13311 function resolveFn(value) {
13312 deferred.resolve(value);
13313 }
13314
13315 function rejectFn(reason) {
13316 deferred.reject(reason);
13317 }
13318
13319 resolver(resolveFn, rejectFn);
13320
13321 return deferred.promise;
13322 };
13323
13324 $Q.defer = defer;
13325 $Q.reject = reject;
13326 $Q.when = when;
13327 $Q.all = all;
13328
13329 return $Q;
13330}
13331
13332function $$RAFProvider() { //rAF
13333 this.$get = ['$window', '$timeout', function($window, $timeout) {
13334 var requestAnimationFrame = $window.requestAnimationFrame ||
13335 $window.webkitRequestAnimationFrame ||
13336 $window.mozRequestAnimationFrame;
13337
13338 var cancelAnimationFrame = $window.cancelAnimationFrame ||
13339 $window.webkitCancelAnimationFrame ||
13340 $window.mozCancelAnimationFrame ||
13341 $window.webkitCancelRequestAnimationFrame;
13342
13343 var rafSupported = !!requestAnimationFrame;
13344 var raf = rafSupported
13345 ? function(fn) {
13346 var id = requestAnimationFrame(fn);
13347 return function() {
13348 cancelAnimationFrame(id);
13349 };
13350 }
13351 : function(fn) {
13352 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
13353 return function() {
13354 $timeout.cancel(timer);
13355 };
13356 };
13357
13358 raf.supported = rafSupported;
13359
13360 return raf;
13361 }];
13362}
13363
13364/**
13365 * DESIGN NOTES
13366 *
13367 * The design decisions behind the scope are heavily favored for speed and memory consumption.
13368 *
13369 * The typical use of scope is to watch the expressions, which most of the time return the same
13370 * value as last time so we optimize the operation.
13371 *
13372 * Closures construction is expensive in terms of speed as well as memory:
13373 * - No closures, instead use prototypical inheritance for API
13374 * - Internal state needs to be stored on scope directly, which means that private state is
13375 * exposed as $$____ properties
13376 *
13377 * Loop operations are optimized by using while(count--) { ... }
13378 * - this means that in order to keep the same order of execution as addition we have to add
13379 * items to the array at the beginning (unshift) instead of at the end (push)
13380 *
13381 * Child scopes are created and removed often
13382 * - Using an array would be slow since inserts in middle are expensive so we use linked list
13383 *
13384 * There are few watches then a lot of observers. This is why you don't want the observer to be
13385 * implemented in the same way as watch. Watch requires return of initialization function which
13386 * are expensive to construct.
13387 */
13388
13389
13390/**
13391 * @ngdoc provider
13392 * @name $rootScopeProvider
13393 * @description
13394 *
13395 * Provider for the $rootScope service.
13396 */
13397
13398/**
13399 * @ngdoc method
13400 * @name $rootScopeProvider#digestTtl
13401 * @description
13402 *
13403 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
13404 * assuming that the model is unstable.
13405 *
13406 * The current default is 10 iterations.
13407 *
13408 * In complex applications it's possible that the dependencies between `$watch`s will result in
13409 * several digest iterations. However if an application needs more than the default 10 digest
13410 * iterations for its model to stabilize then you should investigate what is causing the model to
13411 * continuously change during the digest.
13412 *
13413 * Increasing the TTL could have performance implications, so you should not change it without
13414 * proper justification.
13415 *
13416 * @param {number} limit The number of digest iterations.
13417 */
13418
13419
13420/**
13421 * @ngdoc service
13422 * @name $rootScope
13423 * @description
13424 *
13425 * Every application has a single root {@link ng.$rootScope.Scope scope}.
13426 * All other scopes are descendant scopes of the root scope. Scopes provide separation
13427 * between the model and the view, via a mechanism for watching the model for changes.
13428 * They also provide an event emission/broadcast and subscription facility. See the
13429 * {@link guide/scope developer guide on scopes}.
13430 */
13431function $RootScopeProvider() {
13432 var TTL = 10;
13433 var $rootScopeMinErr = minErr('$rootScope');
13434 var lastDirtyWatch = null;
13435 var applyAsyncId = null;
13436
13437 this.digestTtl = function(value) {
13438 if (arguments.length) {
13439 TTL = value;
13440 }
13441 return TTL;
13442 };
13443
13444 this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
13445 function($injector, $exceptionHandler, $parse, $browser) {
13446
13447 /**
13448 * @ngdoc type
13449 * @name $rootScope.Scope
13450 *
13451 * @description
13452 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
13453 * {@link auto.$injector $injector}. Child scopes are created using the
13454 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
13455 * compiled HTML template is executed.)
13456 *
13457 * Here is a simple scope snippet to show how you can interact with the scope.
13458 * ```html
13459 * <file src="./test/ng/rootScopeSpec.js" tag="docs1" />
13460 * ```
13461 *
13462 * # Inheritance
13463 * A scope can inherit from a parent scope, as in this example:
13464 * ```js
13465 var parent = $rootScope;
13466 var child = parent.$new();
13467
13468 parent.salutation = "Hello";
13469 child.name = "World";
13470 expect(child.salutation).toEqual('Hello');
13471
13472 child.salutation = "Welcome";
13473 expect(child.salutation).toEqual('Welcome');
13474 expect(parent.salutation).toEqual('Hello');
13475 * ```
13476 *
13477 * When interacting with `Scope` in tests, additional helper methods are available on the
13478 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
13479 * details.
13480 *
13481 *
13482 * @param {Object.<string, function()>=} providers Map of service factory which need to be
13483 * provided for the current scope. Defaults to {@link ng}.
13484 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
13485 * append/override services provided by `providers`. This is handy
13486 * when unit-testing and having the need to override a default
13487 * service.
13488 * @returns {Object} Newly created scope.
13489 *
13490 */
13491 function Scope() {
13492 this.$id = nextUid();
13493 this.$$phase = this.$parent = this.$$watchers =
13494 this.$$nextSibling = this.$$prevSibling =
13495 this.$$childHead = this.$$childTail = null;
13496 this.$root = this;
13497 this.$$destroyed = false;
13498 this.$$listeners = {};
13499 this.$$listenerCount = {};
13500 this.$$isolateBindings = null;
13501 }
13502
13503 /**
13504 * @ngdoc property
13505 * @name $rootScope.Scope#$id
13506 *
13507 * @description
13508 * Unique scope ID (monotonically increasing) useful for debugging.
13509 */
13510
13511 /**
13512 * @ngdoc property
13513 * @name $rootScope.Scope#$parent
13514 *
13515 * @description
13516 * Reference to the parent scope.
13517 */
13518
13519 /**
13520 * @ngdoc property
13521 * @name $rootScope.Scope#$root
13522 *
13523 * @description
13524 * Reference to the root scope.
13525 */
13526
13527 Scope.prototype = {
13528 constructor: Scope,
13529 /**
13530 * @ngdoc method
13531 * @name $rootScope.Scope#$new
13532 * @kind function
13533 *
13534 * @description
13535 * Creates a new child {@link ng.$rootScope.Scope scope}.
13536 *
13537 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
13538 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
13539 *
13540 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
13541 * desired for the scope and its child scopes to be permanently detached from the parent and
13542 * thus stop participating in model change detection and listener notification by invoking.
13543 *
13544 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
13545 * parent scope. The scope is isolated, as it can not see parent scope properties.
13546 * When creating widgets, it is useful for the widget to not accidentally read parent
13547 * state.
13548 *
13549 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
13550 * of the newly created scope. Defaults to `this` scope if not provided.
13551 * This is used when creating a transclude scope to correctly place it
13552 * in the scope hierarchy while maintaining the correct prototypical
13553 * inheritance.
13554 *
13555 * @returns {Object} The newly created child scope.
13556 *
13557 */
13558 $new: function(isolate, parent) {
13559 var child;
13560
13561 parent = parent || this;
13562
13563 if (isolate) {
13564 child = new Scope();
13565 child.$root = this.$root;
13566 } else {
13567 // Only create a child scope class if somebody asks for one,
13568 // but cache it to allow the VM to optimize lookups.
13569 if (!this.$$ChildScope) {
13570 this.$$ChildScope = function ChildScope() {
13571 this.$$watchers = this.$$nextSibling =
13572 this.$$childHead = this.$$childTail = null;
13573 this.$$listeners = {};
13574 this.$$listenerCount = {};
13575 this.$id = nextUid();
13576 this.$$ChildScope = null;
13577 };
13578 this.$$ChildScope.prototype = this;
13579 }
13580 child = new this.$$ChildScope();
13581 }
13582 child.$parent = parent;
13583 child.$$prevSibling = parent.$$childTail;
13584 if (parent.$$childHead) {
13585 parent.$$childTail.$$nextSibling = child;
13586 parent.$$childTail = child;
13587 } else {
13588 parent.$$childHead = parent.$$childTail = child;
13589 }
13590
13591 // When the new scope is not isolated or we inherit from `this`, and
13592 // the parent scope is destroyed, the property `$$destroyed` is inherited
13593 // prototypically. In all other cases, this property needs to be set
13594 // when the parent scope is destroyed.
13595 // The listener needs to be added after the parent is set
13596 if (isolate || parent != this) child.$on('$destroy', destroyChild);
13597
13598 return child;
13599
13600 function destroyChild() {
13601 child.$$destroyed = true;
13602 }
13603 },
13604
13605 /**
13606 * @ngdoc method
13607 * @name $rootScope.Scope#$watch
13608 * @kind function
13609 *
13610 * @description
13611 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
13612 *
13613 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
13614 * $digest()} and should return the value that will be watched. (Since
13615 * {@link ng.$rootScope.Scope#$digest $digest()} reruns when it detects changes the
13616 * `watchExpression` can execute multiple times per
13617 * {@link ng.$rootScope.Scope#$digest $digest()} and should be idempotent.)
13618 * - The `listener` is called only when the value from the current `watchExpression` and the
13619 * previous call to `watchExpression` are not equal (with the exception of the initial run,
13620 * see below). Inequality is determined according to reference inequality,
13621 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
13622 * via the `!==` Javascript operator, unless `objectEquality == true`
13623 * (see next point)
13624 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
13625 * according to the {@link angular.equals} function. To save the value of the object for
13626 * later comparison, the {@link angular.copy} function is used. This therefore means that
13627 * watching complex objects will have adverse memory and performance implications.
13628 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
13629 * This is achieved by rerunning the watchers until no changes are detected. The rerun
13630 * iteration limit is 10 to prevent an infinite loop deadlock.
13631 *
13632 *
13633 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
13634 * you can register a `watchExpression` function with no `listener`. (Since `watchExpression`
13635 * can execute multiple times per {@link ng.$rootScope.Scope#$digest $digest} cycle when a
13636 * change is detected, be prepared for multiple calls to your listener.)
13637 *
13638 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
13639 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
13640 * watcher. In rare cases, this is undesirable because the listener is called when the result
13641 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
13642 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
13643 * listener was called due to initialization.
13644 *
13645 *
13646 *
13647 * # Example
13648 * ```js
13649 // let's assume that scope was dependency injected as the $rootScope
13650 var scope = $rootScope;
13651 scope.name = 'misko';
13652 scope.counter = 0;
13653
13654 expect(scope.counter).toEqual(0);
13655 scope.$watch('name', function(newValue, oldValue) {
13656 scope.counter = scope.counter + 1;
13657 });
13658 expect(scope.counter).toEqual(0);
13659
13660 scope.$digest();
13661 // the listener is always called during the first $digest loop after it was registered
13662 expect(scope.counter).toEqual(1);
13663
13664 scope.$digest();
13665 // but now it will not be called unless the value changes
13666 expect(scope.counter).toEqual(1);
13667
13668 scope.name = 'adam';
13669 scope.$digest();
13670 expect(scope.counter).toEqual(2);
13671
13672
13673
13674 // Using a function as a watchExpression
13675 var food;
13676 scope.foodCounter = 0;
13677 expect(scope.foodCounter).toEqual(0);
13678 scope.$watch(
13679 // This function returns the value being watched. It is called for each turn of the $digest loop
13680 function() { return food; },
13681 // This is the change listener, called when the value returned from the above function changes
13682 function(newValue, oldValue) {
13683 if ( newValue !== oldValue ) {
13684 // Only increment the counter if the value changed
13685 scope.foodCounter = scope.foodCounter + 1;
13686 }
13687 }
13688 );
13689 // No digest has been run so the counter will be zero
13690 expect(scope.foodCounter).toEqual(0);
13691
13692 // Run the digest but since food has not changed count will still be zero
13693 scope.$digest();
13694 expect(scope.foodCounter).toEqual(0);
13695
13696 // Update food and run digest. Now the counter will increment
13697 food = 'cheeseburger';
13698 scope.$digest();
13699 expect(scope.foodCounter).toEqual(1);
13700
13701 * ```
13702 *
13703 *
13704 *
13705 * @param {(function()|string)} watchExpression Expression that is evaluated on each
13706 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
13707 * a call to the `listener`.
13708 *
13709 * - `string`: Evaluated as {@link guide/expression expression}
13710 * - `function(scope)`: called with current `scope` as a parameter.
13711 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
13712 * of `watchExpression` changes.
13713 *
13714 * - `newVal` contains the current value of the `watchExpression`
13715 * - `oldVal` contains the previous value of the `watchExpression`
13716 * - `scope` refers to the current scope
13717 * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
13718 * comparing for reference equality.
13719 * @returns {function()} Returns a deregistration function for this listener.
13720 */
13721 $watch: function(watchExp, listener, objectEquality) {
13722 var get = $parse(watchExp);
13723
13724 if (get.$$watchDelegate) {
13725 return get.$$watchDelegate(this, listener, objectEquality, get);
13726 }
13727 var scope = this,
13728 array = scope.$$watchers,
13729 watcher = {
13730 fn: listener,
13731 last: initWatchVal,
13732 get: get,
13733 exp: watchExp,
13734 eq: !!objectEquality
13735 };
13736
13737 lastDirtyWatch = null;
13738
13739 if (!isFunction(listener)) {
13740 watcher.fn = noop;
13741 }
13742
13743 if (!array) {
13744 array = scope.$$watchers = [];
13745 }
13746 // we use unshift since we use a while loop in $digest for speed.
13747 // the while loop reads in reverse order.
13748 array.unshift(watcher);
13749
13750 return function deregisterWatch() {
13751 arrayRemove(array, watcher);
13752 lastDirtyWatch = null;
13753 };
13754 },
13755
13756 /**
13757 * @ngdoc method
13758 * @name $rootScope.Scope#$watchGroup
13759 * @kind function
13760 *
13761 * @description
13762 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
13763 * If any one expression in the collection changes the `listener` is executed.
13764 *
13765 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
13766 * call to $digest() to see if any items changes.
13767 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
13768 *
13769 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
13770 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
13771 *
13772 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
13773 * expression in `watchExpressions` changes
13774 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
13775 * those of `watchExpression`
13776 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
13777 * those of `watchExpression`
13778 * The `scope` refers to the current scope.
13779 * @returns {function()} Returns a de-registration function for all listeners.
13780 */
13781 $watchGroup: function(watchExpressions, listener) {
13782 var oldValues = new Array(watchExpressions.length);
13783 var newValues = new Array(watchExpressions.length);
13784 var deregisterFns = [];
13785 var self = this;
13786 var changeReactionScheduled = false;
13787 var firstRun = true;
13788
13789 if (!watchExpressions.length) {
13790 // No expressions means we call the listener ASAP
13791 var shouldCall = true;
13792 self.$evalAsync(function() {
13793 if (shouldCall) listener(newValues, newValues, self);
13794 });
13795 return function deregisterWatchGroup() {
13796 shouldCall = false;
13797 };
13798 }
13799
13800 if (watchExpressions.length === 1) {
13801 // Special case size of one
13802 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
13803 newValues[0] = value;
13804 oldValues[0] = oldValue;
13805 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
13806 });
13807 }
13808
13809 forEach(watchExpressions, function(expr, i) {
13810 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
13811 newValues[i] = value;
13812 oldValues[i] = oldValue;
13813 if (!changeReactionScheduled) {
13814 changeReactionScheduled = true;
13815 self.$evalAsync(watchGroupAction);
13816 }
13817 });
13818 deregisterFns.push(unwatchFn);
13819 });
13820
13821 function watchGroupAction() {
13822 changeReactionScheduled = false;
13823
13824 if (firstRun) {
13825 firstRun = false;
13826 listener(newValues, newValues, self);
13827 } else {
13828 listener(newValues, oldValues, self);
13829 }
13830 }
13831
13832 return function deregisterWatchGroup() {
13833 while (deregisterFns.length) {
13834 deregisterFns.shift()();
13835 }
13836 };
13837 },
13838
13839
13840 /**
13841 * @ngdoc method
13842 * @name $rootScope.Scope#$watchCollection
13843 * @kind function
13844 *
13845 * @description
13846 * Shallow watches the properties of an object and fires whenever any of the properties change
13847 * (for arrays, this implies watching the array items; for object maps, this implies watching
13848 * the properties). If a change is detected, the `listener` callback is fired.
13849 *
13850 * - The `obj` collection is observed via standard $watch operation and is examined on every
13851 * call to $digest() to see if any items have been added, removed, or moved.
13852 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
13853 * adding, removing, and moving items belonging to an object or array.
13854 *
13855 *
13856 * # Example
13857 * ```js
13858 $scope.names = ['igor', 'matias', 'misko', 'james'];
13859 $scope.dataCount = 4;
13860
13861 $scope.$watchCollection('names', function(newNames, oldNames) {
13862 $scope.dataCount = newNames.length;
13863 });
13864
13865 expect($scope.dataCount).toEqual(4);
13866 $scope.$digest();
13867
13868 //still at 4 ... no changes
13869 expect($scope.dataCount).toEqual(4);
13870
13871 $scope.names.pop();
13872 $scope.$digest();
13873
13874 //now there's been a change
13875 expect($scope.dataCount).toEqual(3);
13876 * ```
13877 *
13878 *
13879 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
13880 * expression value should evaluate to an object or an array which is observed on each
13881 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
13882 * collection will trigger a call to the `listener`.
13883 *
13884 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
13885 * when a change is detected.
13886 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
13887 * - The `oldCollection` object is a copy of the former collection data.
13888 * Due to performance considerations, the`oldCollection` value is computed only if the
13889 * `listener` function declares two or more arguments.
13890 * - The `scope` argument refers to the current scope.
13891 *
13892 * @returns {function()} Returns a de-registration function for this listener. When the
13893 * de-registration function is executed, the internal watch operation is terminated.
13894 */
13895 $watchCollection: function(obj, listener) {
13896 $watchCollectionInterceptor.$stateful = true;
13897
13898 var self = this;
13899 // the current value, updated on each dirty-check run
13900 var newValue;
13901 // a shallow copy of the newValue from the last dirty-check run,
13902 // updated to match newValue during dirty-check run
13903 var oldValue;
13904 // a shallow copy of the newValue from when the last change happened
13905 var veryOldValue;
13906 // only track veryOldValue if the listener is asking for it
13907 var trackVeryOldValue = (listener.length > 1);
13908 var changeDetected = 0;
13909 var changeDetector = $parse(obj, $watchCollectionInterceptor);
13910 var internalArray = [];
13911 var internalObject = {};
13912 var initRun = true;
13913 var oldLength = 0;
13914
13915 function $watchCollectionInterceptor(_value) {
13916 newValue = _value;
13917 var newLength, key, bothNaN, newItem, oldItem;
13918
13919 // If the new value is undefined, then return undefined as the watch may be a one-time watch
13920 if (isUndefined(newValue)) return;
13921
13922 if (!isObject(newValue)) { // if primitive
13923 if (oldValue !== newValue) {
13924 oldValue = newValue;
13925 changeDetected++;
13926 }
13927 } else if (isArrayLike(newValue)) {
13928 if (oldValue !== internalArray) {
13929 // we are transitioning from something which was not an array into array.
13930 oldValue = internalArray;
13931 oldLength = oldValue.length = 0;
13932 changeDetected++;
13933 }
13934
13935 newLength = newValue.length;
13936
13937 if (oldLength !== newLength) {
13938 // if lengths do not match we need to trigger change notification
13939 changeDetected++;
13940 oldValue.length = oldLength = newLength;
13941 }
13942 // copy the items to oldValue and look for changes.
13943 for (var i = 0; i < newLength; i++) {
13944 oldItem = oldValue[i];
13945 newItem = newValue[i];
13946
13947 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
13948 if (!bothNaN && (oldItem !== newItem)) {
13949 changeDetected++;
13950 oldValue[i] = newItem;
13951 }
13952 }
13953 } else {
13954 if (oldValue !== internalObject) {
13955 // we are transitioning from something which was not an object into object.
13956 oldValue = internalObject = {};
13957 oldLength = 0;
13958 changeDetected++;
13959 }
13960 // copy the items to oldValue and look for changes.
13961 newLength = 0;
13962 for (key in newValue) {
13963 if (newValue.hasOwnProperty(key)) {
13964 newLength++;
13965 newItem = newValue[key];
13966 oldItem = oldValue[key];
13967
13968 if (key in oldValue) {
13969 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
13970 if (!bothNaN && (oldItem !== newItem)) {
13971 changeDetected++;
13972 oldValue[key] = newItem;
13973 }
13974 } else {
13975 oldLength++;
13976 oldValue[key] = newItem;
13977 changeDetected++;
13978 }
13979 }
13980 }
13981 if (oldLength > newLength) {
13982 // we used to have more keys, need to find them and destroy them.
13983 changeDetected++;
13984 for (key in oldValue) {
13985 if (!newValue.hasOwnProperty(key)) {
13986 oldLength--;
13987 delete oldValue[key];
13988 }
13989 }
13990 }
13991 }
13992 return changeDetected;
13993 }
13994
13995 function $watchCollectionAction() {
13996 if (initRun) {
13997 initRun = false;
13998 listener(newValue, newValue, self);
13999 } else {
14000 listener(newValue, veryOldValue, self);
14001 }
14002
14003 // make a copy for the next time a collection is changed
14004 if (trackVeryOldValue) {
14005 if (!isObject(newValue)) {
14006 //primitive
14007 veryOldValue = newValue;
14008 } else if (isArrayLike(newValue)) {
14009 veryOldValue = new Array(newValue.length);
14010 for (var i = 0; i < newValue.length; i++) {
14011 veryOldValue[i] = newValue[i];
14012 }
14013 } else { // if object
14014 veryOldValue = {};
14015 for (var key in newValue) {
14016 if (hasOwnProperty.call(newValue, key)) {
14017 veryOldValue[key] = newValue[key];
14018 }
14019 }
14020 }
14021 }
14022 }
14023
14024 return this.$watch(changeDetector, $watchCollectionAction);
14025 },
14026
14027 /**
14028 * @ngdoc method
14029 * @name $rootScope.Scope#$digest
14030 * @kind function
14031 *
14032 * @description
14033 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
14034 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
14035 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
14036 * until no more listeners are firing. This means that it is possible to get into an infinite
14037 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
14038 * iterations exceeds 10.
14039 *
14040 * Usually, you don't call `$digest()` directly in
14041 * {@link ng.directive:ngController controllers} or in
14042 * {@link ng.$compileProvider#directive directives}.
14043 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
14044 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
14045 *
14046 * If you want to be notified whenever `$digest()` is called,
14047 * you can register a `watchExpression` function with
14048 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
14049 *
14050 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
14051 *
14052 * # Example
14053 * ```js
14054 var scope = ...;
14055 scope.name = 'misko';
14056 scope.counter = 0;
14057
14058 expect(scope.counter).toEqual(0);
14059 scope.$watch('name', function(newValue, oldValue) {
14060 scope.counter = scope.counter + 1;
14061 });
14062 expect(scope.counter).toEqual(0);
14063
14064 scope.$digest();
14065 // the listener is always called during the first $digest loop after it was registered
14066 expect(scope.counter).toEqual(1);
14067
14068 scope.$digest();
14069 // but now it will not be called unless the value changes
14070 expect(scope.counter).toEqual(1);
14071
14072 scope.name = 'adam';
14073 scope.$digest();
14074 expect(scope.counter).toEqual(2);
14075 * ```
14076 *
14077 */
14078 $digest: function() {
14079 var watch, value, last,
14080 watchers,
14081 length,
14082 dirty, ttl = TTL,
14083 next, current, target = this,
14084 watchLog = [],
14085 logIdx, logMsg, asyncTask;
14086
14087 beginPhase('$digest');
14088 // Check for changes to browser url that happened in sync before the call to $digest
14089 $browser.$$checkUrlChange();
14090
14091 if (this === $rootScope && applyAsyncId !== null) {
14092 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
14093 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
14094 $browser.defer.cancel(applyAsyncId);
14095 flushApplyAsync();
14096 }
14097
14098 lastDirtyWatch = null;
14099
14100 do { // "while dirty" loop
14101 dirty = false;
14102 current = target;
14103
14104 while (asyncQueue.length) {
14105 try {
14106 asyncTask = asyncQueue.shift();
14107 asyncTask.scope.$eval(asyncTask.expression);
14108 } catch (e) {
14109 $exceptionHandler(e);
14110 }
14111 lastDirtyWatch = null;
14112 }
14113
14114 traverseScopesLoop:
14115 do { // "traverse the scopes" loop
14116 if ((watchers = current.$$watchers)) {
14117 // process our watches
14118 length = watchers.length;
14119 while (length--) {
14120 try {
14121 watch = watchers[length];
14122 // Most common watches are on primitives, in which case we can short
14123 // circuit it with === operator, only when === fails do we use .equals
14124 if (watch) {
14125 if ((value = watch.get(current)) !== (last = watch.last) &&
14126 !(watch.eq
14127 ? equals(value, last)
14128 : (typeof value === 'number' && typeof last === 'number'
14129 && isNaN(value) && isNaN(last)))) {
14130 dirty = true;
14131 lastDirtyWatch = watch;
14132 watch.last = watch.eq ? copy(value, null) : value;
14133 watch.fn(value, ((last === initWatchVal) ? value : last), current);
14134 if (ttl < 5) {
14135 logIdx = 4 - ttl;
14136 if (!watchLog[logIdx]) watchLog[logIdx] = [];
14137 watchLog[logIdx].push({
14138 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
14139 newVal: value,
14140 oldVal: last
14141 });
14142 }
14143 } else if (watch === lastDirtyWatch) {
14144 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
14145 // have already been tested.
14146 dirty = false;
14147 break traverseScopesLoop;
14148 }
14149 }
14150 } catch (e) {
14151 $exceptionHandler(e);
14152 }
14153 }
14154 }
14155
14156 // Insanity Warning: scope depth-first traversal
14157 // yes, this code is a bit crazy, but it works and we have tests to prove it!
14158 // this piece should be kept in sync with the traversal in $broadcast
14159 if (!(next = (current.$$childHead ||
14160 (current !== target && current.$$nextSibling)))) {
14161 while (current !== target && !(next = current.$$nextSibling)) {
14162 current = current.$parent;
14163 }
14164 }
14165 } while ((current = next));
14166
14167 // `break traverseScopesLoop;` takes us to here
14168
14169 if ((dirty || asyncQueue.length) && !(ttl--)) {
14170 clearPhase();
14171 throw $rootScopeMinErr('infdig',
14172 '{0} $digest() iterations reached. Aborting!\n' +
14173 'Watchers fired in the last 5 iterations: {1}',
14174 TTL, watchLog);
14175 }
14176
14177 } while (dirty || asyncQueue.length);
14178
14179 clearPhase();
14180
14181 while (postDigestQueue.length) {
14182 try {
14183 postDigestQueue.shift()();
14184 } catch (e) {
14185 $exceptionHandler(e);
14186 }
14187 }
14188 },
14189
14190
14191 /**
14192 * @ngdoc event
14193 * @name $rootScope.Scope#$destroy
14194 * @eventType broadcast on scope being destroyed
14195 *
14196 * @description
14197 * Broadcasted when a scope and its children are being destroyed.
14198 *
14199 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14200 * clean up DOM bindings before an element is removed from the DOM.
14201 */
14202
14203 /**
14204 * @ngdoc method
14205 * @name $rootScope.Scope#$destroy
14206 * @kind function
14207 *
14208 * @description
14209 * Removes the current scope (and all of its children) from the parent scope. Removal implies
14210 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
14211 * propagate to the current scope and its children. Removal also implies that the current
14212 * scope is eligible for garbage collection.
14213 *
14214 * The `$destroy()` is usually used by directives such as
14215 * {@link ng.directive:ngRepeat ngRepeat} for managing the
14216 * unrolling of the loop.
14217 *
14218 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
14219 * Application code can register a `$destroy` event handler that will give it a chance to
14220 * perform any necessary cleanup.
14221 *
14222 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
14223 * clean up DOM bindings before an element is removed from the DOM.
14224 */
14225 $destroy: function() {
14226 // we can't destroy the root scope or a scope that has been already destroyed
14227 if (this.$$destroyed) return;
14228 var parent = this.$parent;
14229
14230 this.$broadcast('$destroy');
14231 this.$$destroyed = true;
14232 if (this === $rootScope) return;
14233
14234 for (var eventName in this.$$listenerCount) {
14235 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
14236 }
14237
14238 // sever all the references to parent scopes (after this cleanup, the current scope should
14239 // not be retained by any of our references and should be eligible for garbage collection)
14240 if (parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
14241 if (parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
14242 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
14243 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
14244
14245 // Disable listeners, watchers and apply/digest methods
14246 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
14247 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
14248 this.$$listeners = {};
14249
14250 // All of the code below is bogus code that works around V8's memory leak via optimized code
14251 // and inline caches.
14252 //
14253 // see:
14254 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
14255 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
14256 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
14257
14258 this.$parent = this.$$nextSibling = this.$$prevSibling = this.$$childHead =
14259 this.$$childTail = this.$root = this.$$watchers = null;
14260 },
14261
14262 /**
14263 * @ngdoc method
14264 * @name $rootScope.Scope#$eval
14265 * @kind function
14266 *
14267 * @description
14268 * Executes the `expression` on the current scope and returns the result. Any exceptions in
14269 * the expression are propagated (uncaught). This is useful when evaluating Angular
14270 * expressions.
14271 *
14272 * # Example
14273 * ```js
14274 var scope = ng.$rootScope.Scope();
14275 scope.a = 1;
14276 scope.b = 2;
14277
14278 expect(scope.$eval('a+b')).toEqual(3);
14279 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
14280 * ```
14281 *
14282 * @param {(string|function())=} expression An angular expression to be executed.
14283 *
14284 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14285 * - `function(scope)`: execute the function with the current `scope` parameter.
14286 *
14287 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
14288 * @returns {*} The result of evaluating the expression.
14289 */
14290 $eval: function(expr, locals) {
14291 return $parse(expr)(this, locals);
14292 },
14293
14294 /**
14295 * @ngdoc method
14296 * @name $rootScope.Scope#$evalAsync
14297 * @kind function
14298 *
14299 * @description
14300 * Executes the expression on the current scope at a later point in time.
14301 *
14302 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
14303 * that:
14304 *
14305 * - it will execute after the function that scheduled the evaluation (preferably before DOM
14306 * rendering).
14307 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
14308 * `expression` execution.
14309 *
14310 * Any exceptions from the execution of the expression are forwarded to the
14311 * {@link ng.$exceptionHandler $exceptionHandler} service.
14312 *
14313 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
14314 * will be scheduled. However, it is encouraged to always call code that changes the model
14315 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
14316 *
14317 * @param {(string|function())=} expression An angular expression to be executed.
14318 *
14319 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14320 * - `function(scope)`: execute the function with the current `scope` parameter.
14321 *
14322 */
14323 $evalAsync: function(expr) {
14324 // if we are outside of an $digest loop and this is the first time we are scheduling async
14325 // task also schedule async auto-flush
14326 if (!$rootScope.$$phase && !asyncQueue.length) {
14327 $browser.defer(function() {
14328 if (asyncQueue.length) {
14329 $rootScope.$digest();
14330 }
14331 });
14332 }
14333
14334 asyncQueue.push({scope: this, expression: expr});
14335 },
14336
14337 $$postDigest: function(fn) {
14338 postDigestQueue.push(fn);
14339 },
14340
14341 /**
14342 * @ngdoc method
14343 * @name $rootScope.Scope#$apply
14344 * @kind function
14345 *
14346 * @description
14347 * `$apply()` is used to execute an expression in angular from outside of the angular
14348 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
14349 * Because we are calling into the angular framework we need to perform proper scope life
14350 * cycle of {@link ng.$exceptionHandler exception handling},
14351 * {@link ng.$rootScope.Scope#$digest executing watches}.
14352 *
14353 * ## Life cycle
14354 *
14355 * # Pseudo-Code of `$apply()`
14356 * ```js
14357 function $apply(expr) {
14358 try {
14359 return $eval(expr);
14360 } catch (e) {
14361 $exceptionHandler(e);
14362 } finally {
14363 $root.$digest();
14364 }
14365 }
14366 * ```
14367 *
14368 *
14369 * Scope's `$apply()` method transitions through the following stages:
14370 *
14371 * 1. The {@link guide/expression expression} is executed using the
14372 * {@link ng.$rootScope.Scope#$eval $eval()} method.
14373 * 2. Any exceptions from the execution of the expression are forwarded to the
14374 * {@link ng.$exceptionHandler $exceptionHandler} service.
14375 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
14376 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
14377 *
14378 *
14379 * @param {(string|function())=} exp An angular expression to be executed.
14380 *
14381 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14382 * - `function(scope)`: execute the function with current `scope` parameter.
14383 *
14384 * @returns {*} The result of evaluating the expression.
14385 */
14386 $apply: function(expr) {
14387 try {
14388 beginPhase('$apply');
14389 return this.$eval(expr);
14390 } catch (e) {
14391 $exceptionHandler(e);
14392 } finally {
14393 clearPhase();
14394 try {
14395 $rootScope.$digest();
14396 } catch (e) {
14397 $exceptionHandler(e);
14398 throw e;
14399 }
14400 }
14401 },
14402
14403 /**
14404 * @ngdoc method
14405 * @name $rootScope.Scope#$applyAsync
14406 * @kind function
14407 *
14408 * @description
14409 * Schedule the invokation of $apply to occur at a later time. The actual time difference
14410 * varies across browsers, but is typically around ~10 milliseconds.
14411 *
14412 * This can be used to queue up multiple expressions which need to be evaluated in the same
14413 * digest.
14414 *
14415 * @param {(string|function())=} exp An angular expression to be executed.
14416 *
14417 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
14418 * - `function(scope)`: execute the function with current `scope` parameter.
14419 */
14420 $applyAsync: function(expr) {
14421 var scope = this;
14422 expr && applyAsyncQueue.push($applyAsyncExpression);
14423 scheduleApplyAsync();
14424
14425 function $applyAsyncExpression() {
14426 scope.$eval(expr);
14427 }
14428 },
14429
14430 /**
14431 * @ngdoc method
14432 * @name $rootScope.Scope#$on
14433 * @kind function
14434 *
14435 * @description
14436 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
14437 * discussion of event life cycle.
14438 *
14439 * The event listener function format is: `function(event, args...)`. The `event` object
14440 * passed into the listener has the following attributes:
14441 *
14442 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
14443 * `$broadcast`-ed.
14444 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
14445 * event propagates through the scope hierarchy, this property is set to null.
14446 * - `name` - `{string}`: name of the event.
14447 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
14448 * further event propagation (available only for events that were `$emit`-ed).
14449 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
14450 * to true.
14451 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
14452 *
14453 * @param {string} name Event name to listen on.
14454 * @param {function(event, ...args)} listener Function to call when the event is emitted.
14455 * @returns {function()} Returns a deregistration function for this listener.
14456 */
14457 $on: function(name, listener) {
14458 var namedListeners = this.$$listeners[name];
14459 if (!namedListeners) {
14460 this.$$listeners[name] = namedListeners = [];
14461 }
14462 namedListeners.push(listener);
14463
14464 var current = this;
14465 do {
14466 if (!current.$$listenerCount[name]) {
14467 current.$$listenerCount[name] = 0;
14468 }
14469 current.$$listenerCount[name]++;
14470 } while ((current = current.$parent));
14471
14472 var self = this;
14473 return function() {
14474 var indexOfListener = namedListeners.indexOf(listener);
14475 if (indexOfListener !== -1) {
14476 namedListeners[indexOfListener] = null;
14477 decrementListenerCount(self, 1, name);
14478 }
14479 };
14480 },
14481
14482
14483 /**
14484 * @ngdoc method
14485 * @name $rootScope.Scope#$emit
14486 * @kind function
14487 *
14488 * @description
14489 * Dispatches an event `name` upwards through the scope hierarchy notifying the
14490 * registered {@link ng.$rootScope.Scope#$on} listeners.
14491 *
14492 * The event life cycle starts at the scope on which `$emit` was called. All
14493 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14494 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
14495 * registered listeners along the way. The event will stop propagating if one of the listeners
14496 * cancels it.
14497 *
14498 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14499 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14500 *
14501 * @param {string} name Event name to emit.
14502 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14503 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
14504 */
14505 $emit: function(name, args) {
14506 var empty = [],
14507 namedListeners,
14508 scope = this,
14509 stopPropagation = false,
14510 event = {
14511 name: name,
14512 targetScope: scope,
14513 stopPropagation: function() {stopPropagation = true;},
14514 preventDefault: function() {
14515 event.defaultPrevented = true;
14516 },
14517 defaultPrevented: false
14518 },
14519 listenerArgs = concat([event], arguments, 1),
14520 i, length;
14521
14522 do {
14523 namedListeners = scope.$$listeners[name] || empty;
14524 event.currentScope = scope;
14525 for (i = 0, length = namedListeners.length; i < length; i++) {
14526
14527 // if listeners were deregistered, defragment the array
14528 if (!namedListeners[i]) {
14529 namedListeners.splice(i, 1);
14530 i--;
14531 length--;
14532 continue;
14533 }
14534 try {
14535 //allow all listeners attached to the current scope to run
14536 namedListeners[i].apply(null, listenerArgs);
14537 } catch (e) {
14538 $exceptionHandler(e);
14539 }
14540 }
14541 //if any listener on the current scope stops propagation, prevent bubbling
14542 if (stopPropagation) {
14543 event.currentScope = null;
14544 return event;
14545 }
14546 //traverse upwards
14547 scope = scope.$parent;
14548 } while (scope);
14549
14550 event.currentScope = null;
14551
14552 return event;
14553 },
14554
14555
14556 /**
14557 * @ngdoc method
14558 * @name $rootScope.Scope#$broadcast
14559 * @kind function
14560 *
14561 * @description
14562 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
14563 * registered {@link ng.$rootScope.Scope#$on} listeners.
14564 *
14565 * The event life cycle starts at the scope on which `$broadcast` was called. All
14566 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
14567 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
14568 * scope and calls all registered listeners along the way. The event cannot be canceled.
14569 *
14570 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
14571 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
14572 *
14573 * @param {string} name Event name to broadcast.
14574 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
14575 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
14576 */
14577 $broadcast: function(name, args) {
14578 var target = this,
14579 current = target,
14580 next = target,
14581 event = {
14582 name: name,
14583 targetScope: target,
14584 preventDefault: function() {
14585 event.defaultPrevented = true;
14586 },
14587 defaultPrevented: false
14588 };
14589
14590 if (!target.$$listenerCount[name]) return event;
14591
14592 var listenerArgs = concat([event], arguments, 1),
14593 listeners, i, length;
14594
14595 //down while you can, then up and next sibling or up and next sibling until back at root
14596 while ((current = next)) {
14597 event.currentScope = current;
14598 listeners = current.$$listeners[name] || [];
14599 for (i = 0, length = listeners.length; i < length; i++) {
14600 // if listeners were deregistered, defragment the array
14601 if (!listeners[i]) {
14602 listeners.splice(i, 1);
14603 i--;
14604 length--;
14605 continue;
14606 }
14607
14608 try {
14609 listeners[i].apply(null, listenerArgs);
14610 } catch (e) {
14611 $exceptionHandler(e);
14612 }
14613 }
14614
14615 // Insanity Warning: scope depth-first traversal
14616 // yes, this code is a bit crazy, but it works and we have tests to prove it!
14617 // this piece should be kept in sync with the traversal in $digest
14618 // (though it differs due to having the extra check for $$listenerCount)
14619 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
14620 (current !== target && current.$$nextSibling)))) {
14621 while (current !== target && !(next = current.$$nextSibling)) {
14622 current = current.$parent;
14623 }
14624 }
14625 }
14626
14627 event.currentScope = null;
14628 return event;
14629 }
14630 };
14631
14632 var $rootScope = new Scope();
14633
14634 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
14635 var asyncQueue = $rootScope.$$asyncQueue = [];
14636 var postDigestQueue = $rootScope.$$postDigestQueue = [];
14637 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
14638
14639 return $rootScope;
14640
14641
14642 function beginPhase(phase) {
14643 if ($rootScope.$$phase) {
14644 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
14645 }
14646
14647 $rootScope.$$phase = phase;
14648 }
14649
14650 function clearPhase() {
14651 $rootScope.$$phase = null;
14652 }
14653
14654
14655 function decrementListenerCount(current, count, name) {
14656 do {
14657 current.$$listenerCount[name] -= count;
14658
14659 if (current.$$listenerCount[name] === 0) {
14660 delete current.$$listenerCount[name];
14661 }
14662 } while ((current = current.$parent));
14663 }
14664
14665 /**
14666 * function used as an initial value for watchers.
14667 * because it's unique we can easily tell it apart from other values
14668 */
14669 function initWatchVal() {}
14670
14671 function flushApplyAsync() {
14672 while (applyAsyncQueue.length) {
14673 try {
14674 applyAsyncQueue.shift()();
14675 } catch (e) {
14676 $exceptionHandler(e);
14677 }
14678 }
14679 applyAsyncId = null;
14680 }
14681
14682 function scheduleApplyAsync() {
14683 if (applyAsyncId === null) {
14684 applyAsyncId = $browser.defer(function() {
14685 $rootScope.$apply(flushApplyAsync);
14686 });
14687 }
14688 }
14689 }];
14690}
14691
14692/**
14693 * @description
14694 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
14695 */
14696function $$SanitizeUriProvider() {
14697 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
14698 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
14699
14700 /**
14701 * @description
14702 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14703 * urls during a[href] sanitization.
14704 *
14705 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14706 *
14707 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
14708 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
14709 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14710 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14711 *
14712 * @param {RegExp=} regexp New regexp to whitelist urls with.
14713 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14714 * chaining otherwise.
14715 */
14716 this.aHrefSanitizationWhitelist = function(regexp) {
14717 if (isDefined(regexp)) {
14718 aHrefSanitizationWhitelist = regexp;
14719 return this;
14720 }
14721 return aHrefSanitizationWhitelist;
14722 };
14723
14724
14725 /**
14726 * @description
14727 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
14728 * urls during img[src] sanitization.
14729 *
14730 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
14731 *
14732 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
14733 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
14734 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
14735 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
14736 *
14737 * @param {RegExp=} regexp New regexp to whitelist urls with.
14738 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
14739 * chaining otherwise.
14740 */
14741 this.imgSrcSanitizationWhitelist = function(regexp) {
14742 if (isDefined(regexp)) {
14743 imgSrcSanitizationWhitelist = regexp;
14744 return this;
14745 }
14746 return imgSrcSanitizationWhitelist;
14747 };
14748
14749 this.$get = function() {
14750 return function sanitizeUri(uri, isImage) {
14751 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
14752 var normalizedVal;
14753 normalizedVal = urlResolve(uri).href;
14754 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
14755 return 'unsafe:' + normalizedVal;
14756 }
14757 return uri;
14758 };
14759 };
14760}
14761
14762var $sceMinErr = minErr('$sce');
14763
14764var SCE_CONTEXTS = {
14765 HTML: 'html',
14766 CSS: 'css',
14767 URL: 'url',
14768 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
14769 // url. (e.g. ng-include, script src, templateUrl)
14770 RESOURCE_URL: 'resourceUrl',
14771 JS: 'js'
14772};
14773
14774// Helper functions follow.
14775
14776function adjustMatcher(matcher) {
14777 if (matcher === 'self') {
14778 return matcher;
14779 } else if (isString(matcher)) {
14780 // Strings match exactly except for 2 wildcards - '*' and '**'.
14781 // '*' matches any character except those from the set ':/.?&'.
14782 // '**' matches any character (like .* in a RegExp).
14783 // More than 2 *'s raises an error as it's ill defined.
14784 if (matcher.indexOf('***') > -1) {
14785 throw $sceMinErr('iwcard',
14786 'Illegal sequence *** in string matcher. String: {0}', matcher);
14787 }
14788 matcher = escapeForRegexp(matcher).
14789 replace('\\*\\*', '.*').
14790 replace('\\*', '[^:/.?&;]*');
14791 return new RegExp('^' + matcher + '$');
14792 } else if (isRegExp(matcher)) {
14793 // The only other type of matcher allowed is a Regexp.
14794 // Match entire URL / disallow partial matches.
14795 // Flags are reset (i.e. no global, ignoreCase or multiline)
14796 return new RegExp('^' + matcher.source + '$');
14797 } else {
14798 throw $sceMinErr('imatcher',
14799 'Matchers may only be "self", string patterns or RegExp objects');
14800 }
14801}
14802
14803
14804function adjustMatchers(matchers) {
14805 var adjustedMatchers = [];
14806 if (isDefined(matchers)) {
14807 forEach(matchers, function(matcher) {
14808 adjustedMatchers.push(adjustMatcher(matcher));
14809 });
14810 }
14811 return adjustedMatchers;
14812}
14813
14814
14815/**
14816 * @ngdoc service
14817 * @name $sceDelegate
14818 * @kind function
14819 *
14820 * @description
14821 *
14822 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
14823 * Contextual Escaping (SCE)} services to AngularJS.
14824 *
14825 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
14826 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
14827 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
14828 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
14829 * work because `$sce` delegates to `$sceDelegate` for these operations.
14830 *
14831 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
14832 *
14833 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
14834 * can override it completely to change the behavior of `$sce`, the common case would
14835 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
14836 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
14837 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
14838 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
14839 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
14840 */
14841
14842/**
14843 * @ngdoc provider
14844 * @name $sceDelegateProvider
14845 * @description
14846 *
14847 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
14848 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
14849 * that the URLs used for sourcing Angular templates are safe. Refer {@link
14850 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
14851 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
14852 *
14853 * For the general details about this service in Angular, read the main page for {@link ng.$sce
14854 * Strict Contextual Escaping (SCE)}.
14855 *
14856 * **Example**: Consider the following case. <a name="example"></a>
14857 *
14858 * - your app is hosted at url `http://myapp.example.com/`
14859 * - but some of your templates are hosted on other domains you control such as
14860 * `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
14861 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
14862 *
14863 * Here is what a secure configuration for this scenario might look like:
14864 *
14865 * ```
14866 * angular.module('myApp', []).config(function($sceDelegateProvider) {
14867 * $sceDelegateProvider.resourceUrlWhitelist([
14868 * // Allow same origin resource loads.
14869 * 'self',
14870 * // Allow loading from our assets domain. Notice the difference between * and **.
14871 * 'http://srv*.assets.example.com/**'
14872 * ]);
14873 *
14874 * // The blacklist overrides the whitelist so the open redirect here is blocked.
14875 * $sceDelegateProvider.resourceUrlBlacklist([
14876 * 'http://myapp.example.com/clickThru**'
14877 * ]);
14878 * });
14879 * ```
14880 */
14881
14882function $SceDelegateProvider() {
14883 this.SCE_CONTEXTS = SCE_CONTEXTS;
14884
14885 // Resource URLs can also be trusted by policy.
14886 var resourceUrlWhitelist = ['self'],
14887 resourceUrlBlacklist = [];
14888
14889 /**
14890 * @ngdoc method
14891 * @name $sceDelegateProvider#resourceUrlWhitelist
14892 * @kind function
14893 *
14894 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
14895 * provided. This must be an array or null. A snapshot of this array is used so further
14896 * changes to the array are ignored.
14897 *
14898 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
14899 * allowed in this array.
14900 *
14901 * Note: **an empty whitelist array will block all URLs**!
14902 *
14903 * @return {Array} the currently set whitelist array.
14904 *
14905 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
14906 * same origin resource requests.
14907 *
14908 * @description
14909 * Sets/Gets the whitelist of trusted resource URLs.
14910 */
14911 this.resourceUrlWhitelist = function(value) {
14912 if (arguments.length) {
14913 resourceUrlWhitelist = adjustMatchers(value);
14914 }
14915 return resourceUrlWhitelist;
14916 };
14917
14918 /**
14919 * @ngdoc method
14920 * @name $sceDelegateProvider#resourceUrlBlacklist
14921 * @kind function
14922 *
14923 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
14924 * provided. This must be an array or null. A snapshot of this array is used so further
14925 * changes to the array are ignored.
14926 *
14927 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
14928 * allowed in this array.
14929 *
14930 * The typical usage for the blacklist is to **block
14931 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
14932 * these would otherwise be trusted but actually return content from the redirected domain.
14933 *
14934 * Finally, **the blacklist overrides the whitelist** and has the final say.
14935 *
14936 * @return {Array} the currently set blacklist array.
14937 *
14938 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
14939 * is no blacklist.)
14940 *
14941 * @description
14942 * Sets/Gets the blacklist of trusted resource URLs.
14943 */
14944
14945 this.resourceUrlBlacklist = function(value) {
14946 if (arguments.length) {
14947 resourceUrlBlacklist = adjustMatchers(value);
14948 }
14949 return resourceUrlBlacklist;
14950 };
14951
14952 this.$get = ['$injector', function($injector) {
14953
14954 var htmlSanitizer = function htmlSanitizer(html) {
14955 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
14956 };
14957
14958 if ($injector.has('$sanitize')) {
14959 htmlSanitizer = $injector.get('$sanitize');
14960 }
14961
14962
14963 function matchUrl(matcher, parsedUrl) {
14964 if (matcher === 'self') {
14965 return urlIsSameOrigin(parsedUrl);
14966 } else {
14967 // definitely a regex. See adjustMatchers()
14968 return !!matcher.exec(parsedUrl.href);
14969 }
14970 }
14971
14972 function isResourceUrlAllowedByPolicy(url) {
14973 var parsedUrl = urlResolve(url.toString());
14974 var i, n, allowed = false;
14975 // Ensure that at least one item from the whitelist allows this url.
14976 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
14977 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
14978 allowed = true;
14979 break;
14980 }
14981 }
14982 if (allowed) {
14983 // Ensure that no item from the blacklist blocked this url.
14984 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
14985 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
14986 allowed = false;
14987 break;
14988 }
14989 }
14990 }
14991 return allowed;
14992 }
14993
14994 function generateHolderType(Base) {
14995 var holderType = function TrustedValueHolderType(trustedValue) {
14996 this.$$unwrapTrustedValue = function() {
14997 return trustedValue;
14998 };
14999 };
15000 if (Base) {
15001 holderType.prototype = new Base();
15002 }
15003 holderType.prototype.valueOf = function sceValueOf() {
15004 return this.$$unwrapTrustedValue();
15005 };
15006 holderType.prototype.toString = function sceToString() {
15007 return this.$$unwrapTrustedValue().toString();
15008 };
15009 return holderType;
15010 }
15011
15012 var trustedValueHolderBase = generateHolderType(),
15013 byType = {};
15014
15015 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
15016 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
15017 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
15018 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
15019 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
15020
15021 /**
15022 * @ngdoc method
15023 * @name $sceDelegate#trustAs
15024 *
15025 * @description
15026 * Returns an object that is trusted by angular for use in specified strict
15027 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
15028 * attribute interpolation, any dom event binding attribute interpolation
15029 * such as for onclick, etc.) that uses the provided value.
15030 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
15031 *
15032 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
15033 * resourceUrl, html, js and css.
15034 * @param {*} value The value that that should be considered trusted/safe.
15035 * @returns {*} A value that can be used to stand in for the provided `value` in places
15036 * where Angular expects a $sce.trustAs() return value.
15037 */
15038 function trustAs(type, trustedValue) {
15039 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15040 if (!Constructor) {
15041 throw $sceMinErr('icontext',
15042 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
15043 type, trustedValue);
15044 }
15045 if (trustedValue === null || trustedValue === undefined || trustedValue === '') {
15046 return trustedValue;
15047 }
15048 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
15049 // mutable objects, we ensure here that the value passed in is actually a string.
15050 if (typeof trustedValue !== 'string') {
15051 throw $sceMinErr('itype',
15052 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
15053 type);
15054 }
15055 return new Constructor(trustedValue);
15056 }
15057
15058 /**
15059 * @ngdoc method
15060 * @name $sceDelegate#valueOf
15061 *
15062 * @description
15063 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
15064 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
15065 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
15066 *
15067 * If the passed parameter is not a value that had been returned by {@link
15068 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
15069 *
15070 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
15071 * call or anything else.
15072 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
15073 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
15074 * `value` unchanged.
15075 */
15076 function valueOf(maybeTrusted) {
15077 if (maybeTrusted instanceof trustedValueHolderBase) {
15078 return maybeTrusted.$$unwrapTrustedValue();
15079 } else {
15080 return maybeTrusted;
15081 }
15082 }
15083
15084 /**
15085 * @ngdoc method
15086 * @name $sceDelegate#getTrusted
15087 *
15088 * @description
15089 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
15090 * returns the originally supplied value if the queried context type is a supertype of the
15091 * created type. If this condition isn't satisfied, throws an exception.
15092 *
15093 * @param {string} type The kind of context in which this value is to be used.
15094 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
15095 * `$sceDelegate.trustAs`} call.
15096 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
15097 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
15098 */
15099 function getTrusted(type, maybeTrusted) {
15100 if (maybeTrusted === null || maybeTrusted === undefined || maybeTrusted === '') {
15101 return maybeTrusted;
15102 }
15103 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
15104 if (constructor && maybeTrusted instanceof constructor) {
15105 return maybeTrusted.$$unwrapTrustedValue();
15106 }
15107 // If we get here, then we may only take one of two actions.
15108 // 1. sanitize the value for the requested type, or
15109 // 2. throw an exception.
15110 if (type === SCE_CONTEXTS.RESOURCE_URL) {
15111 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
15112 return maybeTrusted;
15113 } else {
15114 throw $sceMinErr('insecurl',
15115 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
15116 maybeTrusted.toString());
15117 }
15118 } else if (type === SCE_CONTEXTS.HTML) {
15119 return htmlSanitizer(maybeTrusted);
15120 }
15121 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
15122 }
15123
15124 return { trustAs: trustAs,
15125 getTrusted: getTrusted,
15126 valueOf: valueOf };
15127 }];
15128}
15129
15130
15131/**
15132 * @ngdoc provider
15133 * @name $sceProvider
15134 * @description
15135 *
15136 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
15137 * - enable/disable Strict Contextual Escaping (SCE) in a module
15138 * - override the default implementation with a custom delegate
15139 *
15140 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
15141 */
15142
15143/* jshint maxlen: false*/
15144
15145/**
15146 * @ngdoc service
15147 * @name $sce
15148 * @kind function
15149 *
15150 * @description
15151 *
15152 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
15153 *
15154 * # Strict Contextual Escaping
15155 *
15156 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
15157 * contexts to result in a value that is marked as safe to use for that context. One example of
15158 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
15159 * to these contexts as privileged or SCE contexts.
15160 *
15161 * As of version 1.2, Angular ships with SCE enabled by default.
15162 *
15163 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
15164 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
15165 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
15166 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
15167 * to the top of your HTML document.
15168 *
15169 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
15170 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
15171 *
15172 * Here's an example of a binding in a privileged context:
15173 *
15174 * ```
15175 * <input ng-model="userHtml">
15176 * <div ng-bind-html="userHtml"></div>
15177 * ```
15178 *
15179 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
15180 * disabled, this application allows the user to render arbitrary HTML into the DIV.
15181 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
15182 * bindings. (HTML is just one example of a context where rendering user controlled input creates
15183 * security vulnerabilities.)
15184 *
15185 * For the case of HTML, you might use a library, either on the client side, or on the server side,
15186 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
15187 *
15188 * How would you ensure that every place that used these types of bindings was bound to a value that
15189 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
15190 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
15191 * properties/fields and forgot to update the binding to the sanitized value?
15192 *
15193 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
15194 * determine that something explicitly says it's safe to use a value for binding in that
15195 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
15196 * for those values that you can easily tell are safe - because they were received from your server,
15197 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
15198 * allowing only the files in a specific directory to do this. Ensuring that the internal API
15199 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
15200 *
15201 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
15202 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
15203 * obtain values that will be accepted by SCE / privileged contexts.
15204 *
15205 *
15206 * ## How does it work?
15207 *
15208 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
15209 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
15210 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
15211 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
15212 *
15213 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
15214 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
15215 * simplified):
15216 *
15217 * ```
15218 * var ngBindHtmlDirective = ['$sce', function($sce) {
15219 * return function(scope, element, attr) {
15220 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
15221 * element.html(value || '');
15222 * });
15223 * };
15224 * }];
15225 * ```
15226 *
15227 * ## Impact on loading templates
15228 *
15229 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
15230 * `templateUrl`'s specified by {@link guide/directive directives}.
15231 *
15232 * By default, Angular only loads templates from the same domain and protocol as the application
15233 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
15234 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
15235 * protocols, you may either either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
15236 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
15237 *
15238 * *Please note*:
15239 * The browser's
15240 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
15241 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
15242 * policy apply in addition to this and may further restrict whether the template is successfully
15243 * loaded. This means that without the right CORS policy, loading templates from a different domain
15244 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
15245 * browsers.
15246 *
15247 * ## This feels like too much overhead
15248 *
15249 * It's important to remember that SCE only applies to interpolation expressions.
15250 *
15251 * If your expressions are constant literals, they're automatically trusted and you don't need to
15252 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
15253 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
15254 *
15255 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
15256 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
15257 *
15258 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
15259 * templates in `ng-include` from your application's domain without having to even know about SCE.
15260 * It blocks loading templates from other domains or loading templates over http from an https
15261 * served document. You can change these by setting your own custom {@link
15262 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
15263 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
15264 *
15265 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
15266 * application that's secure and can be audited to verify that with much more ease than bolting
15267 * security onto an application later.
15268 *
15269 * <a name="contexts"></a>
15270 * ## What trusted context types are supported?
15271 *
15272 * | Context | Notes |
15273 * |---------------------|----------------|
15274 * | `$sce.HTML` | For HTML that's safe to source into the application. The {@link ng.directive:ngBindHtml ngBindHtml} directive uses this context for bindings. If an unsafe value is encountered and the {@link ngSanitize $sanitize} module is present this will sanitize the value instead of throwing an error. |
15275 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
15276 * | `$sce.URL` | For URLs that are safe to follow as links. Currently unused (`<a href=` and `<img src=` sanitize their urls and don't constitute an SCE context. |
15277 * | `$sce.RESOURCE_URL` | For URLs that are not only safe to follow as links, but whose contents are also safe to include in your application. Examples include `ng-include`, `src` / `ngSrc` bindings for tags other than `IMG` (e.g. `IFRAME`, `OBJECT`, etc.) <br><br>Note that `$sce.RESOURCE_URL` makes a stronger statement about the URL than `$sce.URL` does and therefore contexts requiring values trusted for `$sce.RESOURCE_URL` can be used anywhere that values trusted for `$sce.URL` are required. |
15278 * | `$sce.JS` | For JavaScript that is safe to execute in your application's context. Currently unused. Feel free to use it in your own directives. |
15279 *
15280 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
15281 *
15282 * Each element in these arrays must be one of the following:
15283 *
15284 * - **'self'**
15285 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
15286 * domain** as the application document using the **same protocol**.
15287 * - **String** (except the special value `'self'`)
15288 * - The string is matched against the full *normalized / absolute URL* of the resource
15289 * being tested (substring matches are not good enough.)
15290 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
15291 * match themselves.
15292 * - `*`: matches zero or more occurrences of any character other than one of the following 6
15293 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and ';'. It's a useful wildcard for use
15294 * in a whitelist.
15295 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
15296 * not appropriate to use in for a scheme, domain, etc. as it would match too much. (e.g.
15297 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
15298 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
15299 * http://foo.example.com/templates/**).
15300 * - **RegExp** (*see caveat below*)
15301 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
15302 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
15303 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
15304 * have good test coverage.). For instance, the use of `.` in the regex is correct only in a
15305 * small number of cases. A `.` character in the regex used when matching the scheme or a
15306 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
15307 * is highly recommended to use the string patterns and only fall back to regular expressions
15308 * if they as a last resort.
15309 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
15310 * matched against the **entire** *normalized / absolute URL* of the resource being tested
15311 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
15312 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
15313 * - If you are generating your JavaScript from some other templating engine (not
15314 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
15315 * remember to escape your regular expression (and be aware that you might need more than
15316 * one level of escaping depending on your templating engine and the way you interpolated
15317 * the value.) Do make use of your platform's escaping mechanism as it might be good
15318 * enough before coding your own. e.g. Ruby has
15319 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
15320 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
15321 * Javascript lacks a similar built in function for escaping. Take a look at Google
15322 * Closure library's [goog.string.regExpEscape(s)](
15323 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
15324 *
15325 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
15326 *
15327 * ## Show me an example using SCE.
15328 *
15329 * <example module="mySceApp" deps="angular-sanitize.js">
15330 * <file name="index.html">
15331 * <div ng-controller="AppController as myCtrl">
15332 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
15333 * <b>User comments</b><br>
15334 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
15335 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
15336 * exploit.
15337 * <div class="well">
15338 * <div ng-repeat="userComment in myCtrl.userComments">
15339 * <b>{{userComment.name}}</b>:
15340 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
15341 * <br>
15342 * </div>
15343 * </div>
15344 * </div>
15345 * </file>
15346 *
15347 * <file name="script.js">
15348 * angular.module('mySceApp', ['ngSanitize'])
15349 * .controller('AppController', ['$http', '$templateCache', '$sce',
15350 * function($http, $templateCache, $sce) {
15351 * var self = this;
15352 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
15353 * self.userComments = userComments;
15354 * });
15355 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
15356 * '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
15357 * 'sanitization.&quot;">Hover over this text.</span>');
15358 * }]);
15359 * </file>
15360 *
15361 * <file name="test_data.json">
15362 * [
15363 * { "name": "Alice",
15364 * "htmlComment":
15365 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
15366 * },
15367 * { "name": "Bob",
15368 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
15369 * }
15370 * ]
15371 * </file>
15372 *
15373 * <file name="protractor.js" type="protractor">
15374 * describe('SCE doc demo', function() {
15375 * it('should sanitize untrusted values', function() {
15376 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
15377 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
15378 * });
15379 *
15380 * it('should NOT sanitize explicitly trusted values', function() {
15381 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
15382 * '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
15383 * 'sanitization.&quot;">Hover over this text.</span>');
15384 * });
15385 * });
15386 * </file>
15387 * </example>
15388 *
15389 *
15390 *
15391 * ## Can I disable SCE completely?
15392 *
15393 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
15394 * for little coding overhead. It will be much harder to take an SCE disabled application and
15395 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
15396 * for cases where you have a lot of existing code that was written before SCE was introduced and
15397 * you're migrating them a module at a time.
15398 *
15399 * That said, here's how you can completely disable SCE:
15400 *
15401 * ```
15402 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
15403 * // Completely disable SCE. For demonstration purposes only!
15404 * // Do not use in new projects.
15405 * $sceProvider.enabled(false);
15406 * });
15407 * ```
15408 *
15409 */
15410/* jshint maxlen: 100 */
15411
15412function $SceProvider() {
15413 var enabled = true;
15414
15415 /**
15416 * @ngdoc method
15417 * @name $sceProvider#enabled
15418 * @kind function
15419 *
15420 * @param {boolean=} value If provided, then enables/disables SCE.
15421 * @return {boolean} true if SCE is enabled, false otherwise.
15422 *
15423 * @description
15424 * Enables/disables SCE and returns the current value.
15425 */
15426 this.enabled = function(value) {
15427 if (arguments.length) {
15428 enabled = !!value;
15429 }
15430 return enabled;
15431 };
15432
15433
15434 /* Design notes on the default implementation for SCE.
15435 *
15436 * The API contract for the SCE delegate
15437 * -------------------------------------
15438 * The SCE delegate object must provide the following 3 methods:
15439 *
15440 * - trustAs(contextEnum, value)
15441 * This method is used to tell the SCE service that the provided value is OK to use in the
15442 * contexts specified by contextEnum. It must return an object that will be accepted by
15443 * getTrusted() for a compatible contextEnum and return this value.
15444 *
15445 * - valueOf(value)
15446 * For values that were not produced by trustAs(), return them as is. For values that were
15447 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
15448 * trustAs is wrapping the given values into some type, this operation unwraps it when given
15449 * such a value.
15450 *
15451 * - getTrusted(contextEnum, value)
15452 * This function should return the a value that is safe to use in the context specified by
15453 * contextEnum or throw and exception otherwise.
15454 *
15455 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
15456 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
15457 * instance, an implementation could maintain a registry of all trusted objects by context. In
15458 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
15459 * return the same object passed in if it was found in the registry under a compatible context or
15460 * throw an exception otherwise. An implementation might only wrap values some of the time based
15461 * on some criteria. getTrusted() might return a value and not throw an exception for special
15462 * constants or objects even if not wrapped. All such implementations fulfill this contract.
15463 *
15464 *
15465 * A note on the inheritance model for SCE contexts
15466 * ------------------------------------------------
15467 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
15468 * is purely an implementation details.
15469 *
15470 * The contract is simply this:
15471 *
15472 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
15473 * will also succeed.
15474 *
15475 * Inheritance happens to capture this in a natural way. In some future, we
15476 * may not use inheritance anymore. That is OK because no code outside of
15477 * sce.js and sceSpecs.js would need to be aware of this detail.
15478 */
15479
15480 this.$get = ['$parse', '$sceDelegate', function(
15481 $parse, $sceDelegate) {
15482 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
15483 // the "expression(javascript expression)" syntax which is insecure.
15484 if (enabled && msie < 8) {
15485 throw $sceMinErr('iequirks',
15486 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
15487 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
15488 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
15489 }
15490
15491 var sce = shallowCopy(SCE_CONTEXTS);
15492
15493 /**
15494 * @ngdoc method
15495 * @name $sce#isEnabled
15496 * @kind function
15497 *
15498 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
15499 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
15500 *
15501 * @description
15502 * Returns a boolean indicating if SCE is enabled.
15503 */
15504 sce.isEnabled = function() {
15505 return enabled;
15506 };
15507 sce.trustAs = $sceDelegate.trustAs;
15508 sce.getTrusted = $sceDelegate.getTrusted;
15509 sce.valueOf = $sceDelegate.valueOf;
15510
15511 if (!enabled) {
15512 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
15513 sce.valueOf = identity;
15514 }
15515
15516 /**
15517 * @ngdoc method
15518 * @name $sce#parseAs
15519 *
15520 * @description
15521 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
15522 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
15523 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
15524 * *result*)}
15525 *
15526 * @param {string} type The kind of SCE context in which this result will be used.
15527 * @param {string} expression String expression to compile.
15528 * @returns {function(context, locals)} a function which represents the compiled expression:
15529 *
15530 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15531 * are evaluated against (typically a scope object).
15532 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15533 * `context`.
15534 */
15535 sce.parseAs = function sceParseAs(type, expr) {
15536 var parsed = $parse(expr);
15537 if (parsed.literal && parsed.constant) {
15538 return parsed;
15539 } else {
15540 return $parse(expr, function(value) {
15541 return sce.getTrusted(type, value);
15542 });
15543 }
15544 };
15545
15546 /**
15547 * @ngdoc method
15548 * @name $sce#trustAs
15549 *
15550 * @description
15551 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
15552 * returns an object that is trusted by angular for use in specified strict contextual
15553 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
15554 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
15555 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
15556 * escaping.
15557 *
15558 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
15559 * resource_url, html, js and css.
15560 * @param {*} value The value that that should be considered trusted/safe.
15561 * @returns {*} A value that can be used to stand in for the provided `value` in places
15562 * where Angular expects a $sce.trustAs() return value.
15563 */
15564
15565 /**
15566 * @ngdoc method
15567 * @name $sce#trustAsHtml
15568 *
15569 * @description
15570 * Shorthand method. `$sce.trustAsHtml(value)` →
15571 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
15572 *
15573 * @param {*} value The value to trustAs.
15574 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
15575 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
15576 * only accept expressions that are either literal constants or are the
15577 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15578 */
15579
15580 /**
15581 * @ngdoc method
15582 * @name $sce#trustAsUrl
15583 *
15584 * @description
15585 * Shorthand method. `$sce.trustAsUrl(value)` →
15586 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
15587 *
15588 * @param {*} value The value to trustAs.
15589 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
15590 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
15591 * only accept expressions that are either literal constants or are the
15592 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15593 */
15594
15595 /**
15596 * @ngdoc method
15597 * @name $sce#trustAsResourceUrl
15598 *
15599 * @description
15600 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
15601 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
15602 *
15603 * @param {*} value The value to trustAs.
15604 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
15605 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
15606 * only accept expressions that are either literal constants or are the return
15607 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
15608 */
15609
15610 /**
15611 * @ngdoc method
15612 * @name $sce#trustAsJs
15613 *
15614 * @description
15615 * Shorthand method. `$sce.trustAsJs(value)` →
15616 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
15617 *
15618 * @param {*} value The value to trustAs.
15619 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
15620 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
15621 * only accept expressions that are either literal constants or are the
15622 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
15623 */
15624
15625 /**
15626 * @ngdoc method
15627 * @name $sce#getTrusted
15628 *
15629 * @description
15630 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
15631 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
15632 * originally supplied value if the queried context type is a supertype of the created type.
15633 * If this condition isn't satisfied, throws an exception.
15634 *
15635 * @param {string} type The kind of context in which this value is to be used.
15636 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
15637 * call.
15638 * @returns {*} The value the was originally provided to
15639 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
15640 * Otherwise, throws an exception.
15641 */
15642
15643 /**
15644 * @ngdoc method
15645 * @name $sce#getTrustedHtml
15646 *
15647 * @description
15648 * Shorthand method. `$sce.getTrustedHtml(value)` →
15649 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
15650 *
15651 * @param {*} value The value to pass to `$sce.getTrusted`.
15652 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
15653 */
15654
15655 /**
15656 * @ngdoc method
15657 * @name $sce#getTrustedCss
15658 *
15659 * @description
15660 * Shorthand method. `$sce.getTrustedCss(value)` →
15661 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
15662 *
15663 * @param {*} value The value to pass to `$sce.getTrusted`.
15664 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
15665 */
15666
15667 /**
15668 * @ngdoc method
15669 * @name $sce#getTrustedUrl
15670 *
15671 * @description
15672 * Shorthand method. `$sce.getTrustedUrl(value)` →
15673 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
15674 *
15675 * @param {*} value The value to pass to `$sce.getTrusted`.
15676 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
15677 */
15678
15679 /**
15680 * @ngdoc method
15681 * @name $sce#getTrustedResourceUrl
15682 *
15683 * @description
15684 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
15685 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
15686 *
15687 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
15688 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
15689 */
15690
15691 /**
15692 * @ngdoc method
15693 * @name $sce#getTrustedJs
15694 *
15695 * @description
15696 * Shorthand method. `$sce.getTrustedJs(value)` →
15697 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
15698 *
15699 * @param {*} value The value to pass to `$sce.getTrusted`.
15700 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
15701 */
15702
15703 /**
15704 * @ngdoc method
15705 * @name $sce#parseAsHtml
15706 *
15707 * @description
15708 * Shorthand method. `$sce.parseAsHtml(expression string)` →
15709 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
15710 *
15711 * @param {string} expression String expression to compile.
15712 * @returns {function(context, locals)} a function which represents the compiled expression:
15713 *
15714 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15715 * are evaluated against (typically a scope object).
15716 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15717 * `context`.
15718 */
15719
15720 /**
15721 * @ngdoc method
15722 * @name $sce#parseAsCss
15723 *
15724 * @description
15725 * Shorthand method. `$sce.parseAsCss(value)` →
15726 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
15727 *
15728 * @param {string} expression String expression to compile.
15729 * @returns {function(context, locals)} a function which represents the compiled expression:
15730 *
15731 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15732 * are evaluated against (typically a scope object).
15733 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15734 * `context`.
15735 */
15736
15737 /**
15738 * @ngdoc method
15739 * @name $sce#parseAsUrl
15740 *
15741 * @description
15742 * Shorthand method. `$sce.parseAsUrl(value)` →
15743 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
15744 *
15745 * @param {string} expression String expression to compile.
15746 * @returns {function(context, locals)} a function which represents the compiled expression:
15747 *
15748 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15749 * are evaluated against (typically a scope object).
15750 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15751 * `context`.
15752 */
15753
15754 /**
15755 * @ngdoc method
15756 * @name $sce#parseAsResourceUrl
15757 *
15758 * @description
15759 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
15760 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
15761 *
15762 * @param {string} expression String expression to compile.
15763 * @returns {function(context, locals)} a function which represents the compiled expression:
15764 *
15765 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15766 * are evaluated against (typically a scope object).
15767 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15768 * `context`.
15769 */
15770
15771 /**
15772 * @ngdoc method
15773 * @name $sce#parseAsJs
15774 *
15775 * @description
15776 * Shorthand method. `$sce.parseAsJs(value)` →
15777 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
15778 *
15779 * @param {string} expression String expression to compile.
15780 * @returns {function(context, locals)} a function which represents the compiled expression:
15781 *
15782 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15783 * are evaluated against (typically a scope object).
15784 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15785 * `context`.
15786 */
15787
15788 // Shorthand delegations.
15789 var parse = sce.parseAs,
15790 getTrusted = sce.getTrusted,
15791 trustAs = sce.trustAs;
15792
15793 forEach(SCE_CONTEXTS, function(enumValue, name) {
15794 var lName = lowercase(name);
15795 sce[camelCase("parse_as_" + lName)] = function(expr) {
15796 return parse(enumValue, expr);
15797 };
15798 sce[camelCase("get_trusted_" + lName)] = function(value) {
15799 return getTrusted(enumValue, value);
15800 };
15801 sce[camelCase("trust_as_" + lName)] = function(value) {
15802 return trustAs(enumValue, value);
15803 };
15804 });
15805
15806 return sce;
15807 }];
15808}
15809
15810/**
15811 * !!! This is an undocumented "private" service !!!
15812 *
15813 * @name $sniffer
15814 * @requires $window
15815 * @requires $document
15816 *
15817 * @property {boolean} history Does the browser support html5 history api ?
15818 * @property {boolean} transitions Does the browser support CSS transition events ?
15819 * @property {boolean} animations Does the browser support CSS animation events ?
15820 *
15821 * @description
15822 * This is very simple implementation of testing browser's features.
15823 */
15824function $SnifferProvider() {
15825 this.$get = ['$window', '$document', function($window, $document) {
15826 var eventSupport = {},
15827 android =
15828 int((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
15829 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
15830 document = $document[0] || {},
15831 vendorPrefix,
15832 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
15833 bodyStyle = document.body && document.body.style,
15834 transitions = false,
15835 animations = false,
15836 match;
15837
15838 if (bodyStyle) {
15839 for (var prop in bodyStyle) {
15840 if (match = vendorRegex.exec(prop)) {
15841 vendorPrefix = match[0];
15842 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
15843 break;
15844 }
15845 }
15846
15847 if (!vendorPrefix) {
15848 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
15849 }
15850
15851 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
15852 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
15853
15854 if (android && (!transitions || !animations)) {
15855 transitions = isString(document.body.style.webkitTransition);
15856 animations = isString(document.body.style.webkitAnimation);
15857 }
15858 }
15859
15860
15861 return {
15862 // Android has history.pushState, but it does not update location correctly
15863 // so let's not use the history API at all.
15864 // http://code.google.com/p/android/issues/detail?id=17471
15865 // https://github.com/angular/angular.js/issues/904
15866
15867 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
15868 // so let's not use the history API also
15869 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
15870 // jshint -W018
15871 history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
15872 // jshint +W018
15873 hasEvent: function(event) {
15874 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
15875 // it. In particular the event is not fired when backspace or delete key are pressed or
15876 // when cut operation is performed.
15877 if (event == 'input' && msie == 9) return false;
15878
15879 if (isUndefined(eventSupport[event])) {
15880 var divElm = document.createElement('div');
15881 eventSupport[event] = 'on' + event in divElm;
15882 }
15883
15884 return eventSupport[event];
15885 },
15886 csp: csp(),
15887 vendorPrefix: vendorPrefix,
15888 transitions: transitions,
15889 animations: animations,
15890 android: android
15891 };
15892 }];
15893}
15894
15895var $compileMinErr = minErr('$compile');
15896
15897/**
15898 * @ngdoc service
15899 * @name $templateRequest
15900 *
15901 * @description
15902 * The `$templateRequest` service downloads the provided template using `$http` and, upon success,
15903 * stores the contents inside of `$templateCache`. If the HTTP request fails or the response data
15904 * of the HTTP request is empty then a `$compile` error will be thrown (the exception can be thwarted
15905 * by setting the 2nd parameter of the function to true).
15906 *
15907 * @param {string} tpl The HTTP request template URL
15908 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
15909 *
15910 * @return {Promise} the HTTP Promise for the given.
15911 *
15912 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
15913 */
15914function $TemplateRequestProvider() {
15915 this.$get = ['$templateCache', '$http', '$q', function($templateCache, $http, $q) {
15916 function handleRequestFn(tpl, ignoreRequestError) {
15917 var self = handleRequestFn;
15918 self.totalPendingRequests++;
15919
15920 var transformResponse = $http.defaults && $http.defaults.transformResponse;
15921
15922 if (isArray(transformResponse)) {
15923 transformResponse = transformResponse.filter(function(transformer) {
15924 return transformer !== defaultHttpResponseTransform;
15925 });
15926 } else if (transformResponse === defaultHttpResponseTransform) {
15927 transformResponse = null;
15928 }
15929
15930 var httpOptions = {
15931 cache: $templateCache,
15932 transformResponse: transformResponse
15933 };
15934
15935 return $http.get(tpl, httpOptions)
15936 .then(function(response) {
15937 var html = response.data;
15938 self.totalPendingRequests--;
15939 $templateCache.put(tpl, html);
15940 return html;
15941 }, handleError);
15942
15943 function handleError(resp) {
15944 self.totalPendingRequests--;
15945 if (!ignoreRequestError) {
15946 throw $compileMinErr('tpload', 'Failed to load template: {0}', tpl);
15947 }
15948 return $q.reject(resp);
15949 }
15950 }
15951
15952 handleRequestFn.totalPendingRequests = 0;
15953
15954 return handleRequestFn;
15955 }];
15956}
15957
15958function $$TestabilityProvider() {
15959 this.$get = ['$rootScope', '$browser', '$location',
15960 function($rootScope, $browser, $location) {
15961
15962 /**
15963 * @name $testability
15964 *
15965 * @description
15966 * The private $$testability service provides a collection of methods for use when debugging
15967 * or by automated test and debugging tools.
15968 */
15969 var testability = {};
15970
15971 /**
15972 * @name $$testability#findBindings
15973 *
15974 * @description
15975 * Returns an array of elements that are bound (via ng-bind or {{}})
15976 * to expressions matching the input.
15977 *
15978 * @param {Element} element The element root to search from.
15979 * @param {string} expression The binding expression to match.
15980 * @param {boolean} opt_exactMatch If true, only returns exact matches
15981 * for the expression. Filters and whitespace are ignored.
15982 */
15983 testability.findBindings = function(element, expression, opt_exactMatch) {
15984 var bindings = element.getElementsByClassName('ng-binding');
15985 var matches = [];
15986 forEach(bindings, function(binding) {
15987 var dataBinding = angular.element(binding).data('$binding');
15988 if (dataBinding) {
15989 forEach(dataBinding, function(bindingName) {
15990 if (opt_exactMatch) {
15991 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
15992 if (matcher.test(bindingName)) {
15993 matches.push(binding);
15994 }
15995 } else {
15996 if (bindingName.indexOf(expression) != -1) {
15997 matches.push(binding);
15998 }
15999 }
16000 });
16001 }
16002 });
16003 return matches;
16004 };
16005
16006 /**
16007 * @name $$testability#findModels
16008 *
16009 * @description
16010 * Returns an array of elements that are two-way found via ng-model to
16011 * expressions matching the input.
16012 *
16013 * @param {Element} element The element root to search from.
16014 * @param {string} expression The model expression to match.
16015 * @param {boolean} opt_exactMatch If true, only returns exact matches
16016 * for the expression.
16017 */
16018 testability.findModels = function(element, expression, opt_exactMatch) {
16019 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
16020 for (var p = 0; p < prefixes.length; ++p) {
16021 var attributeEquals = opt_exactMatch ? '=' : '*=';
16022 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
16023 var elements = element.querySelectorAll(selector);
16024 if (elements.length) {
16025 return elements;
16026 }
16027 }
16028 };
16029
16030 /**
16031 * @name $$testability#getLocation
16032 *
16033 * @description
16034 * Shortcut for getting the location in a browser agnostic way. Returns
16035 * the path, search, and hash. (e.g. /path?a=b#hash)
16036 */
16037 testability.getLocation = function() {
16038 return $location.url();
16039 };
16040
16041 /**
16042 * @name $$testability#setLocation
16043 *
16044 * @description
16045 * Shortcut for navigating to a location without doing a full page reload.
16046 *
16047 * @param {string} url The location url (path, search and hash,
16048 * e.g. /path?a=b#hash) to go to.
16049 */
16050 testability.setLocation = function(url) {
16051 if (url !== $location.url()) {
16052 $location.url(url);
16053 $rootScope.$digest();
16054 }
16055 };
16056
16057 /**
16058 * @name $$testability#whenStable
16059 *
16060 * @description
16061 * Calls the callback when $timeout and $http requests are completed.
16062 *
16063 * @param {function} callback
16064 */
16065 testability.whenStable = function(callback) {
16066 $browser.notifyWhenNoOutstandingRequests(callback);
16067 };
16068
16069 return testability;
16070 }];
16071}
16072
16073function $TimeoutProvider() {
16074 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
16075 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
16076 var deferreds = {};
16077
16078
16079 /**
16080 * @ngdoc service
16081 * @name $timeout
16082 *
16083 * @description
16084 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
16085 * block and delegates any exceptions to
16086 * {@link ng.$exceptionHandler $exceptionHandler} service.
16087 *
16088 * The return value of registering a timeout function is a promise, which will be resolved when
16089 * the timeout is reached and the timeout function is executed.
16090 *
16091 * To cancel a timeout request, call `$timeout.cancel(promise)`.
16092 *
16093 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
16094 * synchronously flush the queue of deferred functions.
16095 *
16096 * @param {function()} fn A function, whose execution should be delayed.
16097 * @param {number=} [delay=0] Delay in milliseconds.
16098 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
16099 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
16100 * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
16101 * promise will be resolved with is the return value of the `fn` function.
16102 *
16103 */
16104 function timeout(fn, delay, invokeApply) {
16105 var skipApply = (isDefined(invokeApply) && !invokeApply),
16106 deferred = (skipApply ? $$q : $q).defer(),
16107 promise = deferred.promise,
16108 timeoutId;
16109
16110 timeoutId = $browser.defer(function() {
16111 try {
16112 deferred.resolve(fn());
16113 } catch (e) {
16114 deferred.reject(e);
16115 $exceptionHandler(e);
16116 }
16117 finally {
16118 delete deferreds[promise.$$timeoutId];
16119 }
16120
16121 if (!skipApply) $rootScope.$apply();
16122 }, delay);
16123
16124 promise.$$timeoutId = timeoutId;
16125 deferreds[timeoutId] = deferred;
16126
16127 return promise;
16128 }
16129
16130
16131 /**
16132 * @ngdoc method
16133 * @name $timeout#cancel
16134 *
16135 * @description
16136 * Cancels a task associated with the `promise`. As a result of this, the promise will be
16137 * resolved with a rejection.
16138 *
16139 * @param {Promise=} promise Promise returned by the `$timeout` function.
16140 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
16141 * canceled.
16142 */
16143 timeout.cancel = function(promise) {
16144 if (promise && promise.$$timeoutId in deferreds) {
16145 deferreds[promise.$$timeoutId].reject('canceled');
16146 delete deferreds[promise.$$timeoutId];
16147 return $browser.defer.cancel(promise.$$timeoutId);
16148 }
16149 return false;
16150 };
16151
16152 return timeout;
16153 }];
16154}
16155
16156// NOTE: The usage of window and document instead of $window and $document here is
16157// deliberate. This service depends on the specific behavior of anchor nodes created by the
16158// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
16159// cause us to break tests. In addition, when the browser resolves a URL for XHR, it
16160// doesn't know about mocked locations and resolves URLs to the real document - which is
16161// exactly the behavior needed here. There is little value is mocking these out for this
16162// service.
16163var urlParsingNode = document.createElement("a");
16164var originUrl = urlResolve(window.location.href);
16165
16166
16167/**
16168 *
16169 * Implementation Notes for non-IE browsers
16170 * ----------------------------------------
16171 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
16172 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
16173 * URL will be resolved into an absolute URL in the context of the application document.
16174 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
16175 * properties are all populated to reflect the normalized URL. This approach has wide
16176 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
16177 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16178 *
16179 * Implementation Notes for IE
16180 * ---------------------------
16181 * IE >= 8 and <= 10 normalizes the URL when assigned to the anchor node similar to the other
16182 * browsers. However, the parsed components will not be set if the URL assigned did not specify
16183 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
16184 * work around that by performing the parsing in a 2nd step by taking a previously normalized
16185 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
16186 * properties such as protocol, hostname, port, etc.
16187 *
16188 * IE7 does not normalize the URL when assigned to an anchor node. (Apparently, it does, if one
16189 * uses the inner HTML approach to assign the URL as part of an HTML snippet -
16190 * http://stackoverflow.com/a/472729) However, setting img[src] does normalize the URL.
16191 * Unfortunately, setting img[src] to something like "javascript:foo" on IE throws an exception.
16192 * Since the primary usage for normalizing URLs is to sanitize such URLs, we can't use that
16193 * method and IE < 8 is unsupported.
16194 *
16195 * References:
16196 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
16197 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
16198 * http://url.spec.whatwg.org/#urlutils
16199 * https://github.com/angular/angular.js/pull/2902
16200 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
16201 *
16202 * @kind function
16203 * @param {string} url The URL to be parsed.
16204 * @description Normalizes and parses a URL.
16205 * @returns {object} Returns the normalized URL as a dictionary.
16206 *
16207 * | member name | Description |
16208 * |---------------|----------------|
16209 * | href | A normalized version of the provided URL if it was not an absolute URL |
16210 * | protocol | The protocol including the trailing colon |
16211 * | host | The host and port (if the port is non-default) of the normalizedUrl |
16212 * | search | The search params, minus the question mark |
16213 * | hash | The hash string, minus the hash symbol
16214 * | hostname | The hostname
16215 * | port | The port, without ":"
16216 * | pathname | The pathname, beginning with "/"
16217 *
16218 */
16219function urlResolve(url) {
16220 var href = url;
16221
16222 if (msie) {
16223 // Normalize before parse. Refer Implementation Notes on why this is
16224 // done in two steps on IE.
16225 urlParsingNode.setAttribute("href", href);
16226 href = urlParsingNode.href;
16227 }
16228
16229 urlParsingNode.setAttribute('href', href);
16230
16231 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
16232 return {
16233 href: urlParsingNode.href,
16234 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
16235 host: urlParsingNode.host,
16236 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
16237 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
16238 hostname: urlParsingNode.hostname,
16239 port: urlParsingNode.port,
16240 pathname: (urlParsingNode.pathname.charAt(0) === '/')
16241 ? urlParsingNode.pathname
16242 : '/' + urlParsingNode.pathname
16243 };
16244}
16245
16246/**
16247 * Parse a request URL and determine whether this is a same-origin request as the application document.
16248 *
16249 * @param {string|object} requestUrl The url of the request as a string that will be resolved
16250 * or a parsed URL object.
16251 * @returns {boolean} Whether the request is for the same origin as the application document.
16252 */
16253function urlIsSameOrigin(requestUrl) {
16254 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
16255 return (parsed.protocol === originUrl.protocol &&
16256 parsed.host === originUrl.host);
16257}
16258
16259/**
16260 * @ngdoc service
16261 * @name $window
16262 *
16263 * @description
16264 * A reference to the browser's `window` object. While `window`
16265 * is globally available in JavaScript, it causes testability problems, because
16266 * it is a global variable. In angular we always refer to it through the
16267 * `$window` service, so it may be overridden, removed or mocked for testing.
16268 *
16269 * Expressions, like the one defined for the `ngClick` directive in the example
16270 * below, are evaluated with respect to the current scope. Therefore, there is
16271 * no risk of inadvertently coding in a dependency on a global value in such an
16272 * expression.
16273 *
16274 * @example
16275 <example module="windowExample">
16276 <file name="index.html">
16277 <script>
16278 angular.module('windowExample', [])
16279 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
16280 $scope.greeting = 'Hello, World!';
16281 $scope.doGreeting = function(greeting) {
16282 $window.alert(greeting);
16283 };
16284 }]);
16285 </script>
16286 <div ng-controller="ExampleController">
16287 <input type="text" ng-model="greeting" />
16288 <button ng-click="doGreeting(greeting)">ALERT</button>
16289 </div>
16290 </file>
16291 <file name="protractor.js" type="protractor">
16292 it('should display the greeting in the input box', function() {
16293 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
16294 // If we click the button it will block the test runner
16295 // element(':button').click();
16296 });
16297 </file>
16298 </example>
16299 */
16300function $WindowProvider() {
16301 this.$get = valueFn(window);
16302}
16303
16304/* global currencyFilter: true,
16305 dateFilter: true,
16306 filterFilter: true,
16307 jsonFilter: true,
16308 limitToFilter: true,
16309 lowercaseFilter: true,
16310 numberFilter: true,
16311 orderByFilter: true,
16312 uppercaseFilter: true,
16313 */
16314
16315/**
16316 * @ngdoc provider
16317 * @name $filterProvider
16318 * @description
16319 *
16320 * Filters are just functions which transform input to an output. However filters need to be
16321 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
16322 * annotated with dependencies and is responsible for creating a filter function.
16323 *
16324 * ```js
16325 * // Filter registration
16326 * function MyModule($provide, $filterProvider) {
16327 * // create a service to demonstrate injection (not always needed)
16328 * $provide.value('greet', function(name){
16329 * return 'Hello ' + name + '!';
16330 * });
16331 *
16332 * // register a filter factory which uses the
16333 * // greet service to demonstrate DI.
16334 * $filterProvider.register('greet', function(greet){
16335 * // return the filter function which uses the greet service
16336 * // to generate salutation
16337 * return function(text) {
16338 * // filters need to be forgiving so check input validity
16339 * return text && greet(text) || text;
16340 * };
16341 * });
16342 * }
16343 * ```
16344 *
16345 * The filter function is registered with the `$injector` under the filter name suffix with
16346 * `Filter`.
16347 *
16348 * ```js
16349 * it('should be the same instance', inject(
16350 * function($filterProvider) {
16351 * $filterProvider.register('reverse', function(){
16352 * return ...;
16353 * });
16354 * },
16355 * function($filter, reverseFilter) {
16356 * expect($filter('reverse')).toBe(reverseFilter);
16357 * });
16358 * ```
16359 *
16360 *
16361 * For more information about how angular filters work, and how to create your own filters, see
16362 * {@link guide/filter Filters} in the Angular Developer Guide.
16363 */
16364
16365/**
16366 * @ngdoc service
16367 * @name $filter
16368 * @kind function
16369 * @description
16370 * Filters are used for formatting data displayed to the user.
16371 *
16372 * The general syntax in templates is as follows:
16373 *
16374 * {{ expression [| filter_name[:parameter_value] ... ] }}
16375 *
16376 * @param {String} name Name of the filter function to retrieve
16377 * @return {Function} the filter function
16378 * @example
16379 <example name="$filter" module="filterExample">
16380 <file name="index.html">
16381 <div ng-controller="MainCtrl">
16382 <h3>{{ originalText }}</h3>
16383 <h3>{{ filteredText }}</h3>
16384 </div>
16385 </file>
16386
16387 <file name="script.js">
16388 angular.module('filterExample', [])
16389 .controller('MainCtrl', function($scope, $filter) {
16390 $scope.originalText = 'hello';
16391 $scope.filteredText = $filter('uppercase')($scope.originalText);
16392 });
16393 </file>
16394 </example>
16395 */
16396$FilterProvider.$inject = ['$provide'];
16397function $FilterProvider($provide) {
16398 var suffix = 'Filter';
16399
16400 /**
16401 * @ngdoc method
16402 * @name $filterProvider#register
16403 * @param {string|Object} name Name of the filter function, or an object map of filters where
16404 * the keys are the filter names and the values are the filter factories.
16405 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
16406 * of the registered filter instances.
16407 */
16408 function register(name, factory) {
16409 if (isObject(name)) {
16410 var filters = {};
16411 forEach(name, function(filter, key) {
16412 filters[key] = register(key, filter);
16413 });
16414 return filters;
16415 } else {
16416 return $provide.factory(name + suffix, factory);
16417 }
16418 }
16419 this.register = register;
16420
16421 this.$get = ['$injector', function($injector) {
16422 return function(name) {
16423 return $injector.get(name + suffix);
16424 };
16425 }];
16426
16427 ////////////////////////////////////////
16428
16429 /* global
16430 currencyFilter: false,
16431 dateFilter: false,
16432 filterFilter: false,
16433 jsonFilter: false,
16434 limitToFilter: false,
16435 lowercaseFilter: false,
16436 numberFilter: false,
16437 orderByFilter: false,
16438 uppercaseFilter: false,
16439 */
16440
16441 register('currency', currencyFilter);
16442 register('date', dateFilter);
16443 register('filter', filterFilter);
16444 register('json', jsonFilter);
16445 register('limitTo', limitToFilter);
16446 register('lowercase', lowercaseFilter);
16447 register('number', numberFilter);
16448 register('orderBy', orderByFilter);
16449 register('uppercase', uppercaseFilter);
16450}
16451
16452/**
16453 * @ngdoc filter
16454 * @name filter
16455 * @kind function
16456 *
16457 * @description
16458 * Selects a subset of items from `array` and returns it as a new array.
16459 *
16460 * @param {Array} array The source array.
16461 * @param {string|Object|function()} expression The predicate to be used for selecting items from
16462 * `array`.
16463 *
16464 * Can be one of:
16465 *
16466 * - `string`: The string is evaluated as an expression and the resulting value is used for substring match against
16467 * the contents of the `array`. All strings or objects with string properties in `array` that contain this string
16468 * will be returned. The predicate can be negated by prefixing the string with `!`.
16469 *
16470 * - `Object`: A pattern object can be used to filter specific properties on objects contained
16471 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
16472 * which have property `name` containing "M" and property `phone` containing "1". A special
16473 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
16474 * property of the object. That's equivalent to the simple substring match with a `string`
16475 * as described above. The predicate can be negated by prefixing the string with `!`.
16476 * For Example `{name: "!M"}` predicate will return an array of items which have property `name`
16477 * not containing "M".
16478 *
16479 * - `function(value, index)`: A predicate function can be used to write arbitrary filters. The
16480 * function is called for each element of `array`. The final result is an array of those
16481 * elements that the predicate returned true for.
16482 *
16483 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
16484 * determining if the expected value (from the filter expression) and actual value (from
16485 * the object in the array) should be considered a match.
16486 *
16487 * Can be one of:
16488 *
16489 * - `function(actual, expected)`:
16490 * The function will be given the object value and the predicate value to compare and
16491 * should return true if the item should be included in filtered result.
16492 *
16493 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(expected, actual)}`.
16494 * this is essentially strict comparison of expected and actual.
16495 *
16496 * - `false|undefined`: A short hand for a function which will look for a substring match in case
16497 * insensitive way.
16498 *
16499 * @example
16500 <example>
16501 <file name="index.html">
16502 <div ng-init="friends = [{name:'John', phone:'555-1276'},
16503 {name:'Mary', phone:'800-BIG-MARY'},
16504 {name:'Mike', phone:'555-4321'},
16505 {name:'Adam', phone:'555-5678'},
16506 {name:'Julie', phone:'555-8765'},
16507 {name:'Juliette', phone:'555-5678'}]"></div>
16508
16509 Search: <input ng-model="searchText">
16510 <table id="searchTextResults">
16511 <tr><th>Name</th><th>Phone</th></tr>
16512 <tr ng-repeat="friend in friends | filter:searchText">
16513 <td>{{friend.name}}</td>
16514 <td>{{friend.phone}}</td>
16515 </tr>
16516 </table>
16517 <hr>
16518 Any: <input ng-model="search.$"> <br>
16519 Name only <input ng-model="search.name"><br>
16520 Phone only <input ng-model="search.phone"><br>
16521 Equality <input type="checkbox" ng-model="strict"><br>
16522 <table id="searchObjResults">
16523 <tr><th>Name</th><th>Phone</th></tr>
16524 <tr ng-repeat="friendObj in friends | filter:search:strict">
16525 <td>{{friendObj.name}}</td>
16526 <td>{{friendObj.phone}}</td>
16527 </tr>
16528 </table>
16529 </file>
16530 <file name="protractor.js" type="protractor">
16531 var expectFriendNames = function(expectedNames, key) {
16532 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
16533 arr.forEach(function(wd, i) {
16534 expect(wd.getText()).toMatch(expectedNames[i]);
16535 });
16536 });
16537 };
16538
16539 it('should search across all fields when filtering with a string', function() {
16540 var searchText = element(by.model('searchText'));
16541 searchText.clear();
16542 searchText.sendKeys('m');
16543 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
16544
16545 searchText.clear();
16546 searchText.sendKeys('76');
16547 expectFriendNames(['John', 'Julie'], 'friend');
16548 });
16549
16550 it('should search in specific fields when filtering with a predicate object', function() {
16551 var searchAny = element(by.model('search.$'));
16552 searchAny.clear();
16553 searchAny.sendKeys('i');
16554 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
16555 });
16556 it('should use a equal comparison when comparator is true', function() {
16557 var searchName = element(by.model('search.name'));
16558 var strict = element(by.model('strict'));
16559 searchName.clear();
16560 searchName.sendKeys('Julie');
16561 strict.click();
16562 expectFriendNames(['Julie'], 'friendObj');
16563 });
16564 </file>
16565 </example>
16566 */
16567function filterFilter() {
16568 return function(array, expression, comparator) {
16569 if (!isArray(array)) return array;
16570
16571 var comparatorType = typeof(comparator),
16572 predicates = [];
16573
16574 predicates.check = function(value, index) {
16575 for (var j = 0; j < predicates.length; j++) {
16576 if (!predicates[j](value, index)) {
16577 return false;
16578 }
16579 }
16580 return true;
16581 };
16582
16583 if (comparatorType !== 'function') {
16584 if (comparatorType === 'boolean' && comparator) {
16585 comparator = function(obj, text) {
16586 return angular.equals(obj, text);
16587 };
16588 } else {
16589 comparator = function(obj, text) {
16590 if (obj && text && typeof obj === 'object' && typeof text === 'object') {
16591 for (var objKey in obj) {
16592 if (objKey.charAt(0) !== '$' && hasOwnProperty.call(obj, objKey) &&
16593 comparator(obj[objKey], text[objKey])) {
16594 return true;
16595 }
16596 }
16597 return false;
16598 }
16599 text = ('' + text).toLowerCase();
16600 return ('' + obj).toLowerCase().indexOf(text) > -1;
16601 };
16602 }
16603 }
16604
16605 var search = function(obj, text) {
16606 if (typeof text === 'string' && text.charAt(0) === '!') {
16607 return !search(obj, text.substr(1));
16608 }
16609 switch (typeof obj) {
16610 case 'boolean':
16611 case 'number':
16612 case 'string':
16613 return comparator(obj, text);
16614 case 'object':
16615 switch (typeof text) {
16616 case 'object':
16617 return comparator(obj, text);
16618 default:
16619 for (var objKey in obj) {
16620 if (objKey.charAt(0) !== '$' && search(obj[objKey], text)) {
16621 return true;
16622 }
16623 }
16624 break;
16625 }
16626 return false;
16627 case 'array':
16628 for (var i = 0; i < obj.length; i++) {
16629 if (search(obj[i], text)) {
16630 return true;
16631 }
16632 }
16633 return false;
16634 default:
16635 return false;
16636 }
16637 };
16638 switch (typeof expression) {
16639 case 'boolean':
16640 case 'number':
16641 case 'string':
16642 // Set up expression object and fall through
16643 expression = {$:expression};
16644 // jshint -W086
16645 case 'object':
16646 // jshint +W086
16647 for (var key in expression) {
16648 (function(path) {
16649 if (typeof expression[path] === 'undefined') return;
16650 predicates.push(function(value) {
16651 return search(path == '$' ? value : (value && value[path]), expression[path]);
16652 });
16653 })(key);
16654 }
16655 break;
16656 case 'function':
16657 predicates.push(expression);
16658 break;
16659 default:
16660 return array;
16661 }
16662 var filtered = [];
16663 for (var j = 0; j < array.length; j++) {
16664 var value = array[j];
16665 if (predicates.check(value, j)) {
16666 filtered.push(value);
16667 }
16668 }
16669 return filtered;
16670 };
16671}
16672
16673/**
16674 * @ngdoc filter
16675 * @name currency
16676 * @kind function
16677 *
16678 * @description
16679 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
16680 * symbol for current locale is used.
16681 *
16682 * @param {number} amount Input to filter.
16683 * @param {string=} symbol Currency symbol or identifier to be displayed.
16684 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
16685 * @returns {string} Formatted number.
16686 *
16687 *
16688 * @example
16689 <example module="currencyExample">
16690 <file name="index.html">
16691 <script>
16692 angular.module('currencyExample', [])
16693 .controller('ExampleController', ['$scope', function($scope) {
16694 $scope.amount = 1234.56;
16695 }]);
16696 </script>
16697 <div ng-controller="ExampleController">
16698 <input type="number" ng-model="amount"> <br>
16699 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
16700 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
16701 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
16702 </div>
16703 </file>
16704 <file name="protractor.js" type="protractor">
16705 it('should init with 1234.56', function() {
16706 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
16707 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
16708 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
16709 });
16710 it('should update', function() {
16711 if (browser.params.browser == 'safari') {
16712 // Safari does not understand the minus key. See
16713 // https://github.com/angular/protractor/issues/481
16714 return;
16715 }
16716 element(by.model('amount')).clear();
16717 element(by.model('amount')).sendKeys('-1234');
16718 expect(element(by.id('currency-default')).getText()).toBe('($1,234.00)');
16719 expect(element(by.id('currency-custom')).getText()).toBe('(USD$1,234.00)');
16720 expect(element(by.id('currency-no-fractions')).getText()).toBe('(USD$1,234)');
16721 });
16722 </file>
16723 </example>
16724 */
16725currencyFilter.$inject = ['$locale'];
16726function currencyFilter($locale) {
16727 var formats = $locale.NUMBER_FORMATS;
16728 return function(amount, currencySymbol, fractionSize) {
16729 if (isUndefined(currencySymbol)) {
16730 currencySymbol = formats.CURRENCY_SYM;
16731 }
16732
16733 if (isUndefined(fractionSize)) {
16734 fractionSize = formats.PATTERNS[1].maxFrac;
16735 }
16736
16737 // if null or undefined pass it through
16738 return (amount == null)
16739 ? amount
16740 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
16741 replace(/\u00A4/g, currencySymbol);
16742 };
16743}
16744
16745/**
16746 * @ngdoc filter
16747 * @name number
16748 * @kind function
16749 *
16750 * @description
16751 * Formats a number as text.
16752 *
16753 * If the input is not a number an empty string is returned.
16754 *
16755 * @param {number|string} number Number to format.
16756 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
16757 * If this is not provided then the fraction size is computed from the current locale's number
16758 * formatting pattern. In the case of the default locale, it will be 3.
16759 * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
16760 *
16761 * @example
16762 <example module="numberFilterExample">
16763 <file name="index.html">
16764 <script>
16765 angular.module('numberFilterExample', [])
16766 .controller('ExampleController', ['$scope', function($scope) {
16767 $scope.val = 1234.56789;
16768 }]);
16769 </script>
16770 <div ng-controller="ExampleController">
16771 Enter number: <input ng-model='val'><br>
16772 Default formatting: <span id='number-default'>{{val | number}}</span><br>
16773 No fractions: <span>{{val | number:0}}</span><br>
16774 Negative number: <span>{{-val | number:4}}</span>
16775 </div>
16776 </file>
16777 <file name="protractor.js" type="protractor">
16778 it('should format numbers', function() {
16779 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
16780 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
16781 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
16782 });
16783
16784 it('should update', function() {
16785 element(by.model('val')).clear();
16786 element(by.model('val')).sendKeys('3374.333');
16787 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
16788 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
16789 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
16790 });
16791 </file>
16792 </example>
16793 */
16794
16795
16796numberFilter.$inject = ['$locale'];
16797function numberFilter($locale) {
16798 var formats = $locale.NUMBER_FORMATS;
16799 return function(number, fractionSize) {
16800
16801 // if null or undefined pass it through
16802 return (number == null)
16803 ? number
16804 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
16805 fractionSize);
16806 };
16807}
16808
16809var DECIMAL_SEP = '.';
16810function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
16811 if (!isFinite(number) || isObject(number)) return '';
16812
16813 var isNegative = number < 0;
16814 number = Math.abs(number);
16815 var numStr = number + '',
16816 formatedText = '',
16817 parts = [];
16818
16819 var hasExponent = false;
16820 if (numStr.indexOf('e') !== -1) {
16821 var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
16822 if (match && match[2] == '-' && match[3] > fractionSize + 1) {
16823 numStr = '0';
16824 number = 0;
16825 } else {
16826 formatedText = numStr;
16827 hasExponent = true;
16828 }
16829 }
16830
16831 if (!hasExponent) {
16832 var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
16833
16834 // determine fractionSize if it is not specified
16835 if (isUndefined(fractionSize)) {
16836 fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
16837 }
16838
16839 // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
16840 // inspired by:
16841 // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
16842 number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
16843
16844 if (number === 0) {
16845 isNegative = false;
16846 }
16847
16848 var fraction = ('' + number).split(DECIMAL_SEP);
16849 var whole = fraction[0];
16850 fraction = fraction[1] || '';
16851
16852 var i, pos = 0,
16853 lgroup = pattern.lgSize,
16854 group = pattern.gSize;
16855
16856 if (whole.length >= (lgroup + group)) {
16857 pos = whole.length - lgroup;
16858 for (i = 0; i < pos; i++) {
16859 if ((pos - i) % group === 0 && i !== 0) {
16860 formatedText += groupSep;
16861 }
16862 formatedText += whole.charAt(i);
16863 }
16864 }
16865
16866 for (i = pos; i < whole.length; i++) {
16867 if ((whole.length - i) % lgroup === 0 && i !== 0) {
16868 formatedText += groupSep;
16869 }
16870 formatedText += whole.charAt(i);
16871 }
16872
16873 // format fraction part.
16874 while (fraction.length < fractionSize) {
16875 fraction += '0';
16876 }
16877
16878 if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
16879 } else {
16880
16881 if (fractionSize > 0 && number > -1 && number < 1) {
16882 formatedText = number.toFixed(fractionSize);
16883 }
16884 }
16885
16886 parts.push(isNegative ? pattern.negPre : pattern.posPre,
16887 formatedText,
16888 isNegative ? pattern.negSuf : pattern.posSuf);
16889 return parts.join('');
16890}
16891
16892function padNumber(num, digits, trim) {
16893 var neg = '';
16894 if (num < 0) {
16895 neg = '-';
16896 num = -num;
16897 }
16898 num = '' + num;
16899 while (num.length < digits) num = '0' + num;
16900 if (trim)
16901 num = num.substr(num.length - digits);
16902 return neg + num;
16903}
16904
16905
16906function dateGetter(name, size, offset, trim) {
16907 offset = offset || 0;
16908 return function(date) {
16909 var value = date['get' + name]();
16910 if (offset > 0 || value > -offset)
16911 value += offset;
16912 if (value === 0 && offset == -12) value = 12;
16913 return padNumber(value, size, trim);
16914 };
16915}
16916
16917function dateStrGetter(name, shortForm) {
16918 return function(date, formats) {
16919 var value = date['get' + name]();
16920 var get = uppercase(shortForm ? ('SHORT' + name) : name);
16921
16922 return formats[get][value];
16923 };
16924}
16925
16926function timeZoneGetter(date) {
16927 var zone = -1 * date.getTimezoneOffset();
16928 var paddedZone = (zone >= 0) ? "+" : "";
16929
16930 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
16931 padNumber(Math.abs(zone % 60), 2);
16932
16933 return paddedZone;
16934}
16935
16936function getFirstThursdayOfYear(year) {
16937 // 0 = index of January
16938 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
16939 // 4 = index of Thursday (+1 to account for 1st = 5)
16940 // 11 = index of *next* Thursday (+1 account for 1st = 12)
16941 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
16942}
16943
16944function getThursdayThisWeek(datetime) {
16945 return new Date(datetime.getFullYear(), datetime.getMonth(),
16946 // 4 = index of Thursday
16947 datetime.getDate() + (4 - datetime.getDay()));
16948}
16949
16950function weekGetter(size) {
16951 return function(date) {
16952 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
16953 thisThurs = getThursdayThisWeek(date);
16954
16955 var diff = +thisThurs - +firstThurs,
16956 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
16957
16958 return padNumber(result, size);
16959 };
16960}
16961
16962function ampmGetter(date, formats) {
16963 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
16964}
16965
16966var DATE_FORMATS = {
16967 yyyy: dateGetter('FullYear', 4),
16968 yy: dateGetter('FullYear', 2, 0, true),
16969 y: dateGetter('FullYear', 1),
16970 MMMM: dateStrGetter('Month'),
16971 MMM: dateStrGetter('Month', true),
16972 MM: dateGetter('Month', 2, 1),
16973 M: dateGetter('Month', 1, 1),
16974 dd: dateGetter('Date', 2),
16975 d: dateGetter('Date', 1),
16976 HH: dateGetter('Hours', 2),
16977 H: dateGetter('Hours', 1),
16978 hh: dateGetter('Hours', 2, -12),
16979 h: dateGetter('Hours', 1, -12),
16980 mm: dateGetter('Minutes', 2),
16981 m: dateGetter('Minutes', 1),
16982 ss: dateGetter('Seconds', 2),
16983 s: dateGetter('Seconds', 1),
16984 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
16985 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
16986 sss: dateGetter('Milliseconds', 3),
16987 EEEE: dateStrGetter('Day'),
16988 EEE: dateStrGetter('Day', true),
16989 a: ampmGetter,
16990 Z: timeZoneGetter,
16991 ww: weekGetter(2),
16992 w: weekGetter(1)
16993};
16994
16995var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEw']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|w+))(.*)/,
16996 NUMBER_STRING = /^\-?\d+$/;
16997
16998/**
16999 * @ngdoc filter
17000 * @name date
17001 * @kind function
17002 *
17003 * @description
17004 * Formats `date` to a string based on the requested `format`.
17005 *
17006 * `format` string can be composed of the following elements:
17007 *
17008 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
17009 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
17010 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
17011 * * `'MMMM'`: Month in year (January-December)
17012 * * `'MMM'`: Month in year (Jan-Dec)
17013 * * `'MM'`: Month in year, padded (01-12)
17014 * * `'M'`: Month in year (1-12)
17015 * * `'dd'`: Day in month, padded (01-31)
17016 * * `'d'`: Day in month (1-31)
17017 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
17018 * * `'EEE'`: Day in Week, (Sun-Sat)
17019 * * `'HH'`: Hour in day, padded (00-23)
17020 * * `'H'`: Hour in day (0-23)
17021 * * `'hh'`: Hour in AM/PM, padded (01-12)
17022 * * `'h'`: Hour in AM/PM, (1-12)
17023 * * `'mm'`: Minute in hour, padded (00-59)
17024 * * `'m'`: Minute in hour (0-59)
17025 * * `'ss'`: Second in minute, padded (00-59)
17026 * * `'s'`: Second in minute (0-59)
17027 * * `'.sss' or ',sss'`: Millisecond in second, padded (000-999)
17028 * * `'a'`: AM/PM marker
17029 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
17030 * * `'ww'`: ISO-8601 week of year (00-53)
17031 * * `'w'`: ISO-8601 week of year (0-53)
17032 *
17033 * `format` string can also be one of the following predefined
17034 * {@link guide/i18n localizable formats}:
17035 *
17036 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
17037 * (e.g. Sep 3, 2010 12:05:08 PM)
17038 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
17039 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
17040 * (e.g. Friday, September 3, 2010)
17041 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
17042 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
17043 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
17044 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
17045 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
17046 *
17047 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
17048 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
17049 * (e.g. `"h 'o''clock'"`).
17050 *
17051 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
17052 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
17053 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
17054 * specified in the string input, the time is considered to be in the local timezone.
17055 * @param {string=} format Formatting rules (see Description). If not specified,
17056 * `mediumDate` is used.
17057 * @param {string=} timezone Timezone to be used for formatting. Right now, only `'UTC'` is supported.
17058 * If not specified, the timezone of the browser will be used.
17059 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
17060 *
17061 * @example
17062 <example>
17063 <file name="index.html">
17064 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
17065 <span>{{1288323623006 | date:'medium'}}</span><br>
17066 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
17067 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
17068 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
17069 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
17070 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
17071 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
17072 </file>
17073 <file name="protractor.js" type="protractor">
17074 it('should format date', function() {
17075 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
17076 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
17077 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
17078 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
17079 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
17080 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
17081 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
17082 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
17083 });
17084 </file>
17085 </example>
17086 */
17087dateFilter.$inject = ['$locale'];
17088function dateFilter($locale) {
17089
17090
17091 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
17092 // 1 2 3 4 5 6 7 8 9 10 11
17093 function jsonStringToDate(string) {
17094 var match;
17095 if (match = string.match(R_ISO8601_STR)) {
17096 var date = new Date(0),
17097 tzHour = 0,
17098 tzMin = 0,
17099 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
17100 timeSetter = match[8] ? date.setUTCHours : date.setHours;
17101
17102 if (match[9]) {
17103 tzHour = int(match[9] + match[10]);
17104 tzMin = int(match[9] + match[11]);
17105 }
17106 dateSetter.call(date, int(match[1]), int(match[2]) - 1, int(match[3]));
17107 var h = int(match[4] || 0) - tzHour;
17108 var m = int(match[5] || 0) - tzMin;
17109 var s = int(match[6] || 0);
17110 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
17111 timeSetter.call(date, h, m, s, ms);
17112 return date;
17113 }
17114 return string;
17115 }
17116
17117
17118 return function(date, format, timezone) {
17119 var text = '',
17120 parts = [],
17121 fn, match;
17122
17123 format = format || 'mediumDate';
17124 format = $locale.DATETIME_FORMATS[format] || format;
17125 if (isString(date)) {
17126 date = NUMBER_STRING.test(date) ? int(date) : jsonStringToDate(date);
17127 }
17128
17129 if (isNumber(date)) {
17130 date = new Date(date);
17131 }
17132
17133 if (!isDate(date)) {
17134 return date;
17135 }
17136
17137 while (format) {
17138 match = DATE_FORMATS_SPLIT.exec(format);
17139 if (match) {
17140 parts = concat(parts, match, 1);
17141 format = parts.pop();
17142 } else {
17143 parts.push(format);
17144 format = null;
17145 }
17146 }
17147
17148 if (timezone && timezone === 'UTC') {
17149 date = new Date(date.getTime());
17150 date.setMinutes(date.getMinutes() + date.getTimezoneOffset());
17151 }
17152 forEach(parts, function(value) {
17153 fn = DATE_FORMATS[value];
17154 text += fn ? fn(date, $locale.DATETIME_FORMATS)
17155 : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
17156 });
17157
17158 return text;
17159 };
17160}
17161
17162
17163/**
17164 * @ngdoc filter
17165 * @name json
17166 * @kind function
17167 *
17168 * @description
17169 * Allows you to convert a JavaScript object into JSON string.
17170 *
17171 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
17172 * the binding is automatically converted to JSON.
17173 *
17174 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
17175 * @returns {string} JSON string.
17176 *
17177 *
17178 * @example
17179 <example>
17180 <file name="index.html">
17181 <pre>{{ {'name':'value'} | json }}</pre>
17182 </file>
17183 <file name="protractor.js" type="protractor">
17184 it('should jsonify filtered objects', function() {
17185 expect(element(by.binding("{'name':'value'}")).getText()).toMatch(/\{\n "name": ?"value"\n}/);
17186 });
17187 </file>
17188 </example>
17189 *
17190 */
17191function jsonFilter() {
17192 return function(object) {
17193 return toJson(object, true);
17194 };
17195}
17196
17197
17198/**
17199 * @ngdoc filter
17200 * @name lowercase
17201 * @kind function
17202 * @description
17203 * Converts string to lowercase.
17204 * @see angular.lowercase
17205 */
17206var lowercaseFilter = valueFn(lowercase);
17207
17208
17209/**
17210 * @ngdoc filter
17211 * @name uppercase
17212 * @kind function
17213 * @description
17214 * Converts string to uppercase.
17215 * @see angular.uppercase
17216 */
17217var uppercaseFilter = valueFn(uppercase);
17218
17219/**
17220 * @ngdoc filter
17221 * @name limitTo
17222 * @kind function
17223 *
17224 * @description
17225 * Creates a new array or string containing only a specified number of elements. The elements
17226 * are taken from either the beginning or the end of the source array, string or number, as specified by
17227 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
17228 * converted to a string.
17229 *
17230 * @param {Array|string|number} input Source array, string or number to be limited.
17231 * @param {string|number} limit The length of the returned array or string. If the `limit` number
17232 * is positive, `limit` number of items from the beginning of the source array/string are copied.
17233 * If the number is negative, `limit` number of items from the end of the source array/string
17234 * are copied. The `limit` will be trimmed if it exceeds `array.length`
17235 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
17236 * had less than `limit` elements.
17237 *
17238 * @example
17239 <example module="limitToExample">
17240 <file name="index.html">
17241 <script>
17242 angular.module('limitToExample', [])
17243 .controller('ExampleController', ['$scope', function($scope) {
17244 $scope.numbers = [1,2,3,4,5,6,7,8,9];
17245 $scope.letters = "abcdefghi";
17246 $scope.longNumber = 2345432342;
17247 $scope.numLimit = 3;
17248 $scope.letterLimit = 3;
17249 $scope.longNumberLimit = 3;
17250 }]);
17251 </script>
17252 <div ng-controller="ExampleController">
17253 Limit {{numbers}} to: <input type="number" step="1" ng-model="numLimit">
17254 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
17255 Limit {{letters}} to: <input type="number" step="1" ng-model="letterLimit">
17256 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
17257 Limit {{longNumber}} to: <input type="number" step="1" ng-model="longNumberLimit">
17258 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
17259 </div>
17260 </file>
17261 <file name="protractor.js" type="protractor">
17262 var numLimitInput = element(by.model('numLimit'));
17263 var letterLimitInput = element(by.model('letterLimit'));
17264 var longNumberLimitInput = element(by.model('longNumberLimit'));
17265 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
17266 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
17267 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
17268
17269 it('should limit the number array to first three items', function() {
17270 expect(numLimitInput.getAttribute('value')).toBe('3');
17271 expect(letterLimitInput.getAttribute('value')).toBe('3');
17272 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
17273 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
17274 expect(limitedLetters.getText()).toEqual('Output letters: abc');
17275 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
17276 });
17277
17278 // There is a bug in safari and protractor that doesn't like the minus key
17279 // it('should update the output when -3 is entered', function() {
17280 // numLimitInput.clear();
17281 // numLimitInput.sendKeys('-3');
17282 // letterLimitInput.clear();
17283 // letterLimitInput.sendKeys('-3');
17284 // longNumberLimitInput.clear();
17285 // longNumberLimitInput.sendKeys('-3');
17286 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
17287 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
17288 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
17289 // });
17290
17291 it('should not exceed the maximum size of input array', function() {
17292 numLimitInput.clear();
17293 numLimitInput.sendKeys('100');
17294 letterLimitInput.clear();
17295 letterLimitInput.sendKeys('100');
17296 longNumberLimitInput.clear();
17297 longNumberLimitInput.sendKeys('100');
17298 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
17299 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
17300 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
17301 });
17302 </file>
17303 </example>
17304*/
17305function limitToFilter() {
17306 return function(input, limit) {
17307 if (isNumber(input)) input = input.toString();
17308 if (!isArray(input) && !isString(input)) return input;
17309
17310 if (Math.abs(Number(limit)) === Infinity) {
17311 limit = Number(limit);
17312 } else {
17313 limit = int(limit);
17314 }
17315
17316 if (isString(input)) {
17317 //NaN check on limit
17318 if (limit) {
17319 return limit >= 0 ? input.slice(0, limit) : input.slice(limit, input.length);
17320 } else {
17321 return "";
17322 }
17323 }
17324
17325 var out = [],
17326 i, n;
17327
17328 // if abs(limit) exceeds maximum length, trim it
17329 if (limit > input.length)
17330 limit = input.length;
17331 else if (limit < -input.length)
17332 limit = -input.length;
17333
17334 if (limit > 0) {
17335 i = 0;
17336 n = limit;
17337 } else {
17338 i = input.length + limit;
17339 n = input.length;
17340 }
17341
17342 for (; i < n; i++) {
17343 out.push(input[i]);
17344 }
17345
17346 return out;
17347 };
17348}
17349
17350/**
17351 * @ngdoc filter
17352 * @name orderBy
17353 * @kind function
17354 *
17355 * @description
17356 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
17357 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
17358 * correctly, make sure they are actually being saved as numbers and not strings.
17359 *
17360 * @param {Array} array The array to sort.
17361 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
17362 * used by the comparator to determine the order of elements.
17363 *
17364 * Can be one of:
17365 *
17366 * - `function`: Getter function. The result of this function will be sorted using the
17367 * `<`, `=`, `>` operator.
17368 * - `string`: An Angular expression. The result of this expression is used to compare elements
17369 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
17370 * 3 first characters of a property called `name`). The result of a constant expression
17371 * is interpreted as a property name to be used in comparisons (for example `"special name"`
17372 * to sort object by the value of their `special name` property). An expression can be
17373 * optionally prefixed with `+` or `-` to control ascending or descending sort order
17374 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
17375 * element itself is used to compare where sorting.
17376 * - `Array`: An array of function or string predicates. The first predicate in the array
17377 * is used for sorting, but when two items are equivalent, the next predicate is used.
17378 *
17379 * If the predicate is missing or empty then it defaults to `'+'`.
17380 *
17381 * @param {boolean=} reverse Reverse the order of the array.
17382 * @returns {Array} Sorted copy of the source array.
17383 *
17384 * @example
17385 <example module="orderByExample">
17386 <file name="index.html">
17387 <script>
17388 angular.module('orderByExample', [])
17389 .controller('ExampleController', ['$scope', function($scope) {
17390 $scope.friends =
17391 [{name:'John', phone:'555-1212', age:10},
17392 {name:'Mary', phone:'555-9876', age:19},
17393 {name:'Mike', phone:'555-4321', age:21},
17394 {name:'Adam', phone:'555-5678', age:35},
17395 {name:'Julie', phone:'555-8765', age:29}];
17396 $scope.predicate = '-age';
17397 }]);
17398 </script>
17399 <div ng-controller="ExampleController">
17400 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
17401 <hr/>
17402 [ <a href="" ng-click="predicate=''">unsorted</a> ]
17403 <table class="friend">
17404 <tr>
17405 <th><a href="" ng-click="predicate = 'name'; reverse=false">Name</a>
17406 (<a href="" ng-click="predicate = '-name'; reverse=false">^</a>)</th>
17407 <th><a href="" ng-click="predicate = 'phone'; reverse=!reverse">Phone Number</a></th>
17408 <th><a href="" ng-click="predicate = 'age'; reverse=!reverse">Age</a></th>
17409 </tr>
17410 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
17411 <td>{{friend.name}}</td>
17412 <td>{{friend.phone}}</td>
17413 <td>{{friend.age}}</td>
17414 </tr>
17415 </table>
17416 </div>
17417 </file>
17418 </example>
17419 *
17420 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
17421 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
17422 * desired parameters.
17423 *
17424 * Example:
17425 *
17426 * @example
17427 <example module="orderByExample">
17428 <file name="index.html">
17429 <div ng-controller="ExampleController">
17430 <table class="friend">
17431 <tr>
17432 <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
17433 (<a href="" ng-click="order('-name',false)">^</a>)</th>
17434 <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
17435 <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
17436 </tr>
17437 <tr ng-repeat="friend in friends">
17438 <td>{{friend.name}}</td>
17439 <td>{{friend.phone}}</td>
17440 <td>{{friend.age}}</td>
17441 </tr>
17442 </table>
17443 </div>
17444 </file>
17445
17446 <file name="script.js">
17447 angular.module('orderByExample', [])
17448 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
17449 var orderBy = $filter('orderBy');
17450 $scope.friends = [
17451 { name: 'John', phone: '555-1212', age: 10 },
17452 { name: 'Mary', phone: '555-9876', age: 19 },
17453 { name: 'Mike', phone: '555-4321', age: 21 },
17454 { name: 'Adam', phone: '555-5678', age: 35 },
17455 { name: 'Julie', phone: '555-8765', age: 29 }
17456 ];
17457 $scope.order = function(predicate, reverse) {
17458 $scope.friends = orderBy($scope.friends, predicate, reverse);
17459 };
17460 $scope.order('-age',false);
17461 }]);
17462 </file>
17463</example>
17464 */
17465orderByFilter.$inject = ['$parse'];
17466function orderByFilter($parse) {
17467 return function(array, sortPredicate, reverseOrder) {
17468 if (!(isArrayLike(array))) return array;
17469 sortPredicate = isArray(sortPredicate) ? sortPredicate : [sortPredicate];
17470 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
17471 sortPredicate = sortPredicate.map(function(predicate) {
17472 var descending = false, get = predicate || identity;
17473 if (isString(predicate)) {
17474 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
17475 descending = predicate.charAt(0) == '-';
17476 predicate = predicate.substring(1);
17477 }
17478 if (predicate === '') {
17479 // Effectively no predicate was passed so we compare identity
17480 return reverseComparator(function(a, b) {
17481 return compare(a, b);
17482 }, descending);
17483 }
17484 get = $parse(predicate);
17485 if (get.constant) {
17486 var key = get();
17487 return reverseComparator(function(a, b) {
17488 return compare(a[key], b[key]);
17489 }, descending);
17490 }
17491 }
17492 return reverseComparator(function(a, b) {
17493 return compare(get(a),get(b));
17494 }, descending);
17495 });
17496 return slice.call(array).sort(reverseComparator(comparator, reverseOrder));
17497
17498 function comparator(o1, o2) {
17499 for (var i = 0; i < sortPredicate.length; i++) {
17500 var comp = sortPredicate[i](o1, o2);
17501 if (comp !== 0) return comp;
17502 }
17503 return 0;
17504 }
17505 function reverseComparator(comp, descending) {
17506 return descending
17507 ? function(a, b) {return comp(b,a);}
17508 : comp;
17509 }
17510 function compare(v1, v2) {
17511 var t1 = typeof v1;
17512 var t2 = typeof v2;
17513 if (t1 == t2) {
17514 if (isDate(v1) && isDate(v2)) {
17515 v1 = v1.valueOf();
17516 v2 = v2.valueOf();
17517 }
17518 if (t1 == "string") {
17519 v1 = v1.toLowerCase();
17520 v2 = v2.toLowerCase();
17521 }
17522 if (v1 === v2) return 0;
17523 return v1 < v2 ? -1 : 1;
17524 } else {
17525 return t1 < t2 ? -1 : 1;
17526 }
17527 }
17528 };
17529}
17530
17531function ngDirective(directive) {
17532 if (isFunction(directive)) {
17533 directive = {
17534 link: directive
17535 };
17536 }
17537 directive.restrict = directive.restrict || 'AC';
17538 return valueFn(directive);
17539}
17540
17541/**
17542 * @ngdoc directive
17543 * @name a
17544 * @restrict E
17545 *
17546 * @description
17547 * Modifies the default behavior of the html A tag so that the default action is prevented when
17548 * the href attribute is empty.
17549 *
17550 * This change permits the easy creation of action links with the `ngClick` directive
17551 * without changing the location or causing page reloads, e.g.:
17552 * `<a href="" ng-click="list.addItem()">Add Item</a>`
17553 */
17554var htmlAnchorDirective = valueFn({
17555 restrict: 'E',
17556 compile: function(element, attr) {
17557 if (!attr.href && !attr.xlinkHref && !attr.name) {
17558 return function(scope, element) {
17559 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
17560 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
17561 'xlink:href' : 'href';
17562 element.on('click', function(event) {
17563 // if we have no href url, then don't navigate anywhere.
17564 if (!element.attr(href)) {
17565 event.preventDefault();
17566 }
17567 });
17568 };
17569 }
17570 }
17571});
17572
17573/**
17574 * @ngdoc directive
17575 * @name ngHref
17576 * @restrict A
17577 * @priority 99
17578 *
17579 * @description
17580 * Using Angular markup like `{{hash}}` in an href attribute will
17581 * make the link go to the wrong URL if the user clicks it before
17582 * Angular has a chance to replace the `{{hash}}` markup with its
17583 * value. Until Angular replaces the markup the link will be broken
17584 * and will most likely return a 404 error. The `ngHref` directive
17585 * solves this problem.
17586 *
17587 * The wrong way to write it:
17588 * ```html
17589 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17590 * ```
17591 *
17592 * The correct way to write it:
17593 * ```html
17594 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
17595 * ```
17596 *
17597 * @element A
17598 * @param {template} ngHref any string which can contain `{{}}` markup.
17599 *
17600 * @example
17601 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
17602 * in links and their different behaviors:
17603 <example>
17604 <file name="index.html">
17605 <input ng-model="value" /><br />
17606 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
17607 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
17608 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
17609 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
17610 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
17611 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
17612 </file>
17613 <file name="protractor.js" type="protractor">
17614 it('should execute ng-click but not reload when href without value', function() {
17615 element(by.id('link-1')).click();
17616 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
17617 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
17618 });
17619
17620 it('should execute ng-click but not reload when href empty string', function() {
17621 element(by.id('link-2')).click();
17622 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
17623 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
17624 });
17625
17626 it('should execute ng-click and change url when ng-href specified', function() {
17627 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
17628
17629 element(by.id('link-3')).click();
17630
17631 // At this point, we navigate away from an Angular page, so we need
17632 // to use browser.driver to get the base webdriver.
17633
17634 browser.wait(function() {
17635 return browser.driver.getCurrentUrl().then(function(url) {
17636 return url.match(/\/123$/);
17637 });
17638 }, 5000, 'page should navigate to /123');
17639 });
17640
17641 xit('should execute ng-click but not reload when href empty string and name specified', function() {
17642 element(by.id('link-4')).click();
17643 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
17644 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
17645 });
17646
17647 it('should execute ng-click but not reload when no href but name specified', function() {
17648 element(by.id('link-5')).click();
17649 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
17650 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
17651 });
17652
17653 it('should only change url when only ng-href', function() {
17654 element(by.model('value')).clear();
17655 element(by.model('value')).sendKeys('6');
17656 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
17657
17658 element(by.id('link-6')).click();
17659
17660 // At this point, we navigate away from an Angular page, so we need
17661 // to use browser.driver to get the base webdriver.
17662 browser.wait(function() {
17663 return browser.driver.getCurrentUrl().then(function(url) {
17664 return url.match(/\/6$/);
17665 });
17666 }, 5000, 'page should navigate to /6');
17667 });
17668 </file>
17669 </example>
17670 */
17671
17672/**
17673 * @ngdoc directive
17674 * @name ngSrc
17675 * @restrict A
17676 * @priority 99
17677 *
17678 * @description
17679 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
17680 * work right: The browser will fetch from the URL with the literal
17681 * text `{{hash}}` until Angular replaces the expression inside
17682 * `{{hash}}`. The `ngSrc` directive solves this problem.
17683 *
17684 * The buggy way to write it:
17685 * ```html
17686 * <img src="http://www.gravatar.com/avatar/{{hash}}"/>
17687 * ```
17688 *
17689 * The correct way to write it:
17690 * ```html
17691 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}"/>
17692 * ```
17693 *
17694 * @element IMG
17695 * @param {template} ngSrc any string which can contain `{{}}` markup.
17696 */
17697
17698/**
17699 * @ngdoc directive
17700 * @name ngSrcset
17701 * @restrict A
17702 * @priority 99
17703 *
17704 * @description
17705 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
17706 * work right: The browser will fetch from the URL with the literal
17707 * text `{{hash}}` until Angular replaces the expression inside
17708 * `{{hash}}`. The `ngSrcset` directive solves this problem.
17709 *
17710 * The buggy way to write it:
17711 * ```html
17712 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17713 * ```
17714 *
17715 * The correct way to write it:
17716 * ```html
17717 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x"/>
17718 * ```
17719 *
17720 * @element IMG
17721 * @param {template} ngSrcset any string which can contain `{{}}` markup.
17722 */
17723
17724/**
17725 * @ngdoc directive
17726 * @name ngDisabled
17727 * @restrict A
17728 * @priority 100
17729 *
17730 * @description
17731 *
17732 * We shouldn't do this, because it will make the button enabled on Chrome/Firefox but not on IE8 and older IEs:
17733 * ```html
17734 * <div ng-init="scope = { isDisabled: false }">
17735 * <button disabled="{{scope.isDisabled}}">Disabled</button>
17736 * </div>
17737 * ```
17738 *
17739 * The HTML specification does not require browsers to preserve the values of boolean attributes
17740 * such as disabled. (Their presence means true and their absence means false.)
17741 * If we put an Angular interpolation expression into such an attribute then the
17742 * binding information would be lost when the browser removes the attribute.
17743 * The `ngDisabled` directive solves this problem for the `disabled` attribute.
17744 * This complementary directive is not removed by the browser and so provides
17745 * a permanent reliable place to store the binding information.
17746 *
17747 * @example
17748 <example>
17749 <file name="index.html">
17750 Click me to toggle: <input type="checkbox" ng-model="checked"><br/>
17751 <button ng-model="button" ng-disabled="checked">Button</button>
17752 </file>
17753 <file name="protractor.js" type="protractor">
17754 it('should toggle button', function() {
17755 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
17756 element(by.model('checked')).click();
17757 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
17758 });
17759 </file>
17760 </example>
17761 *
17762 * @element INPUT
17763 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
17764 * then special attribute "disabled" will be set on the element
17765 */
17766
17767
17768/**
17769 * @ngdoc directive
17770 * @name ngChecked
17771 * @restrict A
17772 * @priority 100
17773 *
17774 * @description
17775 * The HTML specification does not require browsers to preserve the values of boolean attributes
17776 * such as checked. (Their presence means true and their absence means false.)
17777 * If we put an Angular interpolation expression into such an attribute then the
17778 * binding information would be lost when the browser removes the attribute.
17779 * The `ngChecked` directive solves this problem for the `checked` attribute.
17780 * This complementary directive is not removed by the browser and so provides
17781 * a permanent reliable place to store the binding information.
17782 * @example
17783 <example>
17784 <file name="index.html">
17785 Check me to check both: <input type="checkbox" ng-model="master"><br/>
17786 <input id="checkSlave" type="checkbox" ng-checked="master">
17787 </file>
17788 <file name="protractor.js" type="protractor">
17789 it('should check both checkBoxes', function() {
17790 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
17791 element(by.model('master')).click();
17792 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
17793 });
17794 </file>
17795 </example>
17796 *
17797 * @element INPUT
17798 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
17799 * then special attribute "checked" will be set on the element
17800 */
17801
17802
17803/**
17804 * @ngdoc directive
17805 * @name ngReadonly
17806 * @restrict A
17807 * @priority 100
17808 *
17809 * @description
17810 * The HTML specification does not require browsers to preserve the values of boolean attributes
17811 * such as readonly. (Their presence means true and their absence means false.)
17812 * If we put an Angular interpolation expression into such an attribute then the
17813 * binding information would be lost when the browser removes the attribute.
17814 * The `ngReadonly` directive solves this problem for the `readonly` attribute.
17815 * This complementary directive is not removed by the browser and so provides
17816 * a permanent reliable place to store the binding information.
17817 * @example
17818 <example>
17819 <file name="index.html">
17820 Check me to make text readonly: <input type="checkbox" ng-model="checked"><br/>
17821 <input type="text" ng-readonly="checked" value="I'm Angular"/>
17822 </file>
17823 <file name="protractor.js" type="protractor">
17824 it('should toggle readonly attr', function() {
17825 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
17826 element(by.model('checked')).click();
17827 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
17828 });
17829 </file>
17830 </example>
17831 *
17832 * @element INPUT
17833 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
17834 * then special attribute "readonly" will be set on the element
17835 */
17836
17837
17838/**
17839 * @ngdoc directive
17840 * @name ngSelected
17841 * @restrict A
17842 * @priority 100
17843 *
17844 * @description
17845 * The HTML specification does not require browsers to preserve the values of boolean attributes
17846 * such as selected. (Their presence means true and their absence means false.)
17847 * If we put an Angular interpolation expression into such an attribute then the
17848 * binding information would be lost when the browser removes the attribute.
17849 * The `ngSelected` directive solves this problem for the `selected` attribute.
17850 * This complementary directive is not removed by the browser and so provides
17851 * a permanent reliable place to store the binding information.
17852 *
17853 * @example
17854 <example>
17855 <file name="index.html">
17856 Check me to select: <input type="checkbox" ng-model="selected"><br/>
17857 <select>
17858 <option>Hello!</option>
17859 <option id="greet" ng-selected="selected">Greetings!</option>
17860 </select>
17861 </file>
17862 <file name="protractor.js" type="protractor">
17863 it('should select Greetings!', function() {
17864 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
17865 element(by.model('selected')).click();
17866 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
17867 });
17868 </file>
17869 </example>
17870 *
17871 * @element OPTION
17872 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
17873 * then special attribute "selected" will be set on the element
17874 */
17875
17876/**
17877 * @ngdoc directive
17878 * @name ngOpen
17879 * @restrict A
17880 * @priority 100
17881 *
17882 * @description
17883 * The HTML specification does not require browsers to preserve the values of boolean attributes
17884 * such as open. (Their presence means true and their absence means false.)
17885 * If we put an Angular interpolation expression into such an attribute then the
17886 * binding information would be lost when the browser removes the attribute.
17887 * The `ngOpen` directive solves this problem for the `open` attribute.
17888 * This complementary directive is not removed by the browser and so provides
17889 * a permanent reliable place to store the binding information.
17890 * @example
17891 <example>
17892 <file name="index.html">
17893 Check me check multiple: <input type="checkbox" ng-model="open"><br/>
17894 <details id="details" ng-open="open">
17895 <summary>Show/Hide me</summary>
17896 </details>
17897 </file>
17898 <file name="protractor.js" type="protractor">
17899 it('should toggle open', function() {
17900 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
17901 element(by.model('open')).click();
17902 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
17903 });
17904 </file>
17905 </example>
17906 *
17907 * @element DETAILS
17908 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
17909 * then special attribute "open" will be set on the element
17910 */
17911
17912var ngAttributeAliasDirectives = {};
17913
17914
17915// boolean attrs are evaluated
17916forEach(BOOLEAN_ATTR, function(propName, attrName) {
17917 // binding to multiple is not supported
17918 if (propName == "multiple") return;
17919
17920 var normalized = directiveNormalize('ng-' + attrName);
17921 ngAttributeAliasDirectives[normalized] = function() {
17922 return {
17923 restrict: 'A',
17924 priority: 100,
17925 link: function(scope, element, attr) {
17926 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
17927 attr.$set(attrName, !!value);
17928 });
17929 }
17930 };
17931 };
17932});
17933
17934// aliased input attrs are evaluated
17935forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
17936 ngAttributeAliasDirectives[ngAttr] = function() {
17937 return {
17938 priority: 100,
17939 link: function(scope, element, attr) {
17940 //special case ngPattern when a literal regular expression value
17941 //is used as the expression (this way we don't have to watch anything).
17942 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
17943 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
17944 if (match) {
17945 attr.$set("ngPattern", new RegExp(match[1], match[2]));
17946 return;
17947 }
17948 }
17949
17950 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
17951 attr.$set(ngAttr, value);
17952 });
17953 }
17954 };
17955 };
17956});
17957
17958// ng-src, ng-srcset, ng-href are interpolated
17959forEach(['src', 'srcset', 'href'], function(attrName) {
17960 var normalized = directiveNormalize('ng-' + attrName);
17961 ngAttributeAliasDirectives[normalized] = function() {
17962 return {
17963 priority: 99, // it needs to run after the attributes are interpolated
17964 link: function(scope, element, attr) {
17965 var propName = attrName,
17966 name = attrName;
17967
17968 if (attrName === 'href' &&
17969 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
17970 name = 'xlinkHref';
17971 attr.$attr[name] = 'xlink:href';
17972 propName = null;
17973 }
17974
17975 attr.$observe(normalized, function(value) {
17976 if (!value) {
17977 if (attrName === 'href') {
17978 attr.$set(name, null);
17979 }
17980 return;
17981 }
17982
17983 attr.$set(name, value);
17984
17985 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
17986 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
17987 // to set the property as well to achieve the desired effect.
17988 // we use attr[attrName] value since $set can sanitize the url.
17989 if (msie && propName) element.prop(propName, attr[name]);
17990 });
17991 }
17992 };
17993 };
17994});
17995
17996/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
17997 */
17998var nullFormCtrl = {
17999 $addControl: noop,
18000 $$renameControl: nullFormRenameControl,
18001 $removeControl: noop,
18002 $setValidity: noop,
18003 $setDirty: noop,
18004 $setPristine: noop,
18005 $setSubmitted: noop
18006},
18007SUBMITTED_CLASS = 'ng-submitted';
18008
18009function nullFormRenameControl(control, name) {
18010 control.$name = name;
18011}
18012
18013/**
18014 * @ngdoc type
18015 * @name form.FormController
18016 *
18017 * @property {boolean} $pristine True if user has not interacted with the form yet.
18018 * @property {boolean} $dirty True if user has already interacted with the form.
18019 * @property {boolean} $valid True if all of the containing forms and controls are valid.
18020 * @property {boolean} $invalid True if at least one containing control or form is invalid.
18021 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
18022 *
18023 * @property {Object} $error Is an object hash, containing references to controls or
18024 * forms with failing validators, where:
18025 *
18026 * - keys are validation tokens (error names),
18027 * - values are arrays of controls or forms that have a failing validator for given error name.
18028 *
18029 * Built-in validation tokens:
18030 *
18031 * - `email`
18032 * - `max`
18033 * - `maxlength`
18034 * - `min`
18035 * - `minlength`
18036 * - `number`
18037 * - `pattern`
18038 * - `required`
18039 * - `url`
18040 * - `date`
18041 * - `datetimelocal`
18042 * - `time`
18043 * - `week`
18044 * - `month`
18045 *
18046 * @description
18047 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
18048 * such as being valid/invalid or dirty/pristine.
18049 *
18050 * Each {@link ng.directive:form form} directive creates an instance
18051 * of `FormController`.
18052 *
18053 */
18054//asks for $scope to fool the BC controller module
18055FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
18056function FormController(element, attrs, $scope, $animate, $interpolate) {
18057 var form = this,
18058 controls = [];
18059
18060 var parentForm = form.$$parentForm = element.parent().controller('form') || nullFormCtrl;
18061
18062 // init state
18063 form.$error = {};
18064 form.$$success = {};
18065 form.$pending = undefined;
18066 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
18067 form.$dirty = false;
18068 form.$pristine = true;
18069 form.$valid = true;
18070 form.$invalid = false;
18071 form.$submitted = false;
18072
18073 parentForm.$addControl(form);
18074
18075 /**
18076 * @ngdoc method
18077 * @name form.FormController#$rollbackViewValue
18078 *
18079 * @description
18080 * Rollback all form controls pending updates to the `$modelValue`.
18081 *
18082 * Updates may be pending by a debounced event or because the input is waiting for a some future
18083 * event defined in `ng-model-options`. This method is typically needed by the reset button of
18084 * a form that uses `ng-model-options` to pend updates.
18085 */
18086 form.$rollbackViewValue = function() {
18087 forEach(controls, function(control) {
18088 control.$rollbackViewValue();
18089 });
18090 };
18091
18092 /**
18093 * @ngdoc method
18094 * @name form.FormController#$commitViewValue
18095 *
18096 * @description
18097 * Commit all form controls pending updates to the `$modelValue`.
18098 *
18099 * Updates may be pending by a debounced event or because the input is waiting for a some future
18100 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
18101 * usually handles calling this in response to input events.
18102 */
18103 form.$commitViewValue = function() {
18104 forEach(controls, function(control) {
18105 control.$commitViewValue();
18106 });
18107 };
18108
18109 /**
18110 * @ngdoc method
18111 * @name form.FormController#$addControl
18112 *
18113 * @description
18114 * Register a control with the form.
18115 *
18116 * Input elements using ngModelController do this automatically when they are linked.
18117 */
18118 form.$addControl = function(control) {
18119 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
18120 // and not added to the scope. Now we throw an error.
18121 assertNotHasOwnProperty(control.$name, 'input');
18122 controls.push(control);
18123
18124 if (control.$name) {
18125 form[control.$name] = control;
18126 }
18127 };
18128
18129 // Private API: rename a form control
18130 form.$$renameControl = function(control, newName) {
18131 var oldName = control.$name;
18132
18133 if (form[oldName] === control) {
18134 delete form[oldName];
18135 }
18136 form[newName] = control;
18137 control.$name = newName;
18138 };
18139
18140 /**
18141 * @ngdoc method
18142 * @name form.FormController#$removeControl
18143 *
18144 * @description
18145 * Deregister a control from the form.
18146 *
18147 * Input elements using ngModelController do this automatically when they are destroyed.
18148 */
18149 form.$removeControl = function(control) {
18150 if (control.$name && form[control.$name] === control) {
18151 delete form[control.$name];
18152 }
18153 forEach(form.$pending, function(value, name) {
18154 form.$setValidity(name, null, control);
18155 });
18156 forEach(form.$error, function(value, name) {
18157 form.$setValidity(name, null, control);
18158 });
18159
18160 arrayRemove(controls, control);
18161 };
18162
18163
18164 /**
18165 * @ngdoc method
18166 * @name form.FormController#$setValidity
18167 *
18168 * @description
18169 * Sets the validity of a form control.
18170 *
18171 * This method will also propagate to parent forms.
18172 */
18173 addSetValidityMethod({
18174 ctrl: this,
18175 $element: element,
18176 set: function(object, property, control) {
18177 var list = object[property];
18178 if (!list) {
18179 object[property] = [control];
18180 } else {
18181 var index = list.indexOf(control);
18182 if (index === -1) {
18183 list.push(control);
18184 }
18185 }
18186 },
18187 unset: function(object, property, control) {
18188 var list = object[property];
18189 if (!list) {
18190 return;
18191 }
18192 arrayRemove(list, control);
18193 if (list.length === 0) {
18194 delete object[property];
18195 }
18196 },
18197 parentForm: parentForm,
18198 $animate: $animate
18199 });
18200
18201 /**
18202 * @ngdoc method
18203 * @name form.FormController#$setDirty
18204 *
18205 * @description
18206 * Sets the form to a dirty state.
18207 *
18208 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
18209 * state (ng-dirty class). This method will also propagate to parent forms.
18210 */
18211 form.$setDirty = function() {
18212 $animate.removeClass(element, PRISTINE_CLASS);
18213 $animate.addClass(element, DIRTY_CLASS);
18214 form.$dirty = true;
18215 form.$pristine = false;
18216 parentForm.$setDirty();
18217 };
18218
18219 /**
18220 * @ngdoc method
18221 * @name form.FormController#$setPristine
18222 *
18223 * @description
18224 * Sets the form to its pristine state.
18225 *
18226 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
18227 * state (ng-pristine class). This method will also propagate to all the controls contained
18228 * in this form.
18229 *
18230 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
18231 * saving or resetting it.
18232 */
18233 form.$setPristine = function() {
18234 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
18235 form.$dirty = false;
18236 form.$pristine = true;
18237 form.$submitted = false;
18238 forEach(controls, function(control) {
18239 control.$setPristine();
18240 });
18241 };
18242
18243 /**
18244 * @ngdoc method
18245 * @name form.FormController#$setUntouched
18246 *
18247 * @description
18248 * Sets the form to its untouched state.
18249 *
18250 * This method can be called to remove the 'ng-touched' class and set the form controls to their
18251 * untouched state (ng-untouched class).
18252 *
18253 * Setting a form controls back to their untouched state is often useful when setting the form
18254 * back to its pristine state.
18255 */
18256 form.$setUntouched = function() {
18257 forEach(controls, function(control) {
18258 control.$setUntouched();
18259 });
18260 };
18261
18262 /**
18263 * @ngdoc method
18264 * @name form.FormController#$setSubmitted
18265 *
18266 * @description
18267 * Sets the form to its submitted state.
18268 */
18269 form.$setSubmitted = function() {
18270 $animate.addClass(element, SUBMITTED_CLASS);
18271 form.$submitted = true;
18272 parentForm.$setSubmitted();
18273 };
18274}
18275
18276/**
18277 * @ngdoc directive
18278 * @name ngForm
18279 * @restrict EAC
18280 *
18281 * @description
18282 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
18283 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
18284 * sub-group of controls needs to be determined.
18285 *
18286 * Note: the purpose of `ngForm` is to group controls,
18287 * but not to be a replacement for the `<form>` tag with all of its capabilities
18288 * (e.g. posting to the server, ...).
18289 *
18290 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
18291 * related scope, under this name.
18292 *
18293 */
18294
18295 /**
18296 * @ngdoc directive
18297 * @name form
18298 * @restrict E
18299 *
18300 * @description
18301 * Directive that instantiates
18302 * {@link form.FormController FormController}.
18303 *
18304 * If the `name` attribute is specified, the form controller is published onto the current scope under
18305 * this name.
18306 *
18307 * # Alias: {@link ng.directive:ngForm `ngForm`}
18308 *
18309 * In Angular forms can be nested. This means that the outer form is valid when all of the child
18310 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
18311 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
18312 * `<form>` but can be nested. This allows you to have nested forms, which is very useful when
18313 * using Angular validation directives in forms that are dynamically generated using the
18314 * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
18315 * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
18316 * `ngForm` directive and nest these in an outer `form` element.
18317 *
18318 *
18319 * # CSS classes
18320 * - `ng-valid` is set if the form is valid.
18321 * - `ng-invalid` is set if the form is invalid.
18322 * - `ng-pristine` is set if the form is pristine.
18323 * - `ng-dirty` is set if the form is dirty.
18324 * - `ng-submitted` is set if the form was submitted.
18325 *
18326 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
18327 *
18328 *
18329 * # Submitting a form and preventing the default action
18330 *
18331 * Since the role of forms in client-side Angular applications is different than in classical
18332 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
18333 * page reload that sends the data to the server. Instead some javascript logic should be triggered
18334 * to handle the form submission in an application-specific way.
18335 *
18336 * For this reason, Angular prevents the default action (form submission to the server) unless the
18337 * `<form>` element has an `action` attribute specified.
18338 *
18339 * You can use one of the following two ways to specify what javascript method should be called when
18340 * a form is submitted:
18341 *
18342 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
18343 * - {@link ng.directive:ngClick ngClick} directive on the first
18344 * button or input field of type submit (input[type=submit])
18345 *
18346 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
18347 * or {@link ng.directive:ngClick ngClick} directives.
18348 * This is because of the following form submission rules in the HTML specification:
18349 *
18350 * - If a form has only one input field then hitting enter in this field triggers form submit
18351 * (`ngSubmit`)
18352 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
18353 * doesn't trigger submit
18354 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
18355 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
18356 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
18357 *
18358 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
18359 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
18360 * to have access to the updated model.
18361 *
18362 * ## Animation Hooks
18363 *
18364 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
18365 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
18366 * other validations that are performed within the form. Animations in ngForm are similar to how
18367 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
18368 * as JS animations.
18369 *
18370 * The following example shows a simple way to utilize CSS transitions to style a form element
18371 * that has been rendered as invalid after it has been validated:
18372 *
18373 * <pre>
18374 * //be sure to include ngAnimate as a module to hook into more
18375 * //advanced animations
18376 * .my-form {
18377 * transition:0.5s linear all;
18378 * background: white;
18379 * }
18380 * .my-form.ng-invalid {
18381 * background: red;
18382 * color:white;
18383 * }
18384 * </pre>
18385 *
18386 * @example
18387 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
18388 <file name="index.html">
18389 <script>
18390 angular.module('formExample', [])
18391 .controller('FormController', ['$scope', function($scope) {
18392 $scope.userType = 'guest';
18393 }]);
18394 </script>
18395 <style>
18396 .my-form {
18397 -webkit-transition:all linear 0.5s;
18398 transition:all linear 0.5s;
18399 background: transparent;
18400 }
18401 .my-form.ng-invalid {
18402 background: red;
18403 }
18404 </style>
18405 <form name="myForm" ng-controller="FormController" class="my-form">
18406 userType: <input name="input" ng-model="userType" required>
18407 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
18408 <tt>userType = {{userType}}</tt><br>
18409 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br>
18410 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br>
18411 <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
18412 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
18413 </form>
18414 </file>
18415 <file name="protractor.js" type="protractor">
18416 it('should initialize to model', function() {
18417 var userType = element(by.binding('userType'));
18418 var valid = element(by.binding('myForm.input.$valid'));
18419
18420 expect(userType.getText()).toContain('guest');
18421 expect(valid.getText()).toContain('true');
18422 });
18423
18424 it('should be invalid if empty', function() {
18425 var userType = element(by.binding('userType'));
18426 var valid = element(by.binding('myForm.input.$valid'));
18427 var userInput = element(by.model('userType'));
18428
18429 userInput.clear();
18430 userInput.sendKeys('');
18431
18432 expect(userType.getText()).toEqual('userType =');
18433 expect(valid.getText()).toContain('false');
18434 });
18435 </file>
18436 </example>
18437 *
18438 * @param {string=} name Name of the form. If specified, the form controller will be published into
18439 * related scope, under this name.
18440 */
18441var formDirectiveFactory = function(isNgForm) {
18442 return ['$timeout', function($timeout) {
18443 var formDirective = {
18444 name: 'form',
18445 restrict: isNgForm ? 'EAC' : 'E',
18446 controller: FormController,
18447 compile: function ngFormCompile(formElement) {
18448 // Setup initial state of the control
18449 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
18450
18451 return {
18452 pre: function ngFormPreLink(scope, formElement, attr, controller) {
18453 // if `action` attr is not present on the form, prevent the default action (submission)
18454 if (!('action' in attr)) {
18455 // we can't use jq events because if a form is destroyed during submission the default
18456 // action is not prevented. see #1238
18457 //
18458 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
18459 // page reload if the form was destroyed by submission of the form via a click handler
18460 // on a button in the form. Looks like an IE9 specific bug.
18461 var handleFormSubmission = function(event) {
18462 scope.$apply(function() {
18463 controller.$commitViewValue();
18464 controller.$setSubmitted();
18465 });
18466
18467 event.preventDefault();
18468 };
18469
18470 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18471
18472 // unregister the preventDefault listener so that we don't not leak memory but in a
18473 // way that will achieve the prevention of the default action.
18474 formElement.on('$destroy', function() {
18475 $timeout(function() {
18476 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
18477 }, 0, false);
18478 });
18479 }
18480
18481 var parentFormCtrl = controller.$$parentForm,
18482 alias = controller.$name;
18483
18484 if (alias) {
18485 setter(scope, alias, controller, alias);
18486 attr.$observe(attr.name ? 'name' : 'ngForm', function(newValue) {
18487 if (alias === newValue) return;
18488 setter(scope, alias, undefined, alias);
18489 alias = newValue;
18490 setter(scope, alias, controller, alias);
18491 parentFormCtrl.$$renameControl(controller, alias);
18492 });
18493 }
18494 formElement.on('$destroy', function() {
18495 parentFormCtrl.$removeControl(controller);
18496 if (alias) {
18497 setter(scope, alias, undefined, alias);
18498 }
18499 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
18500 });
18501 }
18502 };
18503 }
18504 };
18505
18506 return formDirective;
18507 }];
18508};
18509
18510var formDirective = formDirectiveFactory();
18511var ngFormDirective = formDirectiveFactory(true);
18512
18513/* global VALID_CLASS: true,
18514 INVALID_CLASS: true,
18515 PRISTINE_CLASS: true,
18516 DIRTY_CLASS: true,
18517 UNTOUCHED_CLASS: true,
18518 TOUCHED_CLASS: true,
18519*/
18520
18521// Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
18522var ISO_DATE_REGEXP = /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z)/;
18523var URL_REGEXP = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/;
18524var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
18525var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))\s*$/;
18526var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
18527var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18528var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
18529var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
18530var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
18531var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
18532
18533var $ngModelMinErr = new minErr('ngModel');
18534
18535var inputType = {
18536
18537 /**
18538 * @ngdoc input
18539 * @name input[text]
18540 *
18541 * @description
18542 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
18543 *
18544 *
18545 * @param {string} ngModel Assignable angular expression to data-bind to.
18546 * @param {string=} name Property name of the form under which the control is published.
18547 * @param {string=} required Adds `required` validation error key if the value is not entered.
18548 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18549 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18550 * `required` when you want to data-bind to the `required` attribute.
18551 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
18552 * minlength.
18553 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
18554 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
18555 * any length.
18556 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
18557 * that contains the regular expression body that will be converted to a regular expression
18558 * as in the ngPattern directive.
18559 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
18560 * a RegExp found by evaluating the Angular expression given in the attribute value.
18561 * If the expression evaluates to a RegExp object then this is used directly.
18562 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
18563 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
18564 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18565 * interaction with the input element.
18566 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
18567 * This parameter is ignored for input[type=password] controls, which will never trim the
18568 * input.
18569 *
18570 * @example
18571 <example name="text-input-directive" module="textInputExample">
18572 <file name="index.html">
18573 <script>
18574 angular.module('textInputExample', [])
18575 .controller('ExampleController', ['$scope', function($scope) {
18576 $scope.text = 'guest';
18577 $scope.word = /^\s*\w*\s*$/;
18578 }]);
18579 </script>
18580 <form name="myForm" ng-controller="ExampleController">
18581 Single word: <input type="text" name="input" ng-model="text"
18582 ng-pattern="word" required ng-trim="false">
18583 <span class="error" ng-show="myForm.input.$error.required">
18584 Required!</span>
18585 <span class="error" ng-show="myForm.input.$error.pattern">
18586 Single word only!</span>
18587
18588 <tt>text = {{text}}</tt><br/>
18589 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18590 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18591 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18592 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18593 </form>
18594 </file>
18595 <file name="protractor.js" type="protractor">
18596 var text = element(by.binding('text'));
18597 var valid = element(by.binding('myForm.input.$valid'));
18598 var input = element(by.model('text'));
18599
18600 it('should initialize to model', function() {
18601 expect(text.getText()).toContain('guest');
18602 expect(valid.getText()).toContain('true');
18603 });
18604
18605 it('should be invalid if empty', function() {
18606 input.clear();
18607 input.sendKeys('');
18608
18609 expect(text.getText()).toEqual('text =');
18610 expect(valid.getText()).toContain('false');
18611 });
18612
18613 it('should be invalid if multi word', function() {
18614 input.clear();
18615 input.sendKeys('hello world');
18616
18617 expect(valid.getText()).toContain('false');
18618 });
18619 </file>
18620 </example>
18621 */
18622 'text': textInputType,
18623
18624 /**
18625 * @ngdoc input
18626 * @name input[date]
18627 *
18628 * @description
18629 * Input with date validation and transformation. In browsers that do not yet support
18630 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
18631 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
18632 * modern browsers do not yet support this input type, it is important to provide cues to users on the
18633 * expected input format via a placeholder or label.
18634 *
18635 * The model must always be a Date object, otherwise Angular will throw an error.
18636 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18637 *
18638 * The timezone to be used to read/write the `Date` instance in the model can be defined using
18639 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18640 *
18641 * @param {string} ngModel Assignable angular expression to data-bind to.
18642 * @param {string=} name Property name of the form under which the control is published.
18643 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18644 * valid ISO date string (yyyy-MM-dd).
18645 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18646 * a valid ISO date string (yyyy-MM-dd).
18647 * @param {string=} required Sets `required` validation error key if the value is not entered.
18648 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18649 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18650 * `required` when you want to data-bind to the `required` attribute.
18651 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18652 * interaction with the input element.
18653 *
18654 * @example
18655 <example name="date-input-directive" module="dateInputExample">
18656 <file name="index.html">
18657 <script>
18658 angular.module('dateInputExample', [])
18659 .controller('DateController', ['$scope', function($scope) {
18660 $scope.value = new Date(2013, 9, 22);
18661 }]);
18662 </script>
18663 <form name="myForm" ng-controller="DateController as dateCtrl">
18664 Pick a date in 2013:
18665 <input type="date" id="exampleInput" name="input" ng-model="value"
18666 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
18667 <span class="error" ng-show="myForm.input.$error.required">
18668 Required!</span>
18669 <span class="error" ng-show="myForm.input.$error.date">
18670 Not a valid date!</span>
18671 <tt>value = {{value | date: "yyyy-MM-dd"}}</tt><br/>
18672 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18673 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18674 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18675 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18676 </form>
18677 </file>
18678 <file name="protractor.js" type="protractor">
18679 var value = element(by.binding('value | date: "yyyy-MM-dd"'));
18680 var valid = element(by.binding('myForm.input.$valid'));
18681 var input = element(by.model('value'));
18682
18683 // currently protractor/webdriver does not support
18684 // sending keys to all known HTML5 input controls
18685 // for various browsers (see https://github.com/angular/protractor/issues/562).
18686 function setInput(val) {
18687 // set the value of the element and force validation.
18688 var scr = "var ipt = document.getElementById('exampleInput'); " +
18689 "ipt.value = '" + val + "';" +
18690 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18691 browser.executeScript(scr);
18692 }
18693
18694 it('should initialize to model', function() {
18695 expect(value.getText()).toContain('2013-10-22');
18696 expect(valid.getText()).toContain('myForm.input.$valid = true');
18697 });
18698
18699 it('should be invalid if empty', function() {
18700 setInput('');
18701 expect(value.getText()).toEqual('value =');
18702 expect(valid.getText()).toContain('myForm.input.$valid = false');
18703 });
18704
18705 it('should be invalid if over max', function() {
18706 setInput('2015-01-01');
18707 expect(value.getText()).toContain('');
18708 expect(valid.getText()).toContain('myForm.input.$valid = false');
18709 });
18710 </file>
18711 </example>
18712 */
18713 'date': createDateInputType('date', DATE_REGEXP,
18714 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
18715 'yyyy-MM-dd'),
18716
18717 /**
18718 * @ngdoc input
18719 * @name input[datetime-local]
18720 *
18721 * @description
18722 * Input with datetime validation and transformation. In browsers that do not yet support
18723 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18724 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
18725 *
18726 * The model must always be a Date object, otherwise Angular will throw an error.
18727 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18728 *
18729 * The timezone to be used to read/write the `Date` instance in the model can be defined using
18730 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18731 *
18732 * @param {string} ngModel Assignable angular expression to data-bind to.
18733 * @param {string=} name Property name of the form under which the control is published.
18734 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18735 * valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18736 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18737 * a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss).
18738 * @param {string=} required Sets `required` validation error key if the value is not entered.
18739 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18740 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18741 * `required` when you want to data-bind to the `required` attribute.
18742 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18743 * interaction with the input element.
18744 *
18745 * @example
18746 <example name="datetimelocal-input-directive" module="dateExample">
18747 <file name="index.html">
18748 <script>
18749 angular.module('dateExample', [])
18750 .controller('DateController', ['$scope', function($scope) {
18751 $scope.value = new Date(2010, 11, 28, 14, 57);
18752 }]);
18753 </script>
18754 <form name="myForm" ng-controller="DateController as dateCtrl">
18755 Pick a date between in 2013:
18756 <input type="datetime-local" id="exampleInput" name="input" ng-model="value"
18757 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
18758 <span class="error" ng-show="myForm.input.$error.required">
18759 Required!</span>
18760 <span class="error" ng-show="myForm.input.$error.datetimelocal">
18761 Not a valid date!</span>
18762 <tt>value = {{value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
18763 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18764 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18765 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18766 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18767 </form>
18768 </file>
18769 <file name="protractor.js" type="protractor">
18770 var value = element(by.binding('value | date: "yyyy-MM-ddTHH:mm:ss"'));
18771 var valid = element(by.binding('myForm.input.$valid'));
18772 var input = element(by.model('value'));
18773
18774 // currently protractor/webdriver does not support
18775 // sending keys to all known HTML5 input controls
18776 // for various browsers (https://github.com/angular/protractor/issues/562).
18777 function setInput(val) {
18778 // set the value of the element and force validation.
18779 var scr = "var ipt = document.getElementById('exampleInput'); " +
18780 "ipt.value = '" + val + "';" +
18781 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18782 browser.executeScript(scr);
18783 }
18784
18785 it('should initialize to model', function() {
18786 expect(value.getText()).toContain('2010-12-28T14:57:00');
18787 expect(valid.getText()).toContain('myForm.input.$valid = true');
18788 });
18789
18790 it('should be invalid if empty', function() {
18791 setInput('');
18792 expect(value.getText()).toEqual('value =');
18793 expect(valid.getText()).toContain('myForm.input.$valid = false');
18794 });
18795
18796 it('should be invalid if over max', function() {
18797 setInput('2015-01-01T23:59:00');
18798 expect(value.getText()).toContain('');
18799 expect(valid.getText()).toContain('myForm.input.$valid = false');
18800 });
18801 </file>
18802 </example>
18803 */
18804 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
18805 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
18806 'yyyy-MM-ddTHH:mm:ss.sss'),
18807
18808 /**
18809 * @ngdoc input
18810 * @name input[time]
18811 *
18812 * @description
18813 * Input with time validation and transformation. In browsers that do not yet support
18814 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18815 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
18816 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
18817 *
18818 * The model must always be a Date object, otherwise Angular will throw an error.
18819 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18820 *
18821 * The timezone to be used to read/write the `Date` instance in the model can be defined using
18822 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18823 *
18824 * @param {string} ngModel Assignable angular expression to data-bind to.
18825 * @param {string=} name Property name of the form under which the control is published.
18826 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18827 * valid ISO time format (HH:mm:ss).
18828 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be a
18829 * valid ISO time format (HH:mm:ss).
18830 * @param {string=} required Sets `required` validation error key if the value is not entered.
18831 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18832 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18833 * `required` when you want to data-bind to the `required` attribute.
18834 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18835 * interaction with the input element.
18836 *
18837 * @example
18838 <example name="time-input-directive" module="timeExample">
18839 <file name="index.html">
18840 <script>
18841 angular.module('timeExample', [])
18842 .controller('DateController', ['$scope', function($scope) {
18843 $scope.value = new Date(1970, 0, 1, 14, 57, 0);
18844 }]);
18845 </script>
18846 <form name="myForm" ng-controller="DateController as dateCtrl">
18847 Pick a between 8am and 5pm:
18848 <input type="time" id="exampleInput" name="input" ng-model="value"
18849 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
18850 <span class="error" ng-show="myForm.input.$error.required">
18851 Required!</span>
18852 <span class="error" ng-show="myForm.input.$error.time">
18853 Not a valid date!</span>
18854 <tt>value = {{value | date: "HH:mm:ss"}}</tt><br/>
18855 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18856 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18857 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18858 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18859 </form>
18860 </file>
18861 <file name="protractor.js" type="protractor">
18862 var value = element(by.binding('value | date: "HH:mm:ss"'));
18863 var valid = element(by.binding('myForm.input.$valid'));
18864 var input = element(by.model('value'));
18865
18866 // currently protractor/webdriver does not support
18867 // sending keys to all known HTML5 input controls
18868 // for various browsers (https://github.com/angular/protractor/issues/562).
18869 function setInput(val) {
18870 // set the value of the element and force validation.
18871 var scr = "var ipt = document.getElementById('exampleInput'); " +
18872 "ipt.value = '" + val + "';" +
18873 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18874 browser.executeScript(scr);
18875 }
18876
18877 it('should initialize to model', function() {
18878 expect(value.getText()).toContain('14:57:00');
18879 expect(valid.getText()).toContain('myForm.input.$valid = true');
18880 });
18881
18882 it('should be invalid if empty', function() {
18883 setInput('');
18884 expect(value.getText()).toEqual('value =');
18885 expect(valid.getText()).toContain('myForm.input.$valid = false');
18886 });
18887
18888 it('should be invalid if over max', function() {
18889 setInput('23:59:00');
18890 expect(value.getText()).toContain('');
18891 expect(valid.getText()).toContain('myForm.input.$valid = false');
18892 });
18893 </file>
18894 </example>
18895 */
18896 'time': createDateInputType('time', TIME_REGEXP,
18897 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
18898 'HH:mm:ss.sss'),
18899
18900 /**
18901 * @ngdoc input
18902 * @name input[week]
18903 *
18904 * @description
18905 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
18906 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18907 * week format (yyyy-W##), for example: `2013-W02`.
18908 *
18909 * The model must always be a Date object, otherwise Angular will throw an error.
18910 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
18911 *
18912 * The timezone to be used to read/write the `Date` instance in the model can be defined using
18913 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
18914 *
18915 * @param {string} ngModel Assignable angular expression to data-bind to.
18916 * @param {string=} name Property name of the form under which the control is published.
18917 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
18918 * valid ISO week format (yyyy-W##).
18919 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
18920 * a valid ISO week format (yyyy-W##).
18921 * @param {string=} required Sets `required` validation error key if the value is not entered.
18922 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
18923 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
18924 * `required` when you want to data-bind to the `required` attribute.
18925 * @param {string=} ngChange Angular expression to be executed when input changes due to user
18926 * interaction with the input element.
18927 *
18928 * @example
18929 <example name="week-input-directive" module="weekExample">
18930 <file name="index.html">
18931 <script>
18932 angular.module('weekExample', [])
18933 .controller('DateController', ['$scope', function($scope) {
18934 $scope.value = new Date(2013, 0, 3);
18935 }]);
18936 </script>
18937 <form name="myForm" ng-controller="DateController as dateCtrl">
18938 Pick a date between in 2013:
18939 <input id="exampleInput" type="week" name="input" ng-model="value"
18940 placeholder="YYYY-W##" min="2012-W32" max="2013-W52" required />
18941 <span class="error" ng-show="myForm.input.$error.required">
18942 Required!</span>
18943 <span class="error" ng-show="myForm.input.$error.week">
18944 Not a valid date!</span>
18945 <tt>value = {{value | date: "yyyy-Www"}}</tt><br/>
18946 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
18947 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
18948 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
18949 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
18950 </form>
18951 </file>
18952 <file name="protractor.js" type="protractor">
18953 var value = element(by.binding('value | date: "yyyy-Www"'));
18954 var valid = element(by.binding('myForm.input.$valid'));
18955 var input = element(by.model('value'));
18956
18957 // currently protractor/webdriver does not support
18958 // sending keys to all known HTML5 input controls
18959 // for various browsers (https://github.com/angular/protractor/issues/562).
18960 function setInput(val) {
18961 // set the value of the element and force validation.
18962 var scr = "var ipt = document.getElementById('exampleInput'); " +
18963 "ipt.value = '" + val + "';" +
18964 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
18965 browser.executeScript(scr);
18966 }
18967
18968 it('should initialize to model', function() {
18969 expect(value.getText()).toContain('2013-W01');
18970 expect(valid.getText()).toContain('myForm.input.$valid = true');
18971 });
18972
18973 it('should be invalid if empty', function() {
18974 setInput('');
18975 expect(value.getText()).toEqual('value =');
18976 expect(valid.getText()).toContain('myForm.input.$valid = false');
18977 });
18978
18979 it('should be invalid if over max', function() {
18980 setInput('2015-W01');
18981 expect(value.getText()).toContain('');
18982 expect(valid.getText()).toContain('myForm.input.$valid = false');
18983 });
18984 </file>
18985 </example>
18986 */
18987 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
18988
18989 /**
18990 * @ngdoc input
18991 * @name input[month]
18992 *
18993 * @description
18994 * Input with month validation and transformation. In browsers that do not yet support
18995 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
18996 * month format (yyyy-MM), for example: `2009-01`.
18997 *
18998 * The model must always be a Date object, otherwise Angular will throw an error.
18999 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
19000 * If the model is not set to the first of the month, the next view to model update will set it
19001 * to the first of the month.
19002 *
19003 * The timezone to be used to read/write the `Date` instance in the model can be defined using
19004 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
19005 *
19006 * @param {string} ngModel Assignable angular expression to data-bind to.
19007 * @param {string=} name Property name of the form under which the control is published.
19008 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be
19009 * a valid ISO month format (yyyy-MM).
19010 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must
19011 * be a valid ISO month format (yyyy-MM).
19012 * @param {string=} required Sets `required` validation error key if the value is not entered.
19013 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19014 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19015 * `required` when you want to data-bind to the `required` attribute.
19016 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19017 * interaction with the input element.
19018 *
19019 * @example
19020 <example name="month-input-directive" module="monthExample">
19021 <file name="index.html">
19022 <script>
19023 angular.module('monthExample', [])
19024 .controller('DateController', ['$scope', function($scope) {
19025 $scope.value = new Date(2013, 9, 1);
19026 }]);
19027 </script>
19028 <form name="myForm" ng-controller="DateController as dateCtrl">
19029 Pick a month int 2013:
19030 <input id="exampleInput" type="month" name="input" ng-model="value"
19031 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
19032 <span class="error" ng-show="myForm.input.$error.required">
19033 Required!</span>
19034 <span class="error" ng-show="myForm.input.$error.month">
19035 Not a valid month!</span>
19036 <tt>value = {{value | date: "yyyy-MM"}}</tt><br/>
19037 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19038 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19039 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19040 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19041 </form>
19042 </file>
19043 <file name="protractor.js" type="protractor">
19044 var value = element(by.binding('value | date: "yyyy-MM"'));
19045 var valid = element(by.binding('myForm.input.$valid'));
19046 var input = element(by.model('value'));
19047
19048 // currently protractor/webdriver does not support
19049 // sending keys to all known HTML5 input controls
19050 // for various browsers (https://github.com/angular/protractor/issues/562).
19051 function setInput(val) {
19052 // set the value of the element and force validation.
19053 var scr = "var ipt = document.getElementById('exampleInput'); " +
19054 "ipt.value = '" + val + "';" +
19055 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
19056 browser.executeScript(scr);
19057 }
19058
19059 it('should initialize to model', function() {
19060 expect(value.getText()).toContain('2013-10');
19061 expect(valid.getText()).toContain('myForm.input.$valid = true');
19062 });
19063
19064 it('should be invalid if empty', function() {
19065 setInput('');
19066 expect(value.getText()).toEqual('value =');
19067 expect(valid.getText()).toContain('myForm.input.$valid = false');
19068 });
19069
19070 it('should be invalid if over max', function() {
19071 setInput('2015-01');
19072 expect(value.getText()).toContain('');
19073 expect(valid.getText()).toContain('myForm.input.$valid = false');
19074 });
19075 </file>
19076 </example>
19077 */
19078 'month': createDateInputType('month', MONTH_REGEXP,
19079 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
19080 'yyyy-MM'),
19081
19082 /**
19083 * @ngdoc input
19084 * @name input[number]
19085 *
19086 * @description
19087 * Text input with number validation and transformation. Sets the `number` validation
19088 * error if not a valid number.
19089 *
19090 * The model must always be a number, otherwise Angular will throw an error.
19091 *
19092 * @param {string} ngModel Assignable angular expression to data-bind to.
19093 * @param {string=} name Property name of the form under which the control is published.
19094 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
19095 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
19096 * @param {string=} required Sets `required` validation error key if the value is not entered.
19097 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19098 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19099 * `required` when you want to data-bind to the `required` attribute.
19100 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19101 * minlength.
19102 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19103 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19104 * any length.
19105 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19106 * that contains the regular expression body that will be converted to a regular expression
19107 * as in the ngPattern directive.
19108 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19109 * a RegExp found by evaluating the Angular expression given in the attribute value.
19110 * If the expression evaluates to a RegExp object then this is used directly.
19111 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19112 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19113 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19114 * interaction with the input element.
19115 *
19116 * @example
19117 <example name="number-input-directive" module="numberExample">
19118 <file name="index.html">
19119 <script>
19120 angular.module('numberExample', [])
19121 .controller('ExampleController', ['$scope', function($scope) {
19122 $scope.value = 12;
19123 }]);
19124 </script>
19125 <form name="myForm" ng-controller="ExampleController">
19126 Number: <input type="number" name="input" ng-model="value"
19127 min="0" max="99" required>
19128 <span class="error" ng-show="myForm.input.$error.required">
19129 Required!</span>
19130 <span class="error" ng-show="myForm.input.$error.number">
19131 Not valid number!</span>
19132 <tt>value = {{value}}</tt><br/>
19133 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19134 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19135 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19136 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19137 </form>
19138 </file>
19139 <file name="protractor.js" type="protractor">
19140 var value = element(by.binding('value'));
19141 var valid = element(by.binding('myForm.input.$valid'));
19142 var input = element(by.model('value'));
19143
19144 it('should initialize to model', function() {
19145 expect(value.getText()).toContain('12');
19146 expect(valid.getText()).toContain('true');
19147 });
19148
19149 it('should be invalid if empty', function() {
19150 input.clear();
19151 input.sendKeys('');
19152 expect(value.getText()).toEqual('value =');
19153 expect(valid.getText()).toContain('false');
19154 });
19155
19156 it('should be invalid if over max', function() {
19157 input.clear();
19158 input.sendKeys('123');
19159 expect(value.getText()).toEqual('value =');
19160 expect(valid.getText()).toContain('false');
19161 });
19162 </file>
19163 </example>
19164 */
19165 'number': numberInputType,
19166
19167
19168 /**
19169 * @ngdoc input
19170 * @name input[url]
19171 *
19172 * @description
19173 * Text input with URL validation. Sets the `url` validation error key if the content is not a
19174 * valid URL.
19175 *
19176 * <div class="alert alert-warning">
19177 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
19178 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
19179 * the built-in validators (see the {@link guide/forms Forms guide})
19180 * </div>
19181 *
19182 * @param {string} ngModel Assignable angular expression to data-bind to.
19183 * @param {string=} name Property name of the form under which the control is published.
19184 * @param {string=} required Sets `required` validation error key if the value is not entered.
19185 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19186 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19187 * `required` when you want to data-bind to the `required` attribute.
19188 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19189 * minlength.
19190 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19191 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19192 * any length.
19193 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19194 * that contains the regular expression body that will be converted to a regular expression
19195 * as in the ngPattern directive.
19196 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19197 * a RegExp found by evaluating the Angular expression given in the attribute value.
19198 * If the expression evaluates to a RegExp object then this is used directly.
19199 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19200 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19201 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19202 * interaction with the input element.
19203 *
19204 * @example
19205 <example name="url-input-directive" module="urlExample">
19206 <file name="index.html">
19207 <script>
19208 angular.module('urlExample', [])
19209 .controller('ExampleController', ['$scope', function($scope) {
19210 $scope.text = 'http://google.com';
19211 }]);
19212 </script>
19213 <form name="myForm" ng-controller="ExampleController">
19214 URL: <input type="url" name="input" ng-model="text" required>
19215 <span class="error" ng-show="myForm.input.$error.required">
19216 Required!</span>
19217 <span class="error" ng-show="myForm.input.$error.url">
19218 Not valid url!</span>
19219 <tt>text = {{text}}</tt><br/>
19220 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19221 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19222 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19223 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19224 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
19225 </form>
19226 </file>
19227 <file name="protractor.js" type="protractor">
19228 var text = element(by.binding('text'));
19229 var valid = element(by.binding('myForm.input.$valid'));
19230 var input = element(by.model('text'));
19231
19232 it('should initialize to model', function() {
19233 expect(text.getText()).toContain('http://google.com');
19234 expect(valid.getText()).toContain('true');
19235 });
19236
19237 it('should be invalid if empty', function() {
19238 input.clear();
19239 input.sendKeys('');
19240
19241 expect(text.getText()).toEqual('text =');
19242 expect(valid.getText()).toContain('false');
19243 });
19244
19245 it('should be invalid if not url', function() {
19246 input.clear();
19247 input.sendKeys('box');
19248
19249 expect(valid.getText()).toContain('false');
19250 });
19251 </file>
19252 </example>
19253 */
19254 'url': urlInputType,
19255
19256
19257 /**
19258 * @ngdoc input
19259 * @name input[email]
19260 *
19261 * @description
19262 * Text input with email validation. Sets the `email` validation error key if not a valid email
19263 * address.
19264 *
19265 * <div class="alert alert-warning">
19266 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
19267 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
19268 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
19269 * </div>
19270 *
19271 * @param {string} ngModel Assignable angular expression to data-bind to.
19272 * @param {string=} name Property name of the form under which the control is published.
19273 * @param {string=} required Sets `required` validation error key if the value is not entered.
19274 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19275 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19276 * `required` when you want to data-bind to the `required` attribute.
19277 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19278 * minlength.
19279 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19280 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
19281 * any length.
19282 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
19283 * that contains the regular expression body that will be converted to a regular expression
19284 * as in the ngPattern directive.
19285 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
19286 * a RegExp found by evaluating the Angular expression given in the attribute value.
19287 * If the expression evaluates to a RegExp object then this is used directly.
19288 * If the expression is a string then it will be converted to a RegExp after wrapping it in `^` and `$`
19289 * characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
19290 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19291 * interaction with the input element.
19292 *
19293 * @example
19294 <example name="email-input-directive" module="emailExample">
19295 <file name="index.html">
19296 <script>
19297 angular.module('emailExample', [])
19298 .controller('ExampleController', ['$scope', function($scope) {
19299 $scope.text = 'me@example.com';
19300 }]);
19301 </script>
19302 <form name="myForm" ng-controller="ExampleController">
19303 Email: <input type="email" name="input" ng-model="text" required>
19304 <span class="error" ng-show="myForm.input.$error.required">
19305 Required!</span>
19306 <span class="error" ng-show="myForm.input.$error.email">
19307 Not valid email!</span>
19308 <tt>text = {{text}}</tt><br/>
19309 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
19310 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
19311 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
19312 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
19313 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
19314 </form>
19315 </file>
19316 <file name="protractor.js" type="protractor">
19317 var text = element(by.binding('text'));
19318 var valid = element(by.binding('myForm.input.$valid'));
19319 var input = element(by.model('text'));
19320
19321 it('should initialize to model', function() {
19322 expect(text.getText()).toContain('me@example.com');
19323 expect(valid.getText()).toContain('true');
19324 });
19325
19326 it('should be invalid if empty', function() {
19327 input.clear();
19328 input.sendKeys('');
19329 expect(text.getText()).toEqual('text =');
19330 expect(valid.getText()).toContain('false');
19331 });
19332
19333 it('should be invalid if not email', function() {
19334 input.clear();
19335 input.sendKeys('xxx');
19336
19337 expect(valid.getText()).toContain('false');
19338 });
19339 </file>
19340 </example>
19341 */
19342 'email': emailInputType,
19343
19344
19345 /**
19346 * @ngdoc input
19347 * @name input[radio]
19348 *
19349 * @description
19350 * HTML radio button.
19351 *
19352 * @param {string} ngModel Assignable angular expression to data-bind to.
19353 * @param {string} value The value to which the expression should be set when selected.
19354 * @param {string=} name Property name of the form under which the control is published.
19355 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19356 * interaction with the input element.
19357 * @param {string} ngValue Angular expression which sets the value to which the expression should
19358 * be set when selected.
19359 *
19360 * @example
19361 <example name="radio-input-directive" module="radioExample">
19362 <file name="index.html">
19363 <script>
19364 angular.module('radioExample', [])
19365 .controller('ExampleController', ['$scope', function($scope) {
19366 $scope.color = 'blue';
19367 $scope.specialValue = {
19368 "id": "12345",
19369 "value": "green"
19370 };
19371 }]);
19372 </script>
19373 <form name="myForm" ng-controller="ExampleController">
19374 <input type="radio" ng-model="color" value="red"> Red <br/>
19375 <input type="radio" ng-model="color" ng-value="specialValue"> Green <br/>
19376 <input type="radio" ng-model="color" value="blue"> Blue <br/>
19377 <tt>color = {{color | json}}</tt><br/>
19378 </form>
19379 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
19380 </file>
19381 <file name="protractor.js" type="protractor">
19382 it('should change state', function() {
19383 var color = element(by.binding('color'));
19384
19385 expect(color.getText()).toContain('blue');
19386
19387 element.all(by.model('color')).get(0).click();
19388
19389 expect(color.getText()).toContain('red');
19390 });
19391 </file>
19392 </example>
19393 */
19394 'radio': radioInputType,
19395
19396
19397 /**
19398 * @ngdoc input
19399 * @name input[checkbox]
19400 *
19401 * @description
19402 * HTML checkbox.
19403 *
19404 * @param {string} ngModel Assignable angular expression to data-bind to.
19405 * @param {string=} name Property name of the form under which the control is published.
19406 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
19407 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
19408 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19409 * interaction with the input element.
19410 *
19411 * @example
19412 <example name="checkbox-input-directive" module="checkboxExample">
19413 <file name="index.html">
19414 <script>
19415 angular.module('checkboxExample', [])
19416 .controller('ExampleController', ['$scope', function($scope) {
19417 $scope.value1 = true;
19418 $scope.value2 = 'YES'
19419 }]);
19420 </script>
19421 <form name="myForm" ng-controller="ExampleController">
19422 Value1: <input type="checkbox" ng-model="value1"> <br/>
19423 Value2: <input type="checkbox" ng-model="value2"
19424 ng-true-value="'YES'" ng-false-value="'NO'"> <br/>
19425 <tt>value1 = {{value1}}</tt><br/>
19426 <tt>value2 = {{value2}}</tt><br/>
19427 </form>
19428 </file>
19429 <file name="protractor.js" type="protractor">
19430 it('should change state', function() {
19431 var value1 = element(by.binding('value1'));
19432 var value2 = element(by.binding('value2'));
19433
19434 expect(value1.getText()).toContain('true');
19435 expect(value2.getText()).toContain('YES');
19436
19437 element(by.model('value1')).click();
19438 element(by.model('value2')).click();
19439
19440 expect(value1.getText()).toContain('false');
19441 expect(value2.getText()).toContain('NO');
19442 });
19443 </file>
19444 </example>
19445 */
19446 'checkbox': checkboxInputType,
19447
19448 'hidden': noop,
19449 'button': noop,
19450 'submit': noop,
19451 'reset': noop,
19452 'file': noop
19453};
19454
19455function stringBasedInputType(ctrl) {
19456 ctrl.$formatters.push(function(value) {
19457 return ctrl.$isEmpty(value) ? value : value.toString();
19458 });
19459}
19460
19461function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19462 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19463 stringBasedInputType(ctrl);
19464}
19465
19466function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19467 var placeholder = element[0].placeholder, noevent = {};
19468 var type = lowercase(element[0].type);
19469
19470 // In composition mode, users are still inputing intermediate text buffer,
19471 // hold the listener until composition is done.
19472 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
19473 if (!$sniffer.android) {
19474 var composing = false;
19475
19476 element.on('compositionstart', function(data) {
19477 composing = true;
19478 });
19479
19480 element.on('compositionend', function() {
19481 composing = false;
19482 listener();
19483 });
19484 }
19485
19486 var listener = function(ev) {
19487 if (composing) return;
19488 var value = element.val(),
19489 event = ev && ev.type;
19490
19491 // IE (11 and under) seem to emit an 'input' event if the placeholder value changes.
19492 // We don't want to dirty the value when this happens, so we abort here. Unfortunately,
19493 // IE also sends input events for other non-input-related things, (such as focusing on a
19494 // form control), so this change is not entirely enough to solve this.
19495 if (msie && (ev || noevent).type === 'input' && element[0].placeholder !== placeholder) {
19496 placeholder = element[0].placeholder;
19497 return;
19498 }
19499
19500 // By default we will trim the value
19501 // If the attribute ng-trim exists we will avoid trimming
19502 // If input type is 'password', the value is never trimmed
19503 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
19504 value = trim(value);
19505 }
19506
19507 // If a control is suffering from bad input (due to native validators), browsers discard its
19508 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
19509 // control's value is the same empty value twice in a row.
19510 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
19511 ctrl.$setViewValue(value, event);
19512 }
19513 };
19514
19515 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
19516 // input event on backspace, delete or cut
19517 if ($sniffer.hasEvent('input')) {
19518 element.on('input', listener);
19519 } else {
19520 var timeout;
19521
19522 var deferListener = function(ev) {
19523 if (!timeout) {
19524 timeout = $browser.defer(function() {
19525 listener(ev);
19526 timeout = null;
19527 });
19528 }
19529 };
19530
19531 element.on('keydown', function(event) {
19532 var key = event.keyCode;
19533
19534 // ignore
19535 // command modifiers arrows
19536 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
19537
19538 deferListener(event);
19539 });
19540
19541 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
19542 if ($sniffer.hasEvent('paste')) {
19543 element.on('paste cut', deferListener);
19544 }
19545 }
19546
19547 // if user paste into input using mouse on older browser
19548 // or form autocomplete on newer browser, we need "change" event to catch it
19549 element.on('change', listener);
19550
19551 ctrl.$render = function() {
19552 element.val(ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue);
19553 };
19554}
19555
19556function weekParser(isoWeek, existingDate) {
19557 if (isDate(isoWeek)) {
19558 return isoWeek;
19559 }
19560
19561 if (isString(isoWeek)) {
19562 WEEK_REGEXP.lastIndex = 0;
19563 var parts = WEEK_REGEXP.exec(isoWeek);
19564 if (parts) {
19565 var year = +parts[1],
19566 week = +parts[2],
19567 hours = 0,
19568 minutes = 0,
19569 seconds = 0,
19570 milliseconds = 0,
19571 firstThurs = getFirstThursdayOfYear(year),
19572 addDays = (week - 1) * 7;
19573
19574 if (existingDate) {
19575 hours = existingDate.getHours();
19576 minutes = existingDate.getMinutes();
19577 seconds = existingDate.getSeconds();
19578 milliseconds = existingDate.getMilliseconds();
19579 }
19580
19581 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
19582 }
19583 }
19584
19585 return NaN;
19586}
19587
19588function createDateParser(regexp, mapping) {
19589 return function(iso, date) {
19590 var parts, map;
19591
19592 if (isDate(iso)) {
19593 return iso;
19594 }
19595
19596 if (isString(iso)) {
19597 // When a date is JSON'ified to wraps itself inside of an extra
19598 // set of double quotes. This makes the date parsing code unable
19599 // to match the date string and parse it as a date.
19600 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
19601 iso = iso.substring(1, iso.length - 1);
19602 }
19603 if (ISO_DATE_REGEXP.test(iso)) {
19604 return new Date(iso);
19605 }
19606 regexp.lastIndex = 0;
19607 parts = regexp.exec(iso);
19608
19609 if (parts) {
19610 parts.shift();
19611 if (date) {
19612 map = {
19613 yyyy: date.getFullYear(),
19614 MM: date.getMonth() + 1,
19615 dd: date.getDate(),
19616 HH: date.getHours(),
19617 mm: date.getMinutes(),
19618 ss: date.getSeconds(),
19619 sss: date.getMilliseconds() / 1000
19620 };
19621 } else {
19622 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
19623 }
19624
19625 forEach(parts, function(part, index) {
19626 if (index < mapping.length) {
19627 map[mapping[index]] = +part;
19628 }
19629 });
19630 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
19631 }
19632 }
19633
19634 return NaN;
19635 };
19636}
19637
19638function createDateInputType(type, regexp, parseDate, format) {
19639 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
19640 badInputChecker(scope, element, attr, ctrl);
19641 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19642 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
19643 var previousDate;
19644
19645 ctrl.$$parserName = type;
19646 ctrl.$parsers.push(function(value) {
19647 if (ctrl.$isEmpty(value)) return null;
19648 if (regexp.test(value)) {
19649 // Note: We cannot read ctrl.$modelValue, as there might be a different
19650 // parser/formatter in the processing chain so that the model
19651 // contains some different data format!
19652 var parsedDate = parseDate(value, previousDate);
19653 if (timezone === 'UTC') {
19654 parsedDate.setMinutes(parsedDate.getMinutes() - parsedDate.getTimezoneOffset());
19655 }
19656 return parsedDate;
19657 }
19658 return undefined;
19659 });
19660
19661 ctrl.$formatters.push(function(value) {
19662 if (value && !isDate(value)) {
19663 throw $ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
19664 }
19665 if (isValidDate(value)) {
19666 previousDate = value;
19667 if (previousDate && timezone === 'UTC') {
19668 var timezoneOffset = 60000 * previousDate.getTimezoneOffset();
19669 previousDate = new Date(previousDate.getTime() + timezoneOffset);
19670 }
19671 return $filter('date')(value, format, timezone);
19672 } else {
19673 previousDate = null;
19674 return '';
19675 }
19676 });
19677
19678 if (isDefined(attr.min) || attr.ngMin) {
19679 var minVal;
19680 ctrl.$validators.min = function(value) {
19681 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
19682 };
19683 attr.$observe('min', function(val) {
19684 minVal = parseObservedDateValue(val);
19685 ctrl.$validate();
19686 });
19687 }
19688
19689 if (isDefined(attr.max) || attr.ngMax) {
19690 var maxVal;
19691 ctrl.$validators.max = function(value) {
19692 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
19693 };
19694 attr.$observe('max', function(val) {
19695 maxVal = parseObservedDateValue(val);
19696 ctrl.$validate();
19697 });
19698 }
19699
19700 function isValidDate(value) {
19701 // Invalid Date: getTime() returns NaN
19702 return value && !(value.getTime && value.getTime() !== value.getTime());
19703 }
19704
19705 function parseObservedDateValue(val) {
19706 return isDefined(val) ? (isDate(val) ? val : parseDate(val)) : undefined;
19707 }
19708 };
19709}
19710
19711function badInputChecker(scope, element, attr, ctrl) {
19712 var node = element[0];
19713 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
19714 if (nativeValidation) {
19715 ctrl.$parsers.push(function(value) {
19716 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
19717 // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
19718 // - also sets validity.badInput (should only be validity.typeMismatch).
19719 // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
19720 // - can ignore this case as we can still read out the erroneous email...
19721 return validity.badInput && !validity.typeMismatch ? undefined : value;
19722 });
19723 }
19724}
19725
19726function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19727 badInputChecker(scope, element, attr, ctrl);
19728 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19729
19730 ctrl.$$parserName = 'number';
19731 ctrl.$parsers.push(function(value) {
19732 if (ctrl.$isEmpty(value)) return null;
19733 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
19734 return undefined;
19735 });
19736
19737 ctrl.$formatters.push(function(value) {
19738 if (!ctrl.$isEmpty(value)) {
19739 if (!isNumber(value)) {
19740 throw $ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
19741 }
19742 value = value.toString();
19743 }
19744 return value;
19745 });
19746
19747 if (attr.min || attr.ngMin) {
19748 var minVal;
19749 ctrl.$validators.min = function(value) {
19750 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
19751 };
19752
19753 attr.$observe('min', function(val) {
19754 if (isDefined(val) && !isNumber(val)) {
19755 val = parseFloat(val, 10);
19756 }
19757 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
19758 // TODO(matsko): implement validateLater to reduce number of validations
19759 ctrl.$validate();
19760 });
19761 }
19762
19763 if (attr.max || attr.ngMax) {
19764 var maxVal;
19765 ctrl.$validators.max = function(value) {
19766 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
19767 };
19768
19769 attr.$observe('max', function(val) {
19770 if (isDefined(val) && !isNumber(val)) {
19771 val = parseFloat(val, 10);
19772 }
19773 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
19774 // TODO(matsko): implement validateLater to reduce number of validations
19775 ctrl.$validate();
19776 });
19777 }
19778}
19779
19780function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19781 // Note: no badInputChecker here by purpose as `url` is only a validation
19782 // in browsers, i.e. we can always read out input.value even if it is not valid!
19783 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19784 stringBasedInputType(ctrl);
19785
19786 ctrl.$$parserName = 'url';
19787 ctrl.$validators.url = function(modelValue, viewValue) {
19788 var value = modelValue || viewValue;
19789 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
19790 };
19791}
19792
19793function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
19794 // Note: no badInputChecker here by purpose as `url` is only a validation
19795 // in browsers, i.e. we can always read out input.value even if it is not valid!
19796 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
19797 stringBasedInputType(ctrl);
19798
19799 ctrl.$$parserName = 'email';
19800 ctrl.$validators.email = function(modelValue, viewValue) {
19801 var value = modelValue || viewValue;
19802 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
19803 };
19804}
19805
19806function radioInputType(scope, element, attr, ctrl) {
19807 // make the name unique, if not defined
19808 if (isUndefined(attr.name)) {
19809 element.attr('name', nextUid());
19810 }
19811
19812 var listener = function(ev) {
19813 if (element[0].checked) {
19814 ctrl.$setViewValue(attr.value, ev && ev.type);
19815 }
19816 };
19817
19818 element.on('click', listener);
19819
19820 ctrl.$render = function() {
19821 var value = attr.value;
19822 element[0].checked = (value == ctrl.$viewValue);
19823 };
19824
19825 attr.$observe('value', ctrl.$render);
19826}
19827
19828function parseConstantExpr($parse, context, name, expression, fallback) {
19829 var parseFn;
19830 if (isDefined(expression)) {
19831 parseFn = $parse(expression);
19832 if (!parseFn.constant) {
19833 throw minErr('ngModel')('constexpr', 'Expected constant expression for `{0}`, but saw ' +
19834 '`{1}`.', name, expression);
19835 }
19836 return parseFn(context);
19837 }
19838 return fallback;
19839}
19840
19841function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
19842 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
19843 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
19844
19845 var listener = function(ev) {
19846 ctrl.$setViewValue(element[0].checked, ev && ev.type);
19847 };
19848
19849 element.on('click', listener);
19850
19851 ctrl.$render = function() {
19852 element[0].checked = ctrl.$viewValue;
19853 };
19854
19855 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
19856 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
19857 // it to a boolean.
19858 ctrl.$isEmpty = function(value) {
19859 return value === false;
19860 };
19861
19862 ctrl.$formatters.push(function(value) {
19863 return equals(value, trueValue);
19864 });
19865
19866 ctrl.$parsers.push(function(value) {
19867 return value ? trueValue : falseValue;
19868 });
19869}
19870
19871
19872/**
19873 * @ngdoc directive
19874 * @name textarea
19875 * @restrict E
19876 *
19877 * @description
19878 * HTML textarea element control with angular data-binding. The data-binding and validation
19879 * properties of this element are exactly the same as those of the
19880 * {@link ng.directive:input input element}.
19881 *
19882 * @param {string} ngModel Assignable angular expression to data-bind to.
19883 * @param {string=} name Property name of the form under which the control is published.
19884 * @param {string=} required Sets `required` validation error key if the value is not entered.
19885 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
19886 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
19887 * `required` when you want to data-bind to the `required` attribute.
19888 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19889 * minlength.
19890 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19891 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
19892 * length.
19893 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19894 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19895 * patterns defined as scope expressions.
19896 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19897 * interaction with the input element.
19898 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
19899 */
19900
19901
19902/**
19903 * @ngdoc directive
19904 * @name input
19905 * @restrict E
19906 *
19907 * @description
19908 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
19909 * input state control, and validation.
19910 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
19911 *
19912 * <div class="alert alert-warning">
19913 * **Note:** Not every feature offered is available for all input types.
19914 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
19915 * </div>
19916 *
19917 * @param {string} ngModel Assignable angular expression to data-bind to.
19918 * @param {string=} name Property name of the form under which the control is published.
19919 * @param {string=} required Sets `required` validation error key if the value is not entered.
19920 * @param {boolean=} ngRequired Sets `required` attribute if set to true
19921 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
19922 * minlength.
19923 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
19924 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
19925 * length.
19926 * @param {string=} ngPattern Sets `pattern` validation error key if the value does not match the
19927 * RegExp pattern expression. Expected value is `/regexp/` for inline patterns or `regexp` for
19928 * patterns defined as scope expressions.
19929 * @param {string=} ngChange Angular expression to be executed when input changes due to user
19930 * interaction with the input element.
19931 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
19932 * This parameter is ignored for input[type=password] controls, which will never trim the
19933 * input.
19934 *
19935 * @example
19936 <example name="input-directive" module="inputExample">
19937 <file name="index.html">
19938 <script>
19939 angular.module('inputExample', [])
19940 .controller('ExampleController', ['$scope', function($scope) {
19941 $scope.user = {name: 'guest', last: 'visitor'};
19942 }]);
19943 </script>
19944 <div ng-controller="ExampleController">
19945 <form name="myForm">
19946 User name: <input type="text" name="userName" ng-model="user.name" required>
19947 <span class="error" ng-show="myForm.userName.$error.required">
19948 Required!</span><br>
19949 Last name: <input type="text" name="lastName" ng-model="user.last"
19950 ng-minlength="3" ng-maxlength="10">
19951 <span class="error" ng-show="myForm.lastName.$error.minlength">
19952 Too short!</span>
19953 <span class="error" ng-show="myForm.lastName.$error.maxlength">
19954 Too long!</span><br>
19955 </form>
19956 <hr>
19957 <tt>user = {{user}}</tt><br/>
19958 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br>
19959 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br>
19960 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br>
19961 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br>
19962 <tt>myForm.$valid = {{myForm.$valid}}</tt><br>
19963 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br>
19964 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br>
19965 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br>
19966 </div>
19967 </file>
19968 <file name="protractor.js" type="protractor">
19969 var user = element(by.exactBinding('user'));
19970 var userNameValid = element(by.binding('myForm.userName.$valid'));
19971 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
19972 var lastNameError = element(by.binding('myForm.lastName.$error'));
19973 var formValid = element(by.binding('myForm.$valid'));
19974 var userNameInput = element(by.model('user.name'));
19975 var userLastInput = element(by.model('user.last'));
19976
19977 it('should initialize to model', function() {
19978 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
19979 expect(userNameValid.getText()).toContain('true');
19980 expect(formValid.getText()).toContain('true');
19981 });
19982
19983 it('should be invalid if empty when required', function() {
19984 userNameInput.clear();
19985 userNameInput.sendKeys('');
19986
19987 expect(user.getText()).toContain('{"last":"visitor"}');
19988 expect(userNameValid.getText()).toContain('false');
19989 expect(formValid.getText()).toContain('false');
19990 });
19991
19992 it('should be valid if empty when min length is set', function() {
19993 userLastInput.clear();
19994 userLastInput.sendKeys('');
19995
19996 expect(user.getText()).toContain('{"name":"guest","last":""}');
19997 expect(lastNameValid.getText()).toContain('true');
19998 expect(formValid.getText()).toContain('true');
19999 });
20000
20001 it('should be invalid if less than required min length', function() {
20002 userLastInput.clear();
20003 userLastInput.sendKeys('xx');
20004
20005 expect(user.getText()).toContain('{"name":"guest"}');
20006 expect(lastNameValid.getText()).toContain('false');
20007 expect(lastNameError.getText()).toContain('minlength');
20008 expect(formValid.getText()).toContain('false');
20009 });
20010
20011 it('should be invalid if longer than max length', function() {
20012 userLastInput.clear();
20013 userLastInput.sendKeys('some ridiculously long name');
20014
20015 expect(user.getText()).toContain('{"name":"guest"}');
20016 expect(lastNameValid.getText()).toContain('false');
20017 expect(lastNameError.getText()).toContain('maxlength');
20018 expect(formValid.getText()).toContain('false');
20019 });
20020 </file>
20021 </example>
20022 */
20023var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
20024 function($browser, $sniffer, $filter, $parse) {
20025 return {
20026 restrict: 'E',
20027 require: ['?ngModel'],
20028 link: {
20029 pre: function(scope, element, attr, ctrls) {
20030 if (ctrls[0]) {
20031 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
20032 $browser, $filter, $parse);
20033 }
20034 }
20035 }
20036 };
20037}];
20038
20039var VALID_CLASS = 'ng-valid',
20040 INVALID_CLASS = 'ng-invalid',
20041 PRISTINE_CLASS = 'ng-pristine',
20042 DIRTY_CLASS = 'ng-dirty',
20043 UNTOUCHED_CLASS = 'ng-untouched',
20044 TOUCHED_CLASS = 'ng-touched',
20045 PENDING_CLASS = 'ng-pending';
20046
20047/**
20048 * @ngdoc type
20049 * @name ngModel.NgModelController
20050 *
20051 * @property {string} $viewValue Actual string value in the view.
20052 * @property {*} $modelValue The value in the model that the control is bound to.
20053 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
20054 the control reads value from the DOM. The functions are called in array order, each passing
20055 its return value through to the next. The last return value is forwarded to the
20056 {@link ngModel.NgModelController#$validators `$validators`} collection.
20057
20058Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
20059`$viewValue`}.
20060
20061Returning `undefined` from a parser means a parse error occurred. In that case,
20062no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
20063will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
20064is set to `true`. The parse error is stored in `ngModel.$error.parse`.
20065
20066 *
20067 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
20068 the model value changes. The functions are called in reverse array order, each passing the value through to the
20069 next. The last return value is used as the actual DOM value.
20070 Used to format / convert values for display in the control.
20071 * ```js
20072 * function formatter(value) {
20073 * if (value) {
20074 * return value.toUpperCase();
20075 * }
20076 * }
20077 * ngModel.$formatters.push(formatter);
20078 * ```
20079 *
20080 * @property {Object.<string, function>} $validators A collection of validators that are applied
20081 * whenever the model value changes. The key value within the object refers to the name of the
20082 * validator while the function refers to the validation operation. The validation operation is
20083 * provided with the model value as an argument and must return a true or false value depending
20084 * on the response of that validation.
20085 *
20086 * ```js
20087 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
20088 * var value = modelValue || viewValue;
20089 * return /[0-9]+/.test(value) &&
20090 * /[a-z]+/.test(value) &&
20091 * /[A-Z]+/.test(value) &&
20092 * /\W+/.test(value);
20093 * };
20094 * ```
20095 *
20096 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
20097 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
20098 * is expected to return a promise when it is run during the model validation process. Once the promise
20099 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
20100 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
20101 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
20102 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
20103 * will only run once all synchronous validators have passed.
20104 *
20105 * Please note that if $http is used then it is important that the server returns a success HTTP response code
20106 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
20107 *
20108 * ```js
20109 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
20110 * var value = modelValue || viewValue;
20111 *
20112 * // Lookup user by username
20113 * return $http.get('/api/users/' + value).
20114 * then(function resolved() {
20115 * //username exists, this means validation fails
20116 * return $q.reject('exists');
20117 * }, function rejected() {
20118 * //username does not exist, therefore this validation passes
20119 * return true;
20120 * });
20121 * };
20122 * ```
20123 *
20124 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
20125 * view value has changed. It is called with no arguments, and its return value is ignored.
20126 * This can be used in place of additional $watches against the model value.
20127 *
20128 * @property {Object} $error An object hash with all failing validator ids as keys.
20129 * @property {Object} $pending An object hash with all pending validator ids as keys.
20130 *
20131 * @property {boolean} $untouched True if control has not lost focus yet.
20132 * @property {boolean} $touched True if control has lost focus.
20133 * @property {boolean} $pristine True if user has not interacted with the control yet.
20134 * @property {boolean} $dirty True if user has already interacted with the control.
20135 * @property {boolean} $valid True if there is no error.
20136 * @property {boolean} $invalid True if at least one error on the control.
20137 * @property {string} $name The name attribute of the control.
20138 *
20139 * @description
20140 *
20141 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
20142 * The controller contains services for data-binding, validation, CSS updates, and value formatting
20143 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
20144 * listening to DOM events.
20145 * Such DOM related logic should be provided by other directives which make use of
20146 * `NgModelController` for data-binding to control elements.
20147 * Angular provides this DOM logic for most {@link input `input`} elements.
20148 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
20149 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
20150 *
20151 * @example
20152 * ### Custom Control Example
20153 * This example shows how to use `NgModelController` with a custom control to achieve
20154 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
20155 * collaborate together to achieve the desired result.
20156 *
20157 * Note that `contenteditable` is an HTML5 attribute, which tells the browser to let the element
20158 * contents be edited in place by the user. This will not work on older browsers.
20159 *
20160 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
20161 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
20162 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
20163 * that content using the `$sce` service.
20164 *
20165 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
20166 <file name="style.css">
20167 [contenteditable] {
20168 border: 1px solid black;
20169 background-color: white;
20170 min-height: 20px;
20171 }
20172
20173 .ng-invalid {
20174 border: 1px solid red;
20175 }
20176
20177 </file>
20178 <file name="script.js">
20179 angular.module('customControl', ['ngSanitize']).
20180 directive('contenteditable', ['$sce', function($sce) {
20181 return {
20182 restrict: 'A', // only activate on element attribute
20183 require: '?ngModel', // get a hold of NgModelController
20184 link: function(scope, element, attrs, ngModel) {
20185 if (!ngModel) return; // do nothing if no ng-model
20186
20187 // Specify how UI should be updated
20188 ngModel.$render = function() {
20189 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
20190 };
20191
20192 // Listen for change events to enable binding
20193 element.on('blur keyup change', function() {
20194 scope.$evalAsync(read);
20195 });
20196 read(); // initialize
20197
20198 // Write data to the model
20199 function read() {
20200 var html = element.html();
20201 // When we clear the content editable the browser leaves a <br> behind
20202 // If strip-br attribute is provided then we strip this out
20203 if ( attrs.stripBr && html == '<br>' ) {
20204 html = '';
20205 }
20206 ngModel.$setViewValue(html);
20207 }
20208 }
20209 };
20210 }]);
20211 </file>
20212 <file name="index.html">
20213 <form name="myForm">
20214 <div contenteditable
20215 name="myWidget" ng-model="userContent"
20216 strip-br="true"
20217 required>Change me!</div>
20218 <span ng-show="myForm.myWidget.$error.required">Required!</span>
20219 <hr>
20220 <textarea ng-model="userContent"></textarea>
20221 </form>
20222 </file>
20223 <file name="protractor.js" type="protractor">
20224 it('should data-bind and become invalid', function() {
20225 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
20226 // SafariDriver can't handle contenteditable
20227 // and Firefox driver can't clear contenteditables very well
20228 return;
20229 }
20230 var contentEditable = element(by.css('[contenteditable]'));
20231 var content = 'Change me!';
20232
20233 expect(contentEditable.getText()).toEqual(content);
20234
20235 contentEditable.clear();
20236 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
20237 expect(contentEditable.getText()).toEqual('');
20238 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
20239 });
20240 </file>
20241 * </example>
20242 *
20243 *
20244 */
20245var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
20246 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
20247 this.$viewValue = Number.NaN;
20248 this.$modelValue = Number.NaN;
20249 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
20250 this.$validators = {};
20251 this.$asyncValidators = {};
20252 this.$parsers = [];
20253 this.$formatters = [];
20254 this.$viewChangeListeners = [];
20255 this.$untouched = true;
20256 this.$touched = false;
20257 this.$pristine = true;
20258 this.$dirty = false;
20259 this.$valid = true;
20260 this.$invalid = false;
20261 this.$error = {}; // keep invalid keys here
20262 this.$$success = {}; // keep valid keys here
20263 this.$pending = undefined; // keep pending keys here
20264 this.$name = $interpolate($attr.name || '', false)($scope);
20265
20266
20267 var parsedNgModel = $parse($attr.ngModel),
20268 parsedNgModelAssign = parsedNgModel.assign,
20269 ngModelGet = parsedNgModel,
20270 ngModelSet = parsedNgModelAssign,
20271 pendingDebounce = null,
20272 ctrl = this;
20273
20274 this.$$setOptions = function(options) {
20275 ctrl.$options = options;
20276 if (options && options.getterSetter) {
20277 var invokeModelGetter = $parse($attr.ngModel + '()'),
20278 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
20279
20280 ngModelGet = function($scope) {
20281 var modelValue = parsedNgModel($scope);
20282 if (isFunction(modelValue)) {
20283 modelValue = invokeModelGetter($scope);
20284 }
20285 return modelValue;
20286 };
20287 ngModelSet = function($scope, newValue) {
20288 if (isFunction(parsedNgModel($scope))) {
20289 invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
20290 } else {
20291 parsedNgModelAssign($scope, ctrl.$modelValue);
20292 }
20293 };
20294 } else if (!parsedNgModel.assign) {
20295 throw $ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
20296 $attr.ngModel, startingTag($element));
20297 }
20298 };
20299
20300 /**
20301 * @ngdoc method
20302 * @name ngModel.NgModelController#$render
20303 *
20304 * @description
20305 * Called when the view needs to be updated. It is expected that the user of the ng-model
20306 * directive will implement this method.
20307 *
20308 * The `$render()` method is invoked in the following situations:
20309 *
20310 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
20311 * committed value then `$render()` is called to update the input control.
20312 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
20313 * the `$viewValue` are different to last time.
20314 *
20315 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
20316 * `$modelValue` and `$viewValue` are actually different to their previous value. If `$modelValue`
20317 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
20318 * invoked if you only change a property on the objects.
20319 */
20320 this.$render = noop;
20321
20322 /**
20323 * @ngdoc method
20324 * @name ngModel.NgModelController#$isEmpty
20325 *
20326 * @description
20327 * This is called when we need to determine if the value of an input is empty.
20328 *
20329 * For instance, the required directive does this to work out if the input has data or not.
20330 *
20331 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
20332 *
20333 * You can override this for input directives whose concept of being empty is different to the
20334 * default. The `checkboxInputType` directive does this because in its case a value of `false`
20335 * implies empty.
20336 *
20337 * @param {*} value The value of the input to check for emptiness.
20338 * @returns {boolean} True if `value` is "empty".
20339 */
20340 this.$isEmpty = function(value) {
20341 return isUndefined(value) || value === '' || value === null || value !== value;
20342 };
20343
20344 var parentForm = $element.inheritedData('$formController') || nullFormCtrl,
20345 currentValidationRunId = 0;
20346
20347 /**
20348 * @ngdoc method
20349 * @name ngModel.NgModelController#$setValidity
20350 *
20351 * @description
20352 * Change the validity state, and notify the form.
20353 *
20354 * This method can be called within $parsers/$formatters or a custom validation implementation.
20355 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
20356 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
20357 *
20358 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
20359 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
20360 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
20361 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
20362 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
20363 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
20364 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
20365 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
20366 * Skipped is used by Angular when validators do not run because of parse errors and
20367 * when `$asyncValidators` do not run because any of the `$validators` failed.
20368 */
20369 addSetValidityMethod({
20370 ctrl: this,
20371 $element: $element,
20372 set: function(object, property) {
20373 object[property] = true;
20374 },
20375 unset: function(object, property) {
20376 delete object[property];
20377 },
20378 parentForm: parentForm,
20379 $animate: $animate
20380 });
20381
20382 /**
20383 * @ngdoc method
20384 * @name ngModel.NgModelController#$setPristine
20385 *
20386 * @description
20387 * Sets the control to its pristine state.
20388 *
20389 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
20390 * state (`ng-pristine` class). A model is considered to be pristine when the control
20391 * has not been changed from when first compiled.
20392 */
20393 this.$setPristine = function() {
20394 ctrl.$dirty = false;
20395 ctrl.$pristine = true;
20396 $animate.removeClass($element, DIRTY_CLASS);
20397 $animate.addClass($element, PRISTINE_CLASS);
20398 };
20399
20400 /**
20401 * @ngdoc method
20402 * @name ngModel.NgModelController#$setDirty
20403 *
20404 * @description
20405 * Sets the control to its dirty state.
20406 *
20407 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
20408 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
20409 * from when first compiled.
20410 */
20411 this.$setDirty = function() {
20412 ctrl.$dirty = true;
20413 ctrl.$pristine = false;
20414 $animate.removeClass($element, PRISTINE_CLASS);
20415 $animate.addClass($element, DIRTY_CLASS);
20416 parentForm.$setDirty();
20417 };
20418
20419 /**
20420 * @ngdoc method
20421 * @name ngModel.NgModelController#$setUntouched
20422 *
20423 * @description
20424 * Sets the control to its untouched state.
20425 *
20426 * This method can be called to remove the `ng-touched` class and set the control to its
20427 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
20428 * by default, however this function can be used to restore that state if the model has
20429 * already been touched by the user.
20430 */
20431 this.$setUntouched = function() {
20432 ctrl.$touched = false;
20433 ctrl.$untouched = true;
20434 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
20435 };
20436
20437 /**
20438 * @ngdoc method
20439 * @name ngModel.NgModelController#$setTouched
20440 *
20441 * @description
20442 * Sets the control to its touched state.
20443 *
20444 * This method can be called to remove the `ng-untouched` class and set the control to its
20445 * touched state (`ng-touched` class). A model is considered to be touched when the user has
20446 * first focused the control element and then shifted focus away from the control (blur event).
20447 */
20448 this.$setTouched = function() {
20449 ctrl.$touched = true;
20450 ctrl.$untouched = false;
20451 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
20452 };
20453
20454 /**
20455 * @ngdoc method
20456 * @name ngModel.NgModelController#$rollbackViewValue
20457 *
20458 * @description
20459 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
20460 * which may be caused by a pending debounced event or because the input is waiting for a some
20461 * future event.
20462 *
20463 * If you have an input that uses `ng-model-options` to set up debounced events or events such
20464 * as blur you can have a situation where there is a period when the `$viewValue`
20465 * is out of synch with the ngModel's `$modelValue`.
20466 *
20467 * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
20468 * programmatically before these debounced/future events have resolved/occurred, because Angular's
20469 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
20470 *
20471 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
20472 * input which may have such events pending. This is important in order to make sure that the
20473 * input field will be updated with the new model value and any pending operations are cancelled.
20474 *
20475 * <example name="ng-model-cancel-update" module="cancel-update-example">
20476 * <file name="app.js">
20477 * angular.module('cancel-update-example', [])
20478 *
20479 * .controller('CancelUpdateController', ['$scope', function($scope) {
20480 * $scope.resetWithCancel = function(e) {
20481 * if (e.keyCode == 27) {
20482 * $scope.myForm.myInput1.$rollbackViewValue();
20483 * $scope.myValue = '';
20484 * }
20485 * };
20486 * $scope.resetWithoutCancel = function(e) {
20487 * if (e.keyCode == 27) {
20488 * $scope.myValue = '';
20489 * }
20490 * };
20491 * }]);
20492 * </file>
20493 * <file name="index.html">
20494 * <div ng-controller="CancelUpdateController">
20495 * <p>Try typing something in each input. See that the model only updates when you
20496 * blur off the input.
20497 * </p>
20498 * <p>Now see what happens if you start typing then press the Escape key</p>
20499 *
20500 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
20501 * <p>With $rollbackViewValue()</p>
20502 * <input name="myInput1" ng-model="myValue" ng-keydown="resetWithCancel($event)"><br/>
20503 * myValue: "{{ myValue }}"
20504 *
20505 * <p>Without $rollbackViewValue()</p>
20506 * <input name="myInput2" ng-model="myValue" ng-keydown="resetWithoutCancel($event)"><br/>
20507 * myValue: "{{ myValue }}"
20508 * </form>
20509 * </div>
20510 * </file>
20511 * </example>
20512 */
20513 this.$rollbackViewValue = function() {
20514 $timeout.cancel(pendingDebounce);
20515 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
20516 ctrl.$render();
20517 };
20518
20519 /**
20520 * @ngdoc method
20521 * @name ngModel.NgModelController#$validate
20522 *
20523 * @description
20524 * Runs each of the registered validators (first synchronous validators and then
20525 * asynchronous validators).
20526 * If the validity changes to invalid, the model will be set to `undefined`,
20527 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
20528 * If the validity changes to valid, it will set the model to the last available valid
20529 * modelValue, i.e. either the last parsed value or the last value set from the scope.
20530 */
20531 this.$validate = function() {
20532 // ignore $validate before model is initialized
20533 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
20534 return;
20535 }
20536
20537 var viewValue = ctrl.$$lastCommittedViewValue;
20538 // Note: we use the $$rawModelValue as $modelValue might have been
20539 // set to undefined during a view -> model update that found validation
20540 // errors. We can't parse the view here, since that could change
20541 // the model although neither viewValue nor the model on the scope changed
20542 var modelValue = ctrl.$$rawModelValue;
20543
20544 // Check if the there's a parse error, so we don't unset it accidentially
20545 var parserName = ctrl.$$parserName || 'parse';
20546 var parserValid = ctrl.$error[parserName] ? false : undefined;
20547
20548 var prevValid = ctrl.$valid;
20549 var prevModelValue = ctrl.$modelValue;
20550
20551 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
20552
20553 ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
20554 // If there was no change in validity, don't update the model
20555 // This prevents changing an invalid modelValue to undefined
20556 if (!allowInvalid && prevValid !== allValid) {
20557 // Note: Don't check ctrl.$valid here, as we could have
20558 // external validators (e.g. calculated on the server),
20559 // that just call $setValidity and need the model value
20560 // to calculate their validity.
20561 ctrl.$modelValue = allValid ? modelValue : undefined;
20562
20563 if (ctrl.$modelValue !== prevModelValue) {
20564 ctrl.$$writeModelToScope();
20565 }
20566 }
20567 });
20568
20569 };
20570
20571 this.$$runValidators = function(parseValid, modelValue, viewValue, doneCallback) {
20572 currentValidationRunId++;
20573 var localValidationRunId = currentValidationRunId;
20574
20575 // check parser error
20576 if (!processParseErrors(parseValid)) {
20577 validationDone(false);
20578 return;
20579 }
20580 if (!processSyncValidators()) {
20581 validationDone(false);
20582 return;
20583 }
20584 processAsyncValidators();
20585
20586 function processParseErrors(parseValid) {
20587 var errorKey = ctrl.$$parserName || 'parse';
20588 if (parseValid === undefined) {
20589 setValidity(errorKey, null);
20590 } else {
20591 setValidity(errorKey, parseValid);
20592 if (!parseValid) {
20593 forEach(ctrl.$validators, function(v, name) {
20594 setValidity(name, null);
20595 });
20596 forEach(ctrl.$asyncValidators, function(v, name) {
20597 setValidity(name, null);
20598 });
20599 return false;
20600 }
20601 }
20602 return true;
20603 }
20604
20605 function processSyncValidators() {
20606 var syncValidatorsValid = true;
20607 forEach(ctrl.$validators, function(validator, name) {
20608 var result = validator(modelValue, viewValue);
20609 syncValidatorsValid = syncValidatorsValid && result;
20610 setValidity(name, result);
20611 });
20612 if (!syncValidatorsValid) {
20613 forEach(ctrl.$asyncValidators, function(v, name) {
20614 setValidity(name, null);
20615 });
20616 return false;
20617 }
20618 return true;
20619 }
20620
20621 function processAsyncValidators() {
20622 var validatorPromises = [];
20623 var allValid = true;
20624 forEach(ctrl.$asyncValidators, function(validator, name) {
20625 var promise = validator(modelValue, viewValue);
20626 if (!isPromiseLike(promise)) {
20627 throw $ngModelMinErr("$asyncValidators",
20628 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
20629 }
20630 setValidity(name, undefined);
20631 validatorPromises.push(promise.then(function() {
20632 setValidity(name, true);
20633 }, function(error) {
20634 allValid = false;
20635 setValidity(name, false);
20636 }));
20637 });
20638 if (!validatorPromises.length) {
20639 validationDone(true);
20640 } else {
20641 $q.all(validatorPromises).then(function() {
20642 validationDone(allValid);
20643 }, noop);
20644 }
20645 }
20646
20647 function setValidity(name, isValid) {
20648 if (localValidationRunId === currentValidationRunId) {
20649 ctrl.$setValidity(name, isValid);
20650 }
20651 }
20652
20653 function validationDone(allValid) {
20654 if (localValidationRunId === currentValidationRunId) {
20655
20656 doneCallback(allValid);
20657 }
20658 }
20659 };
20660
20661 /**
20662 * @ngdoc method
20663 * @name ngModel.NgModelController#$commitViewValue
20664 *
20665 * @description
20666 * Commit a pending update to the `$modelValue`.
20667 *
20668 * Updates may be pending by a debounced event or because the input is waiting for a some future
20669 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
20670 * usually handles calling this in response to input events.
20671 */
20672 this.$commitViewValue = function() {
20673 var viewValue = ctrl.$viewValue;
20674
20675 $timeout.cancel(pendingDebounce);
20676
20677 // If the view value has not changed then we should just exit, except in the case where there is
20678 // a native validator on the element. In this case the validation state may have changed even though
20679 // the viewValue has stayed empty.
20680 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
20681 return;
20682 }
20683 ctrl.$$lastCommittedViewValue = viewValue;
20684
20685 // change to dirty
20686 if (ctrl.$pristine) {
20687 this.$setDirty();
20688 }
20689 this.$$parseAndValidate();
20690 };
20691
20692 this.$$parseAndValidate = function() {
20693 var viewValue = ctrl.$$lastCommittedViewValue;
20694 var modelValue = viewValue;
20695 var parserValid = isUndefined(modelValue) ? undefined : true;
20696
20697 if (parserValid) {
20698 for (var i = 0; i < ctrl.$parsers.length; i++) {
20699 modelValue = ctrl.$parsers[i](modelValue);
20700 if (isUndefined(modelValue)) {
20701 parserValid = false;
20702 break;
20703 }
20704 }
20705 }
20706 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
20707 // ctrl.$modelValue has not been touched yet...
20708 ctrl.$modelValue = ngModelGet($scope);
20709 }
20710 var prevModelValue = ctrl.$modelValue;
20711 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
20712 ctrl.$$rawModelValue = modelValue;
20713 if (allowInvalid) {
20714 ctrl.$modelValue = modelValue;
20715 writeToModelIfNeeded();
20716 }
20717 ctrl.$$runValidators(parserValid, modelValue, viewValue, function(allValid) {
20718 if (!allowInvalid) {
20719 // Note: Don't check ctrl.$valid here, as we could have
20720 // external validators (e.g. calculated on the server),
20721 // that just call $setValidity and need the model value
20722 // to calculate their validity.
20723 ctrl.$modelValue = allValid ? modelValue : undefined;
20724 writeToModelIfNeeded();
20725 }
20726 });
20727
20728 function writeToModelIfNeeded() {
20729 if (ctrl.$modelValue !== prevModelValue) {
20730 ctrl.$$writeModelToScope();
20731 }
20732 }
20733 };
20734
20735 this.$$writeModelToScope = function() {
20736 ngModelSet($scope, ctrl.$modelValue);
20737 forEach(ctrl.$viewChangeListeners, function(listener) {
20738 try {
20739 listener();
20740 } catch (e) {
20741 $exceptionHandler(e);
20742 }
20743 });
20744 };
20745
20746 /**
20747 * @ngdoc method
20748 * @name ngModel.NgModelController#$setViewValue
20749 *
20750 * @description
20751 * Update the view value.
20752 *
20753 * This method should be called when an input directive want to change the view value; typically,
20754 * this is done from within a DOM event handler.
20755 *
20756 * For example {@link ng.directive:input input} calls it when the value of the input changes and
20757 * {@link ng.directive:select select} calls it when an option is selected.
20758 *
20759 * If the new `value` is an object (rather than a string or a number), we should make a copy of the
20760 * object before passing it to `$setViewValue`. This is because `ngModel` does not perform a deep
20761 * watch of objects, it only looks for a change of identity. If you only change the property of
20762 * the object then ngModel will not realise that the object has changed and will not invoke the
20763 * `$parsers` and `$validators` pipelines.
20764 *
20765 * For this reason, you should not change properties of the copy once it has been passed to
20766 * `$setViewValue`. Otherwise you may cause the model value on the scope to change incorrectly.
20767 *
20768 * When this method is called, the new `value` will be staged for committing through the `$parsers`
20769 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
20770 * value sent directly for processing, finally to be applied to `$modelValue` and then the
20771 * **expression** specified in the `ng-model` attribute.
20772 *
20773 * Lastly, all the registered change listeners, in the `$viewChangeListeners` list, are called.
20774 *
20775 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
20776 * and the `default` trigger is not listed, all those actions will remain pending until one of the
20777 * `updateOn` events is triggered on the DOM element.
20778 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
20779 * directive is used with a custom debounce for this particular event.
20780 *
20781 * Note that calling this function does not trigger a `$digest`.
20782 *
20783 * @param {string} value Value from the view.
20784 * @param {string} trigger Event that triggered the update.
20785 */
20786 this.$setViewValue = function(value, trigger) {
20787 ctrl.$viewValue = value;
20788 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
20789 ctrl.$$debounceViewValueCommit(trigger);
20790 }
20791 };
20792
20793 this.$$debounceViewValueCommit = function(trigger) {
20794 var debounceDelay = 0,
20795 options = ctrl.$options,
20796 debounce;
20797
20798 if (options && isDefined(options.debounce)) {
20799 debounce = options.debounce;
20800 if (isNumber(debounce)) {
20801 debounceDelay = debounce;
20802 } else if (isNumber(debounce[trigger])) {
20803 debounceDelay = debounce[trigger];
20804 } else if (isNumber(debounce['default'])) {
20805 debounceDelay = debounce['default'];
20806 }
20807 }
20808
20809 $timeout.cancel(pendingDebounce);
20810 if (debounceDelay) {
20811 pendingDebounce = $timeout(function() {
20812 ctrl.$commitViewValue();
20813 }, debounceDelay);
20814 } else if ($rootScope.$$phase) {
20815 ctrl.$commitViewValue();
20816 } else {
20817 $scope.$apply(function() {
20818 ctrl.$commitViewValue();
20819 });
20820 }
20821 };
20822
20823 // model -> value
20824 // Note: we cannot use a normal scope.$watch as we want to detect the following:
20825 // 1. scope value is 'a'
20826 // 2. user enters 'b'
20827 // 3. ng-change kicks in and reverts scope value to 'a'
20828 // -> scope value did not change since the last digest as
20829 // ng-change executes in apply phase
20830 // 4. view should be changed back to 'a'
20831 $scope.$watch(function ngModelWatch() {
20832 var modelValue = ngModelGet($scope);
20833
20834 // if scope model value and ngModel value are out of sync
20835 // TODO(perf): why not move this to the action fn?
20836 if (modelValue !== ctrl.$modelValue) {
20837 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
20838
20839 var formatters = ctrl.$formatters,
20840 idx = formatters.length;
20841
20842 var viewValue = modelValue;
20843 while (idx--) {
20844 viewValue = formatters[idx](viewValue);
20845 }
20846 if (ctrl.$viewValue !== viewValue) {
20847 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
20848 ctrl.$render();
20849
20850 ctrl.$$runValidators(undefined, modelValue, viewValue, noop);
20851 }
20852 }
20853
20854 return modelValue;
20855 });
20856}];
20857
20858
20859/**
20860 * @ngdoc directive
20861 * @name ngModel
20862 *
20863 * @element input
20864 * @priority 1
20865 *
20866 * @description
20867 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
20868 * property on the scope using {@link ngModel.NgModelController NgModelController},
20869 * which is created and exposed by this directive.
20870 *
20871 * `ngModel` is responsible for:
20872 *
20873 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
20874 * require.
20875 * - Providing validation behavior (i.e. required, number, email, url).
20876 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
20877 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
20878 * - Registering the control with its parent {@link ng.directive:form form}.
20879 *
20880 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
20881 * current scope. If the property doesn't already exist on this scope, it will be created
20882 * implicitly and added to the scope.
20883 *
20884 * For best practices on using `ngModel`, see:
20885 *
20886 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
20887 *
20888 * For basic examples, how to use `ngModel`, see:
20889 *
20890 * - {@link ng.directive:input input}
20891 * - {@link input[text] text}
20892 * - {@link input[checkbox] checkbox}
20893 * - {@link input[radio] radio}
20894 * - {@link input[number] number}
20895 * - {@link input[email] email}
20896 * - {@link input[url] url}
20897 * - {@link input[date] date}
20898 * - {@link input[datetime-local] datetime-local}
20899 * - {@link input[time] time}
20900 * - {@link input[month] month}
20901 * - {@link input[week] week}
20902 * - {@link ng.directive:select select}
20903 * - {@link ng.directive:textarea textarea}
20904 *
20905 * # CSS classes
20906 * The following CSS classes are added and removed on the associated input/select/textarea element
20907 * depending on the validity of the model.
20908 *
20909 * - `ng-valid`: the model is valid
20910 * - `ng-invalid`: the model is invalid
20911 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
20912 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
20913 * - `ng-pristine`: the control hasn't been interacted with yet
20914 * - `ng-dirty`: the control has been interacted with
20915 * - `ng-touched`: the control has been blurred
20916 * - `ng-untouched`: the control hasn't been blurred
20917 * - `ng-pending`: any `$asyncValidators` are unfulfilled
20918 *
20919 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20920 *
20921 * ## Animation Hooks
20922 *
20923 * Animations within models are triggered when any of the associated CSS classes are added and removed
20924 * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
20925 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
20926 * The animations that are triggered within ngModel are similar to how they work in ngClass and
20927 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
20928 *
20929 * The following example shows a simple way to utilize CSS transitions to style an input element
20930 * that has been rendered as invalid after it has been validated:
20931 *
20932 * <pre>
20933 * //be sure to include ngAnimate as a module to hook into more
20934 * //advanced animations
20935 * .my-input {
20936 * transition:0.5s linear all;
20937 * background: white;
20938 * }
20939 * .my-input.ng-invalid {
20940 * background: red;
20941 * color:white;
20942 * }
20943 * </pre>
20944 *
20945 * @example
20946 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
20947 <file name="index.html">
20948 <script>
20949 angular.module('inputExample', [])
20950 .controller('ExampleController', ['$scope', function($scope) {
20951 $scope.val = '1';
20952 }]);
20953 </script>
20954 <style>
20955 .my-input {
20956 -webkit-transition:all linear 0.5s;
20957 transition:all linear 0.5s;
20958 background: transparent;
20959 }
20960 .my-input.ng-invalid {
20961 color:white;
20962 background: red;
20963 }
20964 </style>
20965 Update input to see transitions when valid/invalid.
20966 Integer is a valid value.
20967 <form name="testForm" ng-controller="ExampleController">
20968 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input" />
20969 </form>
20970 </file>
20971 * </example>
20972 *
20973 * ## Binding to a getter/setter
20974 *
20975 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
20976 * function that returns a representation of the model when called with zero arguments, and sets
20977 * the internal state of a model when called with an argument. It's sometimes useful to use this
20978 * for models that have an internal representation that's different than what the model exposes
20979 * to the view.
20980 *
20981 * <div class="alert alert-success">
20982 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
20983 * frequently than other parts of your code.
20984 * </div>
20985 *
20986 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
20987 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
20988 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
20989 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
20990 *
20991 * The following example shows how to use `ngModel` with a getter/setter:
20992 *
20993 * @example
20994 * <example name="ngModel-getter-setter" module="getterSetterExample">
20995 <file name="index.html">
20996 <div ng-controller="ExampleController">
20997 <form name="userForm">
20998 Name:
20999 <input type="text" name="userName"
21000 ng-model="user.name"
21001 ng-model-options="{ getterSetter: true }" />
21002 </form>
21003 <pre>user.name = <span ng-bind="user.name()"></span></pre>
21004 </div>
21005 </file>
21006 <file name="app.js">
21007 angular.module('getterSetterExample', [])
21008 .controller('ExampleController', ['$scope', function($scope) {
21009 var _name = 'Brian';
21010 $scope.user = {
21011 name: function(newName) {
21012 if (angular.isDefined(newName)) {
21013 _name = newName;
21014 }
21015 return _name;
21016 }
21017 };
21018 }]);
21019 </file>
21020 * </example>
21021 */
21022var ngModelDirective = ['$rootScope', function($rootScope) {
21023 return {
21024 restrict: 'A',
21025 require: ['ngModel', '^?form', '^?ngModelOptions'],
21026 controller: NgModelController,
21027 // Prelink needs to run before any input directive
21028 // so that we can set the NgModelOptions in NgModelController
21029 // before anyone else uses it.
21030 priority: 1,
21031 compile: function ngModelCompile(element) {
21032 // Setup initial state of the control
21033 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
21034
21035 return {
21036 pre: function ngModelPreLink(scope, element, attr, ctrls) {
21037 var modelCtrl = ctrls[0],
21038 formCtrl = ctrls[1] || nullFormCtrl;
21039
21040 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
21041
21042 // notify others, especially parent forms
21043 formCtrl.$addControl(modelCtrl);
21044
21045 attr.$observe('name', function(newValue) {
21046 if (modelCtrl.$name !== newValue) {
21047 formCtrl.$$renameControl(modelCtrl, newValue);
21048 }
21049 });
21050
21051 scope.$on('$destroy', function() {
21052 formCtrl.$removeControl(modelCtrl);
21053 });
21054 },
21055 post: function ngModelPostLink(scope, element, attr, ctrls) {
21056 var modelCtrl = ctrls[0];
21057 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
21058 element.on(modelCtrl.$options.updateOn, function(ev) {
21059 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
21060 });
21061 }
21062
21063 element.on('blur', function(ev) {
21064 if (modelCtrl.$touched) return;
21065
21066 if ($rootScope.$$phase) {
21067 scope.$evalAsync(modelCtrl.$setTouched);
21068 } else {
21069 scope.$apply(modelCtrl.$setTouched);
21070 }
21071 });
21072 }
21073 };
21074 }
21075 };
21076}];
21077
21078
21079/**
21080 * @ngdoc directive
21081 * @name ngChange
21082 *
21083 * @description
21084 * Evaluate the given expression when the user changes the input.
21085 * The expression is evaluated immediately, unlike the JavaScript onchange event
21086 * which only triggers at the end of a change (usually, when the user leaves the
21087 * form element or presses the return key).
21088 *
21089 * The `ngChange` expression is only evaluated when a change in the input value causes
21090 * a new value to be committed to the model.
21091 *
21092 * It will not be evaluated:
21093 * * if the value returned from the `$parsers` transformation pipeline has not changed
21094 * * if the input has continued to be invalid since the model will stay `null`
21095 * * if the model is changed programmatically and not by a change to the input value
21096 *
21097 *
21098 * Note, this directive requires `ngModel` to be present.
21099 *
21100 * @element input
21101 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
21102 * in input value.
21103 *
21104 * @example
21105 * <example name="ngChange-directive" module="changeExample">
21106 * <file name="index.html">
21107 * <script>
21108 * angular.module('changeExample', [])
21109 * .controller('ExampleController', ['$scope', function($scope) {
21110 * $scope.counter = 0;
21111 * $scope.change = function() {
21112 * $scope.counter++;
21113 * };
21114 * }]);
21115 * </script>
21116 * <div ng-controller="ExampleController">
21117 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
21118 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
21119 * <label for="ng-change-example2">Confirmed</label><br />
21120 * <tt>debug = {{confirmed}}</tt><br/>
21121 * <tt>counter = {{counter}}</tt><br/>
21122 * </div>
21123 * </file>
21124 * <file name="protractor.js" type="protractor">
21125 * var counter = element(by.binding('counter'));
21126 * var debug = element(by.binding('confirmed'));
21127 *
21128 * it('should evaluate the expression if changing from view', function() {
21129 * expect(counter.getText()).toContain('0');
21130 *
21131 * element(by.id('ng-change-example1')).click();
21132 *
21133 * expect(counter.getText()).toContain('1');
21134 * expect(debug.getText()).toContain('true');
21135 * });
21136 *
21137 * it('should not evaluate the expression if changing from model', function() {
21138 * element(by.id('ng-change-example2')).click();
21139
21140 * expect(counter.getText()).toContain('0');
21141 * expect(debug.getText()).toContain('true');
21142 * });
21143 * </file>
21144 * </example>
21145 */
21146var ngChangeDirective = valueFn({
21147 restrict: 'A',
21148 require: 'ngModel',
21149 link: function(scope, element, attr, ctrl) {
21150 ctrl.$viewChangeListeners.push(function() {
21151 scope.$eval(attr.ngChange);
21152 });
21153 }
21154});
21155
21156
21157var requiredDirective = function() {
21158 return {
21159 restrict: 'A',
21160 require: '?ngModel',
21161 link: function(scope, elm, attr, ctrl) {
21162 if (!ctrl) return;
21163 attr.required = true; // force truthy in case we are on non input element
21164
21165 ctrl.$validators.required = function(modelValue, viewValue) {
21166 return !attr.required || !ctrl.$isEmpty(viewValue);
21167 };
21168
21169 attr.$observe('required', function() {
21170 ctrl.$validate();
21171 });
21172 }
21173 };
21174};
21175
21176
21177var patternDirective = function() {
21178 return {
21179 restrict: 'A',
21180 require: '?ngModel',
21181 link: function(scope, elm, attr, ctrl) {
21182 if (!ctrl) return;
21183
21184 var regexp, patternExp = attr.ngPattern || attr.pattern;
21185 attr.$observe('pattern', function(regex) {
21186 if (isString(regex) && regex.length > 0) {
21187 regex = new RegExp('^' + regex + '$');
21188 }
21189
21190 if (regex && !regex.test) {
21191 throw minErr('ngPattern')('noregexp',
21192 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
21193 regex, startingTag(elm));
21194 }
21195
21196 regexp = regex || undefined;
21197 ctrl.$validate();
21198 });
21199
21200 ctrl.$validators.pattern = function(value) {
21201 return ctrl.$isEmpty(value) || isUndefined(regexp) || regexp.test(value);
21202 };
21203 }
21204 };
21205};
21206
21207
21208var maxlengthDirective = function() {
21209 return {
21210 restrict: 'A',
21211 require: '?ngModel',
21212 link: function(scope, elm, attr, ctrl) {
21213 if (!ctrl) return;
21214
21215 var maxlength = -1;
21216 attr.$observe('maxlength', function(value) {
21217 var intVal = int(value);
21218 maxlength = isNaN(intVal) ? -1 : intVal;
21219 ctrl.$validate();
21220 });
21221 ctrl.$validators.maxlength = function(modelValue, viewValue) {
21222 return (maxlength < 0) || ctrl.$isEmpty(modelValue) || (viewValue.length <= maxlength);
21223 };
21224 }
21225 };
21226};
21227
21228var minlengthDirective = function() {
21229 return {
21230 restrict: 'A',
21231 require: '?ngModel',
21232 link: function(scope, elm, attr, ctrl) {
21233 if (!ctrl) return;
21234
21235 var minlength = 0;
21236 attr.$observe('minlength', function(value) {
21237 minlength = int(value) || 0;
21238 ctrl.$validate();
21239 });
21240 ctrl.$validators.minlength = function(modelValue, viewValue) {
21241 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
21242 };
21243 }
21244 };
21245};
21246
21247
21248/**
21249 * @ngdoc directive
21250 * @name ngList
21251 *
21252 * @description
21253 * Text input that converts between a delimited string and an array of strings. The default
21254 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
21255 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
21256 *
21257 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
21258 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
21259 * list item is respected. This implies that the user of the directive is responsible for
21260 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
21261 * tab or newline character.
21262 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
21263 * when joining the list items back together) and whitespace around each list item is stripped
21264 * before it is added to the model.
21265 *
21266 * ### Example with Validation
21267 *
21268 * <example name="ngList-directive" module="listExample">
21269 * <file name="app.js">
21270 * angular.module('listExample', [])
21271 * .controller('ExampleController', ['$scope', function($scope) {
21272 * $scope.names = ['morpheus', 'neo', 'trinity'];
21273 * }]);
21274 * </file>
21275 * <file name="index.html">
21276 * <form name="myForm" ng-controller="ExampleController">
21277 * List: <input name="namesInput" ng-model="names" ng-list required>
21278 * <span class="error" ng-show="myForm.namesInput.$error.required">
21279 * Required!</span>
21280 * <br>
21281 * <tt>names = {{names}}</tt><br/>
21282 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
21283 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
21284 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21285 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21286 * </form>
21287 * </file>
21288 * <file name="protractor.js" type="protractor">
21289 * var listInput = element(by.model('names'));
21290 * var names = element(by.exactBinding('names'));
21291 * var valid = element(by.binding('myForm.namesInput.$valid'));
21292 * var error = element(by.css('span.error'));
21293 *
21294 * it('should initialize to model', function() {
21295 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
21296 * expect(valid.getText()).toContain('true');
21297 * expect(error.getCssValue('display')).toBe('none');
21298 * });
21299 *
21300 * it('should be invalid if empty', function() {
21301 * listInput.clear();
21302 * listInput.sendKeys('');
21303 *
21304 * expect(names.getText()).toContain('');
21305 * expect(valid.getText()).toContain('false');
21306 * expect(error.getCssValue('display')).not.toBe('none');
21307 * });
21308 * </file>
21309 * </example>
21310 *
21311 * ### Example - splitting on whitespace
21312 * <example name="ngList-directive-newlines">
21313 * <file name="index.html">
21314 * <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
21315 * <pre>{{ list | json }}</pre>
21316 * </file>
21317 * <file name="protractor.js" type="protractor">
21318 * it("should split the text by newlines", function() {
21319 * var listInput = element(by.model('list'));
21320 * var output = element(by.binding('list | json'));
21321 * listInput.sendKeys('abc\ndef\nghi');
21322 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
21323 * });
21324 * </file>
21325 * </example>
21326 *
21327 * @element input
21328 * @param {string=} ngList optional delimiter that should be used to split the value.
21329 */
21330var ngListDirective = function() {
21331 return {
21332 restrict: 'A',
21333 priority: 100,
21334 require: 'ngModel',
21335 link: function(scope, element, attr, ctrl) {
21336 // We want to control whitespace trimming so we use this convoluted approach
21337 // to access the ngList attribute, which doesn't pre-trim the attribute
21338 var ngList = element.attr(attr.$attr.ngList) || ', ';
21339 var trimValues = attr.ngTrim !== 'false';
21340 var separator = trimValues ? trim(ngList) : ngList;
21341
21342 var parse = function(viewValue) {
21343 // If the viewValue is invalid (say required but empty) it will be `undefined`
21344 if (isUndefined(viewValue)) return;
21345
21346 var list = [];
21347
21348 if (viewValue) {
21349 forEach(viewValue.split(separator), function(value) {
21350 if (value) list.push(trimValues ? trim(value) : value);
21351 });
21352 }
21353
21354 return list;
21355 };
21356
21357 ctrl.$parsers.push(parse);
21358 ctrl.$formatters.push(function(value) {
21359 if (isArray(value)) {
21360 return value.join(ngList);
21361 }
21362
21363 return undefined;
21364 });
21365
21366 // Override the standard $isEmpty because an empty array means the input is empty.
21367 ctrl.$isEmpty = function(value) {
21368 return !value || !value.length;
21369 };
21370 }
21371 };
21372};
21373
21374
21375var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
21376/**
21377 * @ngdoc directive
21378 * @name ngValue
21379 *
21380 * @description
21381 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
21382 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
21383 * the bound value.
21384 *
21385 * `ngValue` is useful when dynamically generating lists of radio buttons using
21386 * {@link ngRepeat `ngRepeat`}, as shown below.
21387 *
21388 * Likewise, `ngValue` can be used to generate `<option>` elements for
21389 * the {@link select `select`} element. In that case however, only strings are supported
21390 * for the `value `attribute, so the resulting `ngModel` will always be a string.
21391 * Support for `select` models with non-string values is available via `ngOptions`.
21392 *
21393 * @element input
21394 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
21395 * of the `input` element
21396 *
21397 * @example
21398 <example name="ngValue-directive" module="valueExample">
21399 <file name="index.html">
21400 <script>
21401 angular.module('valueExample', [])
21402 .controller('ExampleController', ['$scope', function($scope) {
21403 $scope.names = ['pizza', 'unicorns', 'robots'];
21404 $scope.my = { favorite: 'unicorns' };
21405 }]);
21406 </script>
21407 <form ng-controller="ExampleController">
21408 <h2>Which is your favorite?</h2>
21409 <label ng-repeat="name in names" for="{{name}}">
21410 {{name}}
21411 <input type="radio"
21412 ng-model="my.favorite"
21413 ng-value="name"
21414 id="{{name}}"
21415 name="favorite">
21416 </label>
21417 <div>You chose {{my.favorite}}</div>
21418 </form>
21419 </file>
21420 <file name="protractor.js" type="protractor">
21421 var favorite = element(by.binding('my.favorite'));
21422
21423 it('should initialize to model', function() {
21424 expect(favorite.getText()).toContain('unicorns');
21425 });
21426 it('should bind the values to the inputs', function() {
21427 element.all(by.model('my.favorite')).get(0).click();
21428 expect(favorite.getText()).toContain('pizza');
21429 });
21430 </file>
21431 </example>
21432 */
21433var ngValueDirective = function() {
21434 return {
21435 restrict: 'A',
21436 priority: 100,
21437 compile: function(tpl, tplAttr) {
21438 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
21439 return function ngValueConstantLink(scope, elm, attr) {
21440 attr.$set('value', scope.$eval(attr.ngValue));
21441 };
21442 } else {
21443 return function ngValueLink(scope, elm, attr) {
21444 scope.$watch(attr.ngValue, function valueWatchAction(value) {
21445 attr.$set('value', value);
21446 });
21447 };
21448 }
21449 }
21450 };
21451};
21452
21453/**
21454 * @ngdoc directive
21455 * @name ngModelOptions
21456 *
21457 * @description
21458 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
21459 * events that will trigger a model update and/or a debouncing delay so that the actual update only
21460 * takes place when a timer expires; this timer will be reset after another change takes place.
21461 *
21462 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
21463 * be different than the value in the actual model. This means that if you update the model you
21464 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
21465 * order to make sure it is synchronized with the model and that any debounced action is canceled.
21466 *
21467 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
21468 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
21469 * important because `form` controllers are published to the related scope under the name in their
21470 * `name` attribute.
21471 *
21472 * Any pending changes will take place immediately when an enclosing form is submitted via the
21473 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
21474 * to have access to the updated model.
21475 *
21476 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
21477 *
21478 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
21479 * - `updateOn`: string specifying which event should the input be bound to. You can set several
21480 * events using an space delimited list. There is a special event called `default` that
21481 * matches the default events belonging of the control.
21482 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
21483 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
21484 * custom value for each event. For example:
21485 * `ng-model-options="{ updateOn: 'default blur', debounce: {'default': 500, 'blur': 0} }"`
21486 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
21487 * not validate correctly instead of the default behavior of setting the model to undefined.
21488 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
21489 `ngModel` as getters/setters.
21490 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
21491 * `<input type="date">`, `<input type="time">`, ... . Right now, the only supported value is `'UTC'`,
21492 * otherwise the default timezone of the browser will be used.
21493 *
21494 * @example
21495
21496 The following example shows how to override immediate updates. Changes on the inputs within the
21497 form will update the model only when the control loses focus (blur event). If `escape` key is
21498 pressed while the input field is focused, the value is reset to the value in the current model.
21499
21500 <example name="ngModelOptions-directive-blur" module="optionsExample">
21501 <file name="index.html">
21502 <div ng-controller="ExampleController">
21503 <form name="userForm">
21504 Name:
21505 <input type="text" name="userName"
21506 ng-model="user.name"
21507 ng-model-options="{ updateOn: 'blur' }"
21508 ng-keyup="cancel($event)" /><br />
21509
21510 Other data:
21511 <input type="text" ng-model="user.data" /><br />
21512 </form>
21513 <pre>user.name = <span ng-bind="user.name"></span></pre>
21514 </div>
21515 </file>
21516 <file name="app.js">
21517 angular.module('optionsExample', [])
21518 .controller('ExampleController', ['$scope', function($scope) {
21519 $scope.user = { name: 'say', data: '' };
21520
21521 $scope.cancel = function(e) {
21522 if (e.keyCode == 27) {
21523 $scope.userForm.userName.$rollbackViewValue();
21524 }
21525 };
21526 }]);
21527 </file>
21528 <file name="protractor.js" type="protractor">
21529 var model = element(by.binding('user.name'));
21530 var input = element(by.model('user.name'));
21531 var other = element(by.model('user.data'));
21532
21533 it('should allow custom events', function() {
21534 input.sendKeys(' hello');
21535 input.click();
21536 expect(model.getText()).toEqual('say');
21537 other.click();
21538 expect(model.getText()).toEqual('say hello');
21539 });
21540
21541 it('should $rollbackViewValue when model changes', function() {
21542 input.sendKeys(' hello');
21543 expect(input.getAttribute('value')).toEqual('say hello');
21544 input.sendKeys(protractor.Key.ESCAPE);
21545 expect(input.getAttribute('value')).toEqual('say');
21546 other.click();
21547 expect(model.getText()).toEqual('say');
21548 });
21549 </file>
21550 </example>
21551
21552 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
21553 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
21554
21555 <example name="ngModelOptions-directive-debounce" module="optionsExample">
21556 <file name="index.html">
21557 <div ng-controller="ExampleController">
21558 <form name="userForm">
21559 Name:
21560 <input type="text" name="userName"
21561 ng-model="user.name"
21562 ng-model-options="{ debounce: 1000 }" />
21563 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button><br />
21564 </form>
21565 <pre>user.name = <span ng-bind="user.name"></span></pre>
21566 </div>
21567 </file>
21568 <file name="app.js">
21569 angular.module('optionsExample', [])
21570 .controller('ExampleController', ['$scope', function($scope) {
21571 $scope.user = { name: 'say' };
21572 }]);
21573 </file>
21574 </example>
21575
21576 This one shows how to bind to getter/setters:
21577
21578 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
21579 <file name="index.html">
21580 <div ng-controller="ExampleController">
21581 <form name="userForm">
21582 Name:
21583 <input type="text" name="userName"
21584 ng-model="user.name"
21585 ng-model-options="{ getterSetter: true }" />
21586 </form>
21587 <pre>user.name = <span ng-bind="user.name()"></span></pre>
21588 </div>
21589 </file>
21590 <file name="app.js">
21591 angular.module('getterSetterExample', [])
21592 .controller('ExampleController', ['$scope', function($scope) {
21593 var _name = 'Brian';
21594 $scope.user = {
21595 name: function(newName) {
21596 return angular.isDefined(newName) ? (_name = newName) : _name;
21597 }
21598 };
21599 }]);
21600 </file>
21601 </example>
21602 */
21603var ngModelOptionsDirective = function() {
21604 return {
21605 restrict: 'A',
21606 controller: ['$scope', '$attrs', function($scope, $attrs) {
21607 var that = this;
21608 this.$options = $scope.$eval($attrs.ngModelOptions);
21609 // Allow adding/overriding bound events
21610 if (this.$options.updateOn !== undefined) {
21611 this.$options.updateOnDefault = false;
21612 // extract "default" pseudo-event from list of events that can trigger a model update
21613 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
21614 that.$options.updateOnDefault = true;
21615 return ' ';
21616 }));
21617 } else {
21618 this.$options.updateOnDefault = true;
21619 }
21620 }]
21621 };
21622};
21623
21624// helper methods
21625function addSetValidityMethod(context) {
21626 var ctrl = context.ctrl,
21627 $element = context.$element,
21628 classCache = {},
21629 set = context.set,
21630 unset = context.unset,
21631 parentForm = context.parentForm,
21632 $animate = context.$animate;
21633
21634 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
21635
21636 ctrl.$setValidity = setValidity;
21637
21638 function setValidity(validationErrorKey, state, options) {
21639 if (state === undefined) {
21640 createAndSet('$pending', validationErrorKey, options);
21641 } else {
21642 unsetAndCleanup('$pending', validationErrorKey, options);
21643 }
21644 if (!isBoolean(state)) {
21645 unset(ctrl.$error, validationErrorKey, options);
21646 unset(ctrl.$$success, validationErrorKey, options);
21647 } else {
21648 if (state) {
21649 unset(ctrl.$error, validationErrorKey, options);
21650 set(ctrl.$$success, validationErrorKey, options);
21651 } else {
21652 set(ctrl.$error, validationErrorKey, options);
21653 unset(ctrl.$$success, validationErrorKey, options);
21654 }
21655 }
21656 if (ctrl.$pending) {
21657 cachedToggleClass(PENDING_CLASS, true);
21658 ctrl.$valid = ctrl.$invalid = undefined;
21659 toggleValidationCss('', null);
21660 } else {
21661 cachedToggleClass(PENDING_CLASS, false);
21662 ctrl.$valid = isObjectEmpty(ctrl.$error);
21663 ctrl.$invalid = !ctrl.$valid;
21664 toggleValidationCss('', ctrl.$valid);
21665 }
21666
21667 // re-read the state as the set/unset methods could have
21668 // combined state in ctrl.$error[validationError] (used for forms),
21669 // where setting/unsetting only increments/decrements the value,
21670 // and does not replace it.
21671 var combinedState;
21672 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
21673 combinedState = undefined;
21674 } else if (ctrl.$error[validationErrorKey]) {
21675 combinedState = false;
21676 } else if (ctrl.$$success[validationErrorKey]) {
21677 combinedState = true;
21678 } else {
21679 combinedState = null;
21680 }
21681 toggleValidationCss(validationErrorKey, combinedState);
21682 parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
21683 }
21684
21685 function createAndSet(name, value, options) {
21686 if (!ctrl[name]) {
21687 ctrl[name] = {};
21688 }
21689 set(ctrl[name], value, options);
21690 }
21691
21692 function unsetAndCleanup(name, value, options) {
21693 if (ctrl[name]) {
21694 unset(ctrl[name], value, options);
21695 }
21696 if (isObjectEmpty(ctrl[name])) {
21697 ctrl[name] = undefined;
21698 }
21699 }
21700
21701 function cachedToggleClass(className, switchValue) {
21702 if (switchValue && !classCache[className]) {
21703 $animate.addClass($element, className);
21704 classCache[className] = true;
21705 } else if (!switchValue && classCache[className]) {
21706 $animate.removeClass($element, className);
21707 classCache[className] = false;
21708 }
21709 }
21710
21711 function toggleValidationCss(validationErrorKey, isValid) {
21712 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
21713
21714 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
21715 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
21716 }
21717}
21718
21719function isObjectEmpty(obj) {
21720 if (obj) {
21721 for (var prop in obj) {
21722 return false;
21723 }
21724 }
21725 return true;
21726}
21727
21728/**
21729 * @ngdoc directive
21730 * @name ngBind
21731 * @restrict AC
21732 *
21733 * @description
21734 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
21735 * with the value of a given expression, and to update the text content when the value of that
21736 * expression changes.
21737 *
21738 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
21739 * `{{ expression }}` which is similar but less verbose.
21740 *
21741 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
21742 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
21743 * element attribute, it makes the bindings invisible to the user while the page is loading.
21744 *
21745 * An alternative solution to this problem would be using the
21746 * {@link ng.directive:ngCloak ngCloak} directive.
21747 *
21748 *
21749 * @element ANY
21750 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
21751 *
21752 * @example
21753 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
21754 <example module="bindExample">
21755 <file name="index.html">
21756 <script>
21757 angular.module('bindExample', [])
21758 .controller('ExampleController', ['$scope', function($scope) {
21759 $scope.name = 'Whirled';
21760 }]);
21761 </script>
21762 <div ng-controller="ExampleController">
21763 Enter name: <input type="text" ng-model="name"><br>
21764 Hello <span ng-bind="name"></span>!
21765 </div>
21766 </file>
21767 <file name="protractor.js" type="protractor">
21768 it('should check ng-bind', function() {
21769 var nameInput = element(by.model('name'));
21770
21771 expect(element(by.binding('name')).getText()).toBe('Whirled');
21772 nameInput.clear();
21773 nameInput.sendKeys('world');
21774 expect(element(by.binding('name')).getText()).toBe('world');
21775 });
21776 </file>
21777 </example>
21778 */
21779var ngBindDirective = ['$compile', function($compile) {
21780 return {
21781 restrict: 'AC',
21782 compile: function ngBindCompile(templateElement) {
21783 $compile.$$addBindingClass(templateElement);
21784 return function ngBindLink(scope, element, attr) {
21785 $compile.$$addBindingInfo(element, attr.ngBind);
21786 element = element[0];
21787 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
21788 element.textContent = value === undefined ? '' : value;
21789 });
21790 };
21791 }
21792 };
21793}];
21794
21795
21796/**
21797 * @ngdoc directive
21798 * @name ngBindTemplate
21799 *
21800 * @description
21801 * The `ngBindTemplate` directive specifies that the element
21802 * text content should be replaced with the interpolation of the template
21803 * in the `ngBindTemplate` attribute.
21804 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
21805 * expressions. This directive is needed since some HTML elements
21806 * (such as TITLE and OPTION) cannot contain SPAN elements.
21807 *
21808 * @element ANY
21809 * @param {string} ngBindTemplate template of form
21810 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
21811 *
21812 * @example
21813 * Try it here: enter text in text box and watch the greeting change.
21814 <example module="bindExample">
21815 <file name="index.html">
21816 <script>
21817 angular.module('bindExample', [])
21818 .controller('ExampleController', ['$scope', function($scope) {
21819 $scope.salutation = 'Hello';
21820 $scope.name = 'World';
21821 }]);
21822 </script>
21823 <div ng-controller="ExampleController">
21824 Salutation: <input type="text" ng-model="salutation"><br>
21825 Name: <input type="text" ng-model="name"><br>
21826 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
21827 </div>
21828 </file>
21829 <file name="protractor.js" type="protractor">
21830 it('should check ng-bind', function() {
21831 var salutationElem = element(by.binding('salutation'));
21832 var salutationInput = element(by.model('salutation'));
21833 var nameInput = element(by.model('name'));
21834
21835 expect(salutationElem.getText()).toBe('Hello World!');
21836
21837 salutationInput.clear();
21838 salutationInput.sendKeys('Greetings');
21839 nameInput.clear();
21840 nameInput.sendKeys('user');
21841
21842 expect(salutationElem.getText()).toBe('Greetings user!');
21843 });
21844 </file>
21845 </example>
21846 */
21847var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
21848 return {
21849 compile: function ngBindTemplateCompile(templateElement) {
21850 $compile.$$addBindingClass(templateElement);
21851 return function ngBindTemplateLink(scope, element, attr) {
21852 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
21853 $compile.$$addBindingInfo(element, interpolateFn.expressions);
21854 element = element[0];
21855 attr.$observe('ngBindTemplate', function(value) {
21856 element.textContent = value === undefined ? '' : value;
21857 });
21858 };
21859 }
21860 };
21861}];
21862
21863
21864/**
21865 * @ngdoc directive
21866 * @name ngBindHtml
21867 *
21868 * @description
21869 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
21870 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
21871 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
21872 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
21873 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
21874 *
21875 * You may also bypass sanitization for values you know are safe. To do so, bind to
21876 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
21877 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
21878 *
21879 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
21880 * will have an exception (instead of an exploit.)
21881 *
21882 * @element ANY
21883 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
21884 *
21885 * @example
21886
21887 <example module="bindHtmlExample" deps="angular-sanitize.js">
21888 <file name="index.html">
21889 <div ng-controller="ExampleController">
21890 <p ng-bind-html="myHTML"></p>
21891 </div>
21892 </file>
21893
21894 <file name="script.js">
21895 angular.module('bindHtmlExample', ['ngSanitize'])
21896 .controller('ExampleController', ['$scope', function($scope) {
21897 $scope.myHTML =
21898 'I am an <code>HTML</code>string with ' +
21899 '<a href="#">links!</a> and other <em>stuff</em>';
21900 }]);
21901 </file>
21902
21903 <file name="protractor.js" type="protractor">
21904 it('should check ng-bind-html', function() {
21905 expect(element(by.binding('myHTML')).getText()).toBe(
21906 'I am an HTMLstring with links! and other stuff');
21907 });
21908 </file>
21909 </example>
21910 */
21911var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
21912 return {
21913 restrict: 'A',
21914 compile: function ngBindHtmlCompile(tElement, tAttrs) {
21915 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
21916 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
21917 return (value || '').toString();
21918 });
21919 $compile.$$addBindingClass(tElement);
21920
21921 return function ngBindHtmlLink(scope, element, attr) {
21922 $compile.$$addBindingInfo(element, attr.ngBindHtml);
21923
21924 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
21925 // we re-evaluate the expr because we want a TrustedValueHolderType
21926 // for $sce, not a string
21927 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
21928 });
21929 };
21930 }
21931 };
21932}];
21933
21934function classDirective(name, selector) {
21935 name = 'ngClass' + name;
21936 return ['$animate', function($animate) {
21937 return {
21938 restrict: 'AC',
21939 link: function(scope, element, attr) {
21940 var oldVal;
21941
21942 scope.$watch(attr[name], ngClassWatchAction, true);
21943
21944 attr.$observe('class', function(value) {
21945 ngClassWatchAction(scope.$eval(attr[name]));
21946 });
21947
21948
21949 if (name !== 'ngClass') {
21950 scope.$watch('$index', function($index, old$index) {
21951 // jshint bitwise: false
21952 var mod = $index & 1;
21953 if (mod !== (old$index & 1)) {
21954 var classes = arrayClasses(scope.$eval(attr[name]));
21955 mod === selector ?
21956 addClasses(classes) :
21957 removeClasses(classes);
21958 }
21959 });
21960 }
21961
21962 function addClasses(classes) {
21963 var newClasses = digestClassCounts(classes, 1);
21964 attr.$addClass(newClasses);
21965 }
21966
21967 function removeClasses(classes) {
21968 var newClasses = digestClassCounts(classes, -1);
21969 attr.$removeClass(newClasses);
21970 }
21971
21972 function digestClassCounts(classes, count) {
21973 var classCounts = element.data('$classCounts') || {};
21974 var classesToUpdate = [];
21975 forEach(classes, function(className) {
21976 if (count > 0 || classCounts[className]) {
21977 classCounts[className] = (classCounts[className] || 0) + count;
21978 if (classCounts[className] === +(count > 0)) {
21979 classesToUpdate.push(className);
21980 }
21981 }
21982 });
21983 element.data('$classCounts', classCounts);
21984 return classesToUpdate.join(' ');
21985 }
21986
21987 function updateClasses(oldClasses, newClasses) {
21988 var toAdd = arrayDifference(newClasses, oldClasses);
21989 var toRemove = arrayDifference(oldClasses, newClasses);
21990 toAdd = digestClassCounts(toAdd, 1);
21991 toRemove = digestClassCounts(toRemove, -1);
21992 if (toAdd && toAdd.length) {
21993 $animate.addClass(element, toAdd);
21994 }
21995 if (toRemove && toRemove.length) {
21996 $animate.removeClass(element, toRemove);
21997 }
21998 }
21999
22000 function ngClassWatchAction(newVal) {
22001 if (selector === true || scope.$index % 2 === selector) {
22002 var newClasses = arrayClasses(newVal || []);
22003 if (!oldVal) {
22004 addClasses(newClasses);
22005 } else if (!equals(newVal,oldVal)) {
22006 var oldClasses = arrayClasses(oldVal);
22007 updateClasses(oldClasses, newClasses);
22008 }
22009 }
22010 oldVal = shallowCopy(newVal);
22011 }
22012 }
22013 };
22014
22015 function arrayDifference(tokens1, tokens2) {
22016 var values = [];
22017
22018 outer:
22019 for (var i = 0; i < tokens1.length; i++) {
22020 var token = tokens1[i];
22021 for (var j = 0; j < tokens2.length; j++) {
22022 if (token == tokens2[j]) continue outer;
22023 }
22024 values.push(token);
22025 }
22026 return values;
22027 }
22028
22029 function arrayClasses(classVal) {
22030 if (isArray(classVal)) {
22031 return classVal;
22032 } else if (isString(classVal)) {
22033 return classVal.split(' ');
22034 } else if (isObject(classVal)) {
22035 var classes = [];
22036 forEach(classVal, function(v, k) {
22037 if (v) {
22038 classes = classes.concat(k.split(' '));
22039 }
22040 });
22041 return classes;
22042 }
22043 return classVal;
22044 }
22045 }];
22046}
22047
22048/**
22049 * @ngdoc directive
22050 * @name ngClass
22051 * @restrict AC
22052 *
22053 * @description
22054 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22055 * an expression that represents all classes to be added.
22056 *
22057 * The directive operates in three different ways, depending on which of three types the expression
22058 * evaluates to:
22059 *
22060 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22061 * names.
22062 *
22063 * 2. If the expression evaluates to an array, each element of the array should be a string that is
22064 * one or more space-delimited class names.
22065 *
22066 * 3. If the expression evaluates to an object, then for each key-value pair of the
22067 * object with a truthy value the corresponding key is used as a class name.
22068 *
22069 * The directive won't add duplicate classes if a particular class was already set.
22070 *
22071 * When the expression changes, the previously added classes are removed and only then the
22072 * new classes are added.
22073 *
22074 * @animations
22075 * add - happens just before the class is applied to the element
22076 * remove - happens just before the class is removed from the element
22077 *
22078 * @element ANY
22079 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22080 * of the evaluation can be a string representing space delimited class
22081 * names, an array, or a map of class names to boolean values. In the case of a map, the
22082 * names of the properties whose values are truthy will be added as css classes to the
22083 * element.
22084 *
22085 * @example Example that demonstrates basic bindings via ngClass directive.
22086 <example>
22087 <file name="index.html">
22088 <p ng-class="{strike: deleted, bold: important, red: error}">Map Syntax Example</p>
22089 <input type="checkbox" ng-model="deleted"> deleted (apply "strike" class)<br>
22090 <input type="checkbox" ng-model="important"> important (apply "bold" class)<br>
22091 <input type="checkbox" ng-model="error"> error (apply "red" class)
22092 <hr>
22093 <p ng-class="style">Using String Syntax</p>
22094 <input type="text" ng-model="style" placeholder="Type: bold strike red">
22095 <hr>
22096 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22097 <input ng-model="style1" placeholder="Type: bold, strike or red"><br>
22098 <input ng-model="style2" placeholder="Type: bold, strike or red"><br>
22099 <input ng-model="style3" placeholder="Type: bold, strike or red"><br>
22100 </file>
22101 <file name="style.css">
22102 .strike {
22103 text-decoration: line-through;
22104 }
22105 .bold {
22106 font-weight: bold;
22107 }
22108 .red {
22109 color: red;
22110 }
22111 </file>
22112 <file name="protractor.js" type="protractor">
22113 var ps = element.all(by.css('p'));
22114
22115 it('should let you toggle the class', function() {
22116
22117 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22118 expect(ps.first().getAttribute('class')).not.toMatch(/red/);
22119
22120 element(by.model('important')).click();
22121 expect(ps.first().getAttribute('class')).toMatch(/bold/);
22122
22123 element(by.model('error')).click();
22124 expect(ps.first().getAttribute('class')).toMatch(/red/);
22125 });
22126
22127 it('should let you toggle string example', function() {
22128 expect(ps.get(1).getAttribute('class')).toBe('');
22129 element(by.model('style')).clear();
22130 element(by.model('style')).sendKeys('red');
22131 expect(ps.get(1).getAttribute('class')).toBe('red');
22132 });
22133
22134 it('array example should have 3 classes', function() {
22135 expect(ps.last().getAttribute('class')).toBe('');
22136 element(by.model('style1')).sendKeys('bold');
22137 element(by.model('style2')).sendKeys('strike');
22138 element(by.model('style3')).sendKeys('red');
22139 expect(ps.last().getAttribute('class')).toBe('bold strike red');
22140 });
22141 </file>
22142 </example>
22143
22144 ## Animations
22145
22146 The example below demonstrates how to perform animations using ngClass.
22147
22148 <example module="ngAnimate" deps="angular-animate.js" animations="true">
22149 <file name="index.html">
22150 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22151 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22152 <br>
22153 <span class="base-class" ng-class="myVar">Sample Text</span>
22154 </file>
22155 <file name="style.css">
22156 .base-class {
22157 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22158 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22159 }
22160
22161 .base-class.my-class {
22162 color: red;
22163 font-size:3em;
22164 }
22165 </file>
22166 <file name="protractor.js" type="protractor">
22167 it('should check ng-class', function() {
22168 expect(element(by.css('.base-class')).getAttribute('class')).not.
22169 toMatch(/my-class/);
22170
22171 element(by.id('setbtn')).click();
22172
22173 expect(element(by.css('.base-class')).getAttribute('class')).
22174 toMatch(/my-class/);
22175
22176 element(by.id('clearbtn')).click();
22177
22178 expect(element(by.css('.base-class')).getAttribute('class')).not.
22179 toMatch(/my-class/);
22180 });
22181 </file>
22182 </example>
22183
22184
22185 ## ngClass and pre-existing CSS3 Transitions/Animations
22186 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
22187 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
22188 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
22189 to view the step by step details of {@link ng.$animate#addClass $animate.addClass} and
22190 {@link ng.$animate#removeClass $animate.removeClass}.
22191 */
22192var ngClassDirective = classDirective('', true);
22193
22194/**
22195 * @ngdoc directive
22196 * @name ngClassOdd
22197 * @restrict AC
22198 *
22199 * @description
22200 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22201 * {@link ng.directive:ngClass ngClass}, except they work in
22202 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22203 *
22204 * This directive can be applied only within the scope of an
22205 * {@link ng.directive:ngRepeat ngRepeat}.
22206 *
22207 * @element ANY
22208 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
22209 * of the evaluation can be a string representing space delimited class names or an array.
22210 *
22211 * @example
22212 <example>
22213 <file name="index.html">
22214 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22215 <li ng-repeat="name in names">
22216 <span ng-class-odd="'odd'" ng-class-even="'even'">
22217 {{name}}
22218 </span>
22219 </li>
22220 </ol>
22221 </file>
22222 <file name="style.css">
22223 .odd {
22224 color: red;
22225 }
22226 .even {
22227 color: blue;
22228 }
22229 </file>
22230 <file name="protractor.js" type="protractor">
22231 it('should check ng-class-odd and ng-class-even', function() {
22232 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22233 toMatch(/odd/);
22234 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22235 toMatch(/even/);
22236 });
22237 </file>
22238 </example>
22239 */
22240var ngClassOddDirective = classDirective('Odd', 0);
22241
22242/**
22243 * @ngdoc directive
22244 * @name ngClassEven
22245 * @restrict AC
22246 *
22247 * @description
22248 * The `ngClassOdd` and `ngClassEven` directives work exactly as
22249 * {@link ng.directive:ngClass ngClass}, except they work in
22250 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
22251 *
22252 * This directive can be applied only within the scope of an
22253 * {@link ng.directive:ngRepeat ngRepeat}.
22254 *
22255 * @element ANY
22256 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
22257 * result of the evaluation can be a string representing space delimited class names or an array.
22258 *
22259 * @example
22260 <example>
22261 <file name="index.html">
22262 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
22263 <li ng-repeat="name in names">
22264 <span ng-class-odd="'odd'" ng-class-even="'even'">
22265 {{name}} &nbsp; &nbsp; &nbsp;
22266 </span>
22267 </li>
22268 </ol>
22269 </file>
22270 <file name="style.css">
22271 .odd {
22272 color: red;
22273 }
22274 .even {
22275 color: blue;
22276 }
22277 </file>
22278 <file name="protractor.js" type="protractor">
22279 it('should check ng-class-odd and ng-class-even', function() {
22280 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
22281 toMatch(/odd/);
22282 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
22283 toMatch(/even/);
22284 });
22285 </file>
22286 </example>
22287 */
22288var ngClassEvenDirective = classDirective('Even', 1);
22289
22290/**
22291 * @ngdoc directive
22292 * @name ngCloak
22293 * @restrict AC
22294 *
22295 * @description
22296 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
22297 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
22298 * directive to avoid the undesirable flicker effect caused by the html template display.
22299 *
22300 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
22301 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
22302 * of the browser view.
22303 *
22304 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
22305 * `angular.min.js`.
22306 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
22307 *
22308 * ```css
22309 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
22310 * display: none !important;
22311 * }
22312 * ```
22313 *
22314 * When this css rule is loaded by the browser, all html elements (including their children) that
22315 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
22316 * during the compilation of the template it deletes the `ngCloak` element attribute, making
22317 * the compiled element visible.
22318 *
22319 * For the best result, the `angular.js` script must be loaded in the head section of the html
22320 * document; alternatively, the css rule above must be included in the external stylesheet of the
22321 * application.
22322 *
22323 * Legacy browsers, like IE7, do not provide attribute selector support (added in CSS 2.1) so they
22324 * cannot match the `[ng\:cloak]` selector. To work around this limitation, you must add the css
22325 * class `ng-cloak` in addition to the `ngCloak` directive as shown in the example below.
22326 *
22327 * @element ANY
22328 *
22329 * @example
22330 <example>
22331 <file name="index.html">
22332 <div id="template1" ng-cloak>{{ 'hello' }}</div>
22333 <div id="template2" ng-cloak class="ng-cloak">{{ 'hello IE7' }}</div>
22334 </file>
22335 <file name="protractor.js" type="protractor">
22336 it('should remove the template directive and css class', function() {
22337 expect($('#template1').getAttribute('ng-cloak')).
22338 toBeNull();
22339 expect($('#template2').getAttribute('ng-cloak')).
22340 toBeNull();
22341 });
22342 </file>
22343 </example>
22344 *
22345 */
22346var ngCloakDirective = ngDirective({
22347 compile: function(element, attr) {
22348 attr.$set('ngCloak', undefined);
22349 element.removeClass('ng-cloak');
22350 }
22351});
22352
22353/**
22354 * @ngdoc directive
22355 * @name ngController
22356 *
22357 * @description
22358 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
22359 * supports the principles behind the Model-View-Controller design pattern.
22360 *
22361 * MVC components in angular:
22362 *
22363 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
22364 * are accessed through bindings.
22365 * * View — The template (HTML with data bindings) that is rendered into the View.
22366 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
22367 * logic behind the application to decorate the scope with functions and values
22368 *
22369 * Note that you can also attach controllers to the DOM by declaring it in a route definition
22370 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
22371 * again using `ng-controller` in the template itself. This will cause the controller to be attached
22372 * and executed twice.
22373 *
22374 * @element ANY
22375 * @scope
22376 * @priority 500
22377 * @param {expression} ngController Name of a constructor function registered with the current
22378 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
22379 * that on the current scope evaluates to a constructor function.
22380 *
22381 * The controller instance can be published into a scope property by specifying
22382 * `ng-controller="as propertyName"`.
22383 *
22384 * If the current `$controllerProvider` is configured to use globals (via
22385 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
22386 * also be the name of a globally accessible constructor function (not recommended).
22387 *
22388 * @example
22389 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
22390 * greeting are methods declared on the controller (see source tab). These methods can
22391 * easily be called from the angular markup. Any changes to the data are automatically reflected
22392 * in the View without the need for a manual update.
22393 *
22394 * Two different declaration styles are included below:
22395 *
22396 * * one binds methods and properties directly onto the controller using `this`:
22397 * `ng-controller="SettingsController1 as settings"`
22398 * * one injects `$scope` into the controller:
22399 * `ng-controller="SettingsController2"`
22400 *
22401 * The second option is more common in the Angular community, and is generally used in boilerplates
22402 * and in this guide. However, there are advantages to binding properties directly to the controller
22403 * and avoiding scope.
22404 *
22405 * * Using `controller as` makes it obvious which controller you are accessing in the template when
22406 * multiple controllers apply to an element.
22407 * * If you are writing your controllers as classes you have easier access to the properties and
22408 * methods, which will appear on the scope, from inside the controller code.
22409 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
22410 * inheritance masking primitives.
22411 *
22412 * This example demonstrates the `controller as` syntax.
22413 *
22414 * <example name="ngControllerAs" module="controllerAsExample">
22415 * <file name="index.html">
22416 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
22417 * Name: <input type="text" ng-model="settings.name"/>
22418 * [ <a href="" ng-click="settings.greet()">greet</a> ]<br/>
22419 * Contact:
22420 * <ul>
22421 * <li ng-repeat="contact in settings.contacts">
22422 * <select ng-model="contact.type">
22423 * <option>phone</option>
22424 * <option>email</option>
22425 * </select>
22426 * <input type="text" ng-model="contact.value"/>
22427 * [ <a href="" ng-click="settings.clearContact(contact)">clear</a>
22428 * | <a href="" ng-click="settings.removeContact(contact)">X</a> ]
22429 * </li>
22430 * <li>[ <a href="" ng-click="settings.addContact()">add</a> ]</li>
22431 * </ul>
22432 * </div>
22433 * </file>
22434 * <file name="app.js">
22435 * angular.module('controllerAsExample', [])
22436 * .controller('SettingsController1', SettingsController1);
22437 *
22438 * function SettingsController1() {
22439 * this.name = "John Smith";
22440 * this.contacts = [
22441 * {type: 'phone', value: '408 555 1212'},
22442 * {type: 'email', value: 'john.smith@example.org'} ];
22443 * }
22444 *
22445 * SettingsController1.prototype.greet = function() {
22446 * alert(this.name);
22447 * };
22448 *
22449 * SettingsController1.prototype.addContact = function() {
22450 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
22451 * };
22452 *
22453 * SettingsController1.prototype.removeContact = function(contactToRemove) {
22454 * var index = this.contacts.indexOf(contactToRemove);
22455 * this.contacts.splice(index, 1);
22456 * };
22457 *
22458 * SettingsController1.prototype.clearContact = function(contact) {
22459 * contact.type = 'phone';
22460 * contact.value = '';
22461 * };
22462 * </file>
22463 * <file name="protractor.js" type="protractor">
22464 * it('should check controller as', function() {
22465 * var container = element(by.id('ctrl-as-exmpl'));
22466 * expect(container.element(by.model('settings.name'))
22467 * .getAttribute('value')).toBe('John Smith');
22468 *
22469 * var firstRepeat =
22470 * container.element(by.repeater('contact in settings.contacts').row(0));
22471 * var secondRepeat =
22472 * container.element(by.repeater('contact in settings.contacts').row(1));
22473 *
22474 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22475 * .toBe('408 555 1212');
22476 *
22477 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
22478 * .toBe('john.smith@example.org');
22479 *
22480 * firstRepeat.element(by.linkText('clear')).click();
22481 *
22482 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22483 * .toBe('');
22484 *
22485 * container.element(by.linkText('add')).click();
22486 *
22487 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
22488 * .element(by.model('contact.value'))
22489 * .getAttribute('value'))
22490 * .toBe('yourname@example.org');
22491 * });
22492 * </file>
22493 * </example>
22494 *
22495 * This example demonstrates the "attach to `$scope`" style of controller.
22496 *
22497 * <example name="ngController" module="controllerExample">
22498 * <file name="index.html">
22499 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
22500 * Name: <input type="text" ng-model="name"/>
22501 * [ <a href="" ng-click="greet()">greet</a> ]<br/>
22502 * Contact:
22503 * <ul>
22504 * <li ng-repeat="contact in contacts">
22505 * <select ng-model="contact.type">
22506 * <option>phone</option>
22507 * <option>email</option>
22508 * </select>
22509 * <input type="text" ng-model="contact.value"/>
22510 * [ <a href="" ng-click="clearContact(contact)">clear</a>
22511 * | <a href="" ng-click="removeContact(contact)">X</a> ]
22512 * </li>
22513 * <li>[ <a href="" ng-click="addContact()">add</a> ]</li>
22514 * </ul>
22515 * </div>
22516 * </file>
22517 * <file name="app.js">
22518 * angular.module('controllerExample', [])
22519 * .controller('SettingsController2', ['$scope', SettingsController2]);
22520 *
22521 * function SettingsController2($scope) {
22522 * $scope.name = "John Smith";
22523 * $scope.contacts = [
22524 * {type:'phone', value:'408 555 1212'},
22525 * {type:'email', value:'john.smith@example.org'} ];
22526 *
22527 * $scope.greet = function() {
22528 * alert($scope.name);
22529 * };
22530 *
22531 * $scope.addContact = function() {
22532 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
22533 * };
22534 *
22535 * $scope.removeContact = function(contactToRemove) {
22536 * var index = $scope.contacts.indexOf(contactToRemove);
22537 * $scope.contacts.splice(index, 1);
22538 * };
22539 *
22540 * $scope.clearContact = function(contact) {
22541 * contact.type = 'phone';
22542 * contact.value = '';
22543 * };
22544 * }
22545 * </file>
22546 * <file name="protractor.js" type="protractor">
22547 * it('should check controller', function() {
22548 * var container = element(by.id('ctrl-exmpl'));
22549 *
22550 * expect(container.element(by.model('name'))
22551 * .getAttribute('value')).toBe('John Smith');
22552 *
22553 * var firstRepeat =
22554 * container.element(by.repeater('contact in contacts').row(0));
22555 * var secondRepeat =
22556 * container.element(by.repeater('contact in contacts').row(1));
22557 *
22558 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22559 * .toBe('408 555 1212');
22560 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
22561 * .toBe('john.smith@example.org');
22562 *
22563 * firstRepeat.element(by.linkText('clear')).click();
22564 *
22565 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
22566 * .toBe('');
22567 *
22568 * container.element(by.linkText('add')).click();
22569 *
22570 * expect(container.element(by.repeater('contact in contacts').row(2))
22571 * .element(by.model('contact.value'))
22572 * .getAttribute('value'))
22573 * .toBe('yourname@example.org');
22574 * });
22575 * </file>
22576 *</example>
22577
22578 */
22579var ngControllerDirective = [function() {
22580 return {
22581 restrict: 'A',
22582 scope: true,
22583 controller: '@',
22584 priority: 500
22585 };
22586}];
22587
22588/**
22589 * @ngdoc directive
22590 * @name ngCsp
22591 *
22592 * @element html
22593 * @description
22594 * Enables [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) support.
22595 *
22596 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
22597 *
22598 * CSP forbids apps to use `eval` or `Function(string)` generated functions (among other things).
22599 * For Angular to be CSP compatible there are only two things that we need to do differently:
22600 *
22601 * - don't use `Function` constructor to generate optimized value getters
22602 * - don't inject custom stylesheet into the document
22603 *
22604 * AngularJS uses `Function(string)` generated functions as a speed optimization. Applying the `ngCsp`
22605 * directive will cause Angular to use CSP compatibility mode. When this mode is on AngularJS will
22606 * evaluate all expressions up to 30% slower than in non-CSP mode, but no security violations will
22607 * be raised.
22608 *
22609 * CSP forbids JavaScript to inline stylesheet rules. In non CSP mode Angular automatically
22610 * includes some CSS rules (e.g. {@link ng.directive:ngCloak ngCloak}).
22611 * To make those directives work in CSP mode, include the `angular-csp.css` manually.
22612 *
22613 * Angular tries to autodetect if CSP is active and automatically turn on the CSP-safe mode. This
22614 * autodetection however triggers a CSP error to be logged in the console:
22615 *
22616 * ```
22617 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
22618 * script in the following Content Security Policy directive: "default-src 'self'". Note that
22619 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
22620 * ```
22621 *
22622 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
22623 * directive on the root element of the application or on the `angular.js` script tag, whichever
22624 * appears first in the html document.
22625 *
22626 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
22627 *
22628 * @example
22629 * This example shows how to apply the `ngCsp` directive to the `html` tag.
22630 ```html
22631 <!doctype html>
22632 <html ng-app ng-csp>
22633 ...
22634 ...
22635 </html>
22636 ```
22637 * @example
22638 // Note: the suffix `.csp` in the example name triggers
22639 // csp mode in our http server!
22640 <example name="example.csp" module="cspExample" ng-csp="true">
22641 <file name="index.html">
22642 <div ng-controller="MainController as ctrl">
22643 <div>
22644 <button ng-click="ctrl.inc()" id="inc">Increment</button>
22645 <span id="counter">
22646 {{ctrl.counter}}
22647 </span>
22648 </div>
22649
22650 <div>
22651 <button ng-click="ctrl.evil()" id="evil">Evil</button>
22652 <span id="evilError">
22653 {{ctrl.evilError}}
22654 </span>
22655 </div>
22656 </div>
22657 </file>
22658 <file name="script.js">
22659 angular.module('cspExample', [])
22660 .controller('MainController', function() {
22661 this.counter = 0;
22662 this.inc = function() {
22663 this.counter++;
22664 };
22665 this.evil = function() {
22666 // jshint evil:true
22667 try {
22668 eval('1+2');
22669 } catch (e) {
22670 this.evilError = e.message;
22671 }
22672 };
22673 });
22674 </file>
22675 <file name="protractor.js" type="protractor">
22676 var util, webdriver;
22677
22678 var incBtn = element(by.id('inc'));
22679 var counter = element(by.id('counter'));
22680 var evilBtn = element(by.id('evil'));
22681 var evilError = element(by.id('evilError'));
22682
22683 function getAndClearSevereErrors() {
22684 return browser.manage().logs().get('browser').then(function(browserLog) {
22685 return browserLog.filter(function(logEntry) {
22686 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
22687 });
22688 });
22689 }
22690
22691 function clearErrors() {
22692 getAndClearSevereErrors();
22693 }
22694
22695 function expectNoErrors() {
22696 getAndClearSevereErrors().then(function(filteredLog) {
22697 expect(filteredLog.length).toEqual(0);
22698 if (filteredLog.length) {
22699 console.log('browser console errors: ' + util.inspect(filteredLog));
22700 }
22701 });
22702 }
22703
22704 function expectError(regex) {
22705 getAndClearSevereErrors().then(function(filteredLog) {
22706 var found = false;
22707 filteredLog.forEach(function(log) {
22708 if (log.message.match(regex)) {
22709 found = true;
22710 }
22711 });
22712 if (!found) {
22713 throw new Error('expected an error that matches ' + regex);
22714 }
22715 });
22716 }
22717
22718 beforeEach(function() {
22719 util = require('util');
22720 webdriver = require('protractor/node_modules/selenium-webdriver');
22721 });
22722
22723 // For now, we only test on Chrome,
22724 // as Safari does not load the page with Protractor's injected scripts,
22725 // and Firefox webdriver always disables content security policy (#6358)
22726 if (browser.params.browser !== 'chrome') {
22727 return;
22728 }
22729
22730 it('should not report errors when the page is loaded', function() {
22731 // clear errors so we are not dependent on previous tests
22732 clearErrors();
22733 // Need to reload the page as the page is already loaded when
22734 // we come here
22735 browser.driver.getCurrentUrl().then(function(url) {
22736 browser.get(url);
22737 });
22738 expectNoErrors();
22739 });
22740
22741 it('should evaluate expressions', function() {
22742 expect(counter.getText()).toEqual('0');
22743 incBtn.click();
22744 expect(counter.getText()).toEqual('1');
22745 expectNoErrors();
22746 });
22747
22748 it('should throw and report an error when using "eval"', function() {
22749 evilBtn.click();
22750 expect(evilError.getText()).toMatch(/Content Security Policy/);
22751 expectError(/Content Security Policy/);
22752 });
22753 </file>
22754 </example>
22755 */
22756
22757// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
22758// bootstrap the system (before $parse is instantiated), for this reason we just have
22759// the csp.isActive() fn that looks for ng-csp attribute anywhere in the current doc
22760
22761/**
22762 * @ngdoc directive
22763 * @name ngClick
22764 *
22765 * @description
22766 * The ngClick directive allows you to specify custom behavior when
22767 * an element is clicked.
22768 *
22769 * @element ANY
22770 * @priority 0
22771 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
22772 * click. ({@link guide/expression#-event- Event object is available as `$event`})
22773 *
22774 * @example
22775 <example>
22776 <file name="index.html">
22777 <button ng-click="count = count + 1" ng-init="count=0">
22778 Increment
22779 </button>
22780 <span>
22781 count: {{count}}
22782 </span>
22783 </file>
22784 <file name="protractor.js" type="protractor">
22785 it('should check ng-click', function() {
22786 expect(element(by.binding('count')).getText()).toMatch('0');
22787 element(by.css('button')).click();
22788 expect(element(by.binding('count')).getText()).toMatch('1');
22789 });
22790 </file>
22791 </example>
22792 */
22793/*
22794 * A collection of directives that allows creation of custom event handlers that are defined as
22795 * angular expressions and are compiled and executed within the current scope.
22796 */
22797var ngEventDirectives = {};
22798
22799// For events that might fire synchronously during DOM manipulation
22800// we need to execute their event handlers asynchronously using $evalAsync,
22801// so that they are not executed in an inconsistent state.
22802var forceAsyncEvents = {
22803 'blur': true,
22804 'focus': true
22805};
22806forEach(
22807 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
22808 function(eventName) {
22809 var directiveName = directiveNormalize('ng-' + eventName);
22810 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
22811 return {
22812 restrict: 'A',
22813 compile: function($element, attr) {
22814 // We expose the powerful $event object on the scope that provides access to the Window,
22815 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
22816 // checks at the cost of speed since event handler expressions are not executed as
22817 // frequently as regular change detection.
22818 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
22819 return function ngEventHandler(scope, element) {
22820 element.on(eventName, function(event) {
22821 var callback = function() {
22822 fn(scope, {$event:event});
22823 };
22824 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
22825 scope.$evalAsync(callback);
22826 } else {
22827 scope.$apply(callback);
22828 }
22829 });
22830 };
22831 }
22832 };
22833 }];
22834 }
22835);
22836
22837/**
22838 * @ngdoc directive
22839 * @name ngDblclick
22840 *
22841 * @description
22842 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
22843 *
22844 * @element ANY
22845 * @priority 0
22846 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
22847 * a dblclick. (The Event object is available as `$event`)
22848 *
22849 * @example
22850 <example>
22851 <file name="index.html">
22852 <button ng-dblclick="count = count + 1" ng-init="count=0">
22853 Increment (on double click)
22854 </button>
22855 count: {{count}}
22856 </file>
22857 </example>
22858 */
22859
22860
22861/**
22862 * @ngdoc directive
22863 * @name ngMousedown
22864 *
22865 * @description
22866 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
22867 *
22868 * @element ANY
22869 * @priority 0
22870 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
22871 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
22872 *
22873 * @example
22874 <example>
22875 <file name="index.html">
22876 <button ng-mousedown="count = count + 1" ng-init="count=0">
22877 Increment (on mouse down)
22878 </button>
22879 count: {{count}}
22880 </file>
22881 </example>
22882 */
22883
22884
22885/**
22886 * @ngdoc directive
22887 * @name ngMouseup
22888 *
22889 * @description
22890 * Specify custom behavior on mouseup event.
22891 *
22892 * @element ANY
22893 * @priority 0
22894 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
22895 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
22896 *
22897 * @example
22898 <example>
22899 <file name="index.html">
22900 <button ng-mouseup="count = count + 1" ng-init="count=0">
22901 Increment (on mouse up)
22902 </button>
22903 count: {{count}}
22904 </file>
22905 </example>
22906 */
22907
22908/**
22909 * @ngdoc directive
22910 * @name ngMouseover
22911 *
22912 * @description
22913 * Specify custom behavior on mouseover event.
22914 *
22915 * @element ANY
22916 * @priority 0
22917 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
22918 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
22919 *
22920 * @example
22921 <example>
22922 <file name="index.html">
22923 <button ng-mouseover="count = count + 1" ng-init="count=0">
22924 Increment (when mouse is over)
22925 </button>
22926 count: {{count}}
22927 </file>
22928 </example>
22929 */
22930
22931
22932/**
22933 * @ngdoc directive
22934 * @name ngMouseenter
22935 *
22936 * @description
22937 * Specify custom behavior on mouseenter event.
22938 *
22939 * @element ANY
22940 * @priority 0
22941 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
22942 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
22943 *
22944 * @example
22945 <example>
22946 <file name="index.html">
22947 <button ng-mouseenter="count = count + 1" ng-init="count=0">
22948 Increment (when mouse enters)
22949 </button>
22950 count: {{count}}
22951 </file>
22952 </example>
22953 */
22954
22955
22956/**
22957 * @ngdoc directive
22958 * @name ngMouseleave
22959 *
22960 * @description
22961 * Specify custom behavior on mouseleave event.
22962 *
22963 * @element ANY
22964 * @priority 0
22965 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
22966 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
22967 *
22968 * @example
22969 <example>
22970 <file name="index.html">
22971 <button ng-mouseleave="count = count + 1" ng-init="count=0">
22972 Increment (when mouse leaves)
22973 </button>
22974 count: {{count}}
22975 </file>
22976 </example>
22977 */
22978
22979
22980/**
22981 * @ngdoc directive
22982 * @name ngMousemove
22983 *
22984 * @description
22985 * Specify custom behavior on mousemove event.
22986 *
22987 * @element ANY
22988 * @priority 0
22989 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
22990 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
22991 *
22992 * @example
22993 <example>
22994 <file name="index.html">
22995 <button ng-mousemove="count = count + 1" ng-init="count=0">
22996 Increment (when mouse moves)
22997 </button>
22998 count: {{count}}
22999 </file>
23000 </example>
23001 */
23002
23003
23004/**
23005 * @ngdoc directive
23006 * @name ngKeydown
23007 *
23008 * @description
23009 * Specify custom behavior on keydown event.
23010 *
23011 * @element ANY
23012 * @priority 0
23013 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23014 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23015 *
23016 * @example
23017 <example>
23018 <file name="index.html">
23019 <input ng-keydown="count = count + 1" ng-init="count=0">
23020 key down count: {{count}}
23021 </file>
23022 </example>
23023 */
23024
23025
23026/**
23027 * @ngdoc directive
23028 * @name ngKeyup
23029 *
23030 * @description
23031 * Specify custom behavior on keyup event.
23032 *
23033 * @element ANY
23034 * @priority 0
23035 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23036 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23037 *
23038 * @example
23039 <example>
23040 <file name="index.html">
23041 <p>Typing in the input box below updates the key count</p>
23042 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23043
23044 <p>Typing in the input box below updates the keycode</p>
23045 <input ng-keyup="event=$event">
23046 <p>event keyCode: {{ event.keyCode }}</p>
23047 <p>event altKey: {{ event.altKey }}</p>
23048 </file>
23049 </example>
23050 */
23051
23052
23053/**
23054 * @ngdoc directive
23055 * @name ngKeypress
23056 *
23057 * @description
23058 * Specify custom behavior on keypress event.
23059 *
23060 * @element ANY
23061 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23062 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23063 * and can be interrogated for keyCode, altKey, etc.)
23064 *
23065 * @example
23066 <example>
23067 <file name="index.html">
23068 <input ng-keypress="count = count + 1" ng-init="count=0">
23069 key press count: {{count}}
23070 </file>
23071 </example>
23072 */
23073
23074
23075/**
23076 * @ngdoc directive
23077 * @name ngSubmit
23078 *
23079 * @description
23080 * Enables binding angular expressions to onsubmit events.
23081 *
23082 * Additionally it prevents the default action (which for form means sending the request to the
23083 * server and reloading the current page), but only if the form does not contain `action`,
23084 * `data-action`, or `x-action` attributes.
23085 *
23086 * <div class="alert alert-warning">
23087 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23088 * `ngSubmit` handlers together. See the
23089 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23090 * for a detailed discussion of when `ngSubmit` may be triggered.
23091 * </div>
23092 *
23093 * @element form
23094 * @priority 0
23095 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23096 * ({@link guide/expression#-event- Event object is available as `$event`})
23097 *
23098 * @example
23099 <example module="submitExample">
23100 <file name="index.html">
23101 <script>
23102 angular.module('submitExample', [])
23103 .controller('ExampleController', ['$scope', function($scope) {
23104 $scope.list = [];
23105 $scope.text = 'hello';
23106 $scope.submit = function() {
23107 if ($scope.text) {
23108 $scope.list.push(this.text);
23109 $scope.text = '';
23110 }
23111 };
23112 }]);
23113 </script>
23114 <form ng-submit="submit()" ng-controller="ExampleController">
23115 Enter text and hit enter:
23116 <input type="text" ng-model="text" name="text" />
23117 <input type="submit" id="submit" value="Submit" />
23118 <pre>list={{list}}</pre>
23119 </form>
23120 </file>
23121 <file name="protractor.js" type="protractor">
23122 it('should check ng-submit', function() {
23123 expect(element(by.binding('list')).getText()).toBe('list=[]');
23124 element(by.css('#submit')).click();
23125 expect(element(by.binding('list')).getText()).toContain('hello');
23126 expect(element(by.model('text')).getAttribute('value')).toBe('');
23127 });
23128 it('should ignore empty strings', function() {
23129 expect(element(by.binding('list')).getText()).toBe('list=[]');
23130 element(by.css('#submit')).click();
23131 element(by.css('#submit')).click();
23132 expect(element(by.binding('list')).getText()).toContain('hello');
23133 });
23134 </file>
23135 </example>
23136 */
23137
23138/**
23139 * @ngdoc directive
23140 * @name ngFocus
23141 *
23142 * @description
23143 * Specify custom behavior on focus event.
23144 *
23145 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
23146 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23147 * during an `$apply` to ensure a consistent state.
23148 *
23149 * @element window, input, select, textarea, a
23150 * @priority 0
23151 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
23152 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
23153 *
23154 * @example
23155 * See {@link ng.directive:ngClick ngClick}
23156 */
23157
23158/**
23159 * @ngdoc directive
23160 * @name ngBlur
23161 *
23162 * @description
23163 * Specify custom behavior on blur event.
23164 *
23165 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
23166 * an element has lost focus.
23167 *
23168 * Note: As the `blur` event is executed synchronously also during DOM manipulations
23169 * (e.g. removing a focussed input),
23170 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
23171 * during an `$apply` to ensure a consistent state.
23172 *
23173 * @element window, input, select, textarea, a
23174 * @priority 0
23175 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
23176 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
23177 *
23178 * @example
23179 * See {@link ng.directive:ngClick ngClick}
23180 */
23181
23182/**
23183 * @ngdoc directive
23184 * @name ngCopy
23185 *
23186 * @description
23187 * Specify custom behavior on copy event.
23188 *
23189 * @element window, input, select, textarea, a
23190 * @priority 0
23191 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
23192 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
23193 *
23194 * @example
23195 <example>
23196 <file name="index.html">
23197 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
23198 copied: {{copied}}
23199 </file>
23200 </example>
23201 */
23202
23203/**
23204 * @ngdoc directive
23205 * @name ngCut
23206 *
23207 * @description
23208 * Specify custom behavior on cut event.
23209 *
23210 * @element window, input, select, textarea, a
23211 * @priority 0
23212 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
23213 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
23214 *
23215 * @example
23216 <example>
23217 <file name="index.html">
23218 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
23219 cut: {{cut}}
23220 </file>
23221 </example>
23222 */
23223
23224/**
23225 * @ngdoc directive
23226 * @name ngPaste
23227 *
23228 * @description
23229 * Specify custom behavior on paste event.
23230 *
23231 * @element window, input, select, textarea, a
23232 * @priority 0
23233 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
23234 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
23235 *
23236 * @example
23237 <example>
23238 <file name="index.html">
23239 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
23240 pasted: {{paste}}
23241 </file>
23242 </example>
23243 */
23244
23245/**
23246 * @ngdoc directive
23247 * @name ngIf
23248 * @restrict A
23249 *
23250 * @description
23251 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
23252 * {expression}. If the expression assigned to `ngIf` evaluates to a false
23253 * value then the element is removed from the DOM, otherwise a clone of the
23254 * element is reinserted into the DOM.
23255 *
23256 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
23257 * element in the DOM rather than changing its visibility via the `display` css property. A common
23258 * case when this difference is significant is when using css selectors that rely on an element's
23259 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
23260 *
23261 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
23262 * is created when the element is restored. The scope created within `ngIf` inherits from
23263 * its parent scope using
23264 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
23265 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
23266 * a javascript primitive defined in the parent scope. In this case any modifications made to the
23267 * variable within the child scope will override (hide) the value in the parent scope.
23268 *
23269 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
23270 * is if an element's class attribute is directly modified after it's compiled, using something like
23271 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
23272 * the added class will be lost because the original compiled state is used to regenerate the element.
23273 *
23274 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
23275 * and `leave` effects.
23276 *
23277 * @animations
23278 * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
23279 * leave - happens just before the `ngIf` contents are removed from the DOM
23280 *
23281 * @element ANY
23282 * @scope
23283 * @priority 600
23284 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
23285 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
23286 * element is added to the DOM tree.
23287 *
23288 * @example
23289 <example module="ngAnimate" deps="angular-animate.js" animations="true">
23290 <file name="index.html">
23291 Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /><br/>
23292 Show when checked:
23293 <span ng-if="checked" class="animate-if">
23294 This is removed when the checkbox is unchecked.
23295 </span>
23296 </file>
23297 <file name="animations.css">
23298 .animate-if {
23299 background:white;
23300 border:1px solid black;
23301 padding:10px;
23302 }
23303
23304 .animate-if.ng-enter, .animate-if.ng-leave {
23305 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23306 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23307 }
23308
23309 .animate-if.ng-enter,
23310 .animate-if.ng-leave.ng-leave-active {
23311 opacity:0;
23312 }
23313
23314 .animate-if.ng-leave,
23315 .animate-if.ng-enter.ng-enter-active {
23316 opacity:1;
23317 }
23318 </file>
23319 </example>
23320 */
23321var ngIfDirective = ['$animate', function($animate) {
23322 return {
23323 multiElement: true,
23324 transclude: 'element',
23325 priority: 600,
23326 terminal: true,
23327 restrict: 'A',
23328 $$tlb: true,
23329 link: function($scope, $element, $attr, ctrl, $transclude) {
23330 var block, childScope, previousElements;
23331 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
23332
23333 if (value) {
23334 if (!childScope) {
23335 $transclude(function(clone, newScope) {
23336 childScope = newScope;
23337 clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
23338 // Note: We only need the first/last node of the cloned nodes.
23339 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
23340 // by a directive with templateUrl when its template arrives.
23341 block = {
23342 clone: clone
23343 };
23344 $animate.enter(clone, $element.parent(), $element);
23345 });
23346 }
23347 } else {
23348 if (previousElements) {
23349 previousElements.remove();
23350 previousElements = null;
23351 }
23352 if (childScope) {
23353 childScope.$destroy();
23354 childScope = null;
23355 }
23356 if (block) {
23357 previousElements = getBlockNodes(block.clone);
23358 $animate.leave(previousElements).then(function() {
23359 previousElements = null;
23360 });
23361 block = null;
23362 }
23363 }
23364 });
23365 }
23366 };
23367}];
23368
23369/**
23370 * @ngdoc directive
23371 * @name ngInclude
23372 * @restrict ECA
23373 *
23374 * @description
23375 * Fetches, compiles and includes an external HTML fragment.
23376 *
23377 * By default, the template URL is restricted to the same domain and protocol as the
23378 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
23379 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
23380 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
23381 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
23382 * ng.$sce Strict Contextual Escaping}.
23383 *
23384 * In addition, the browser's
23385 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
23386 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
23387 * policy may further restrict whether the template is successfully loaded.
23388 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
23389 * access on some browsers.
23390 *
23391 * @animations
23392 * enter - animation is used to bring new content into the browser.
23393 * leave - animation is used to animate existing content away.
23394 *
23395 * The enter and leave animation occur concurrently.
23396 *
23397 * @scope
23398 * @priority 400
23399 *
23400 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
23401 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
23402 * @param {string=} onload Expression to evaluate when a new partial is loaded.
23403 *
23404 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
23405 * $anchorScroll} to scroll the viewport after the content is loaded.
23406 *
23407 * - If the attribute is not set, disable scrolling.
23408 * - If the attribute is set without value, enable scrolling.
23409 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
23410 *
23411 * @example
23412 <example module="includeExample" deps="angular-animate.js" animations="true">
23413 <file name="index.html">
23414 <div ng-controller="ExampleController">
23415 <select ng-model="template" ng-options="t.name for t in templates">
23416 <option value="">(blank)</option>
23417 </select>
23418 url of the template: <tt>{{template.url}}</tt>
23419 <hr/>
23420 <div class="slide-animate-container">
23421 <div class="slide-animate" ng-include="template.url"></div>
23422 </div>
23423 </div>
23424 </file>
23425 <file name="script.js">
23426 angular.module('includeExample', ['ngAnimate'])
23427 .controller('ExampleController', ['$scope', function($scope) {
23428 $scope.templates =
23429 [ { name: 'template1.html', url: 'template1.html'},
23430 { name: 'template2.html', url: 'template2.html'} ];
23431 $scope.template = $scope.templates[0];
23432 }]);
23433 </file>
23434 <file name="template1.html">
23435 Content of template1.html
23436 </file>
23437 <file name="template2.html">
23438 Content of template2.html
23439 </file>
23440 <file name="animations.css">
23441 .slide-animate-container {
23442 position:relative;
23443 background:white;
23444 border:1px solid black;
23445 height:40px;
23446 overflow:hidden;
23447 }
23448
23449 .slide-animate {
23450 padding:10px;
23451 }
23452
23453 .slide-animate.ng-enter, .slide-animate.ng-leave {
23454 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23455 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
23456
23457 position:absolute;
23458 top:0;
23459 left:0;
23460 right:0;
23461 bottom:0;
23462 display:block;
23463 padding:10px;
23464 }
23465
23466 .slide-animate.ng-enter {
23467 top:-50px;
23468 }
23469 .slide-animate.ng-enter.ng-enter-active {
23470 top:0;
23471 }
23472
23473 .slide-animate.ng-leave {
23474 top:0;
23475 }
23476 .slide-animate.ng-leave.ng-leave-active {
23477 top:50px;
23478 }
23479 </file>
23480 <file name="protractor.js" type="protractor">
23481 var templateSelect = element(by.model('template'));
23482 var includeElem = element(by.css('[ng-include]'));
23483
23484 it('should load template1.html', function() {
23485 expect(includeElem.getText()).toMatch(/Content of template1.html/);
23486 });
23487
23488 it('should load template2.html', function() {
23489 if (browser.params.browser == 'firefox') {
23490 // Firefox can't handle using selects
23491 // See https://github.com/angular/protractor/issues/480
23492 return;
23493 }
23494 templateSelect.click();
23495 templateSelect.all(by.css('option')).get(2).click();
23496 expect(includeElem.getText()).toMatch(/Content of template2.html/);
23497 });
23498
23499 it('should change to blank', function() {
23500 if (browser.params.browser == 'firefox') {
23501 // Firefox can't handle using selects
23502 return;
23503 }
23504 templateSelect.click();
23505 templateSelect.all(by.css('option')).get(0).click();
23506 expect(includeElem.isPresent()).toBe(false);
23507 });
23508 </file>
23509 </example>
23510 */
23511
23512
23513/**
23514 * @ngdoc event
23515 * @name ngInclude#$includeContentRequested
23516 * @eventType emit on the scope ngInclude was declared in
23517 * @description
23518 * Emitted every time the ngInclude content is requested.
23519 *
23520 * @param {Object} angularEvent Synthetic event object.
23521 * @param {String} src URL of content to load.
23522 */
23523
23524
23525/**
23526 * @ngdoc event
23527 * @name ngInclude#$includeContentLoaded
23528 * @eventType emit on the current ngInclude scope
23529 * @description
23530 * Emitted every time the ngInclude content is reloaded.
23531 *
23532 * @param {Object} angularEvent Synthetic event object.
23533 * @param {String} src URL of content to load.
23534 */
23535
23536
23537/**
23538 * @ngdoc event
23539 * @name ngInclude#$includeContentError
23540 * @eventType emit on the scope ngInclude was declared in
23541 * @description
23542 * Emitted when a template HTTP request yields an erronous response (status < 200 || status > 299)
23543 *
23544 * @param {Object} angularEvent Synthetic event object.
23545 * @param {String} src URL of content to load.
23546 */
23547var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate', '$sce',
23548 function($templateRequest, $anchorScroll, $animate, $sce) {
23549 return {
23550 restrict: 'ECA',
23551 priority: 400,
23552 terminal: true,
23553 transclude: 'element',
23554 controller: angular.noop,
23555 compile: function(element, attr) {
23556 var srcExp = attr.ngInclude || attr.src,
23557 onloadExp = attr.onload || '',
23558 autoScrollExp = attr.autoscroll;
23559
23560 return function(scope, $element, $attr, ctrl, $transclude) {
23561 var changeCounter = 0,
23562 currentScope,
23563 previousElement,
23564 currentElement;
23565
23566 var cleanupLastIncludeContent = function() {
23567 if (previousElement) {
23568 previousElement.remove();
23569 previousElement = null;
23570 }
23571 if (currentScope) {
23572 currentScope.$destroy();
23573 currentScope = null;
23574 }
23575 if (currentElement) {
23576 $animate.leave(currentElement).then(function() {
23577 previousElement = null;
23578 });
23579 previousElement = currentElement;
23580 currentElement = null;
23581 }
23582 };
23583
23584 scope.$watch($sce.parseAsResourceUrl(srcExp), function ngIncludeWatchAction(src) {
23585 var afterAnimation = function() {
23586 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
23587 $anchorScroll();
23588 }
23589 };
23590 var thisChangeId = ++changeCounter;
23591
23592 if (src) {
23593 //set the 2nd param to true to ignore the template request error so that the inner
23594 //contents and scope can be cleaned up.
23595 $templateRequest(src, true).then(function(response) {
23596 if (thisChangeId !== changeCounter) return;
23597 var newScope = scope.$new();
23598 ctrl.template = response;
23599
23600 // Note: This will also link all children of ng-include that were contained in the original
23601 // html. If that content contains controllers, ... they could pollute/change the scope.
23602 // However, using ng-include on an element with additional content does not make sense...
23603 // Note: We can't remove them in the cloneAttchFn of $transclude as that
23604 // function is called before linking the content, which would apply child
23605 // directives to non existing elements.
23606 var clone = $transclude(newScope, function(clone) {
23607 cleanupLastIncludeContent();
23608 $animate.enter(clone, null, $element).then(afterAnimation);
23609 });
23610
23611 currentScope = newScope;
23612 currentElement = clone;
23613
23614 currentScope.$emit('$includeContentLoaded', src);
23615 scope.$eval(onloadExp);
23616 }, function() {
23617 if (thisChangeId === changeCounter) {
23618 cleanupLastIncludeContent();
23619 scope.$emit('$includeContentError', src);
23620 }
23621 });
23622 scope.$emit('$includeContentRequested', src);
23623 } else {
23624 cleanupLastIncludeContent();
23625 ctrl.template = null;
23626 }
23627 });
23628 };
23629 }
23630 };
23631}];
23632
23633// This directive is called during the $transclude call of the first `ngInclude` directive.
23634// It will replace and compile the content of the element with the loaded template.
23635// We need this directive so that the element content is already filled when
23636// the link function of another directive on the same element as ngInclude
23637// is called.
23638var ngIncludeFillContentDirective = ['$compile',
23639 function($compile) {
23640 return {
23641 restrict: 'ECA',
23642 priority: -400,
23643 require: 'ngInclude',
23644 link: function(scope, $element, $attr, ctrl) {
23645 if (/SVG/.test($element[0].toString())) {
23646 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
23647 // support innerHTML, so detect this here and try to generate the contents
23648 // specially.
23649 $element.empty();
23650 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
23651 function namespaceAdaptedClone(clone) {
23652 $element.append(clone);
23653 }, {futureParentElement: $element});
23654 return;
23655 }
23656
23657 $element.html(ctrl.template);
23658 $compile($element.contents())(scope);
23659 }
23660 };
23661 }];
23662
23663/**
23664 * @ngdoc directive
23665 * @name ngInit
23666 * @restrict AC
23667 *
23668 * @description
23669 * The `ngInit` directive allows you to evaluate an expression in the
23670 * current scope.
23671 *
23672 * <div class="alert alert-error">
23673 * The only appropriate use of `ngInit` is for aliasing special properties of
23674 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below. Besides this case, you
23675 * should use {@link guide/controller controllers} rather than `ngInit`
23676 * to initialize values on a scope.
23677 * </div>
23678 * <div class="alert alert-warning">
23679 * **Note**: If you have assignment in `ngInit` along with {@link ng.$filter `$filter`}, make
23680 * sure you have parenthesis for correct precedence:
23681 * <pre class="prettyprint">
23682 * <div ng-init="test1 = (data | orderBy:'name')"></div>
23683 * </pre>
23684 * </div>
23685 *
23686 * @priority 450
23687 *
23688 * @element ANY
23689 * @param {expression} ngInit {@link guide/expression Expression} to eval.
23690 *
23691 * @example
23692 <example module="initExample">
23693 <file name="index.html">
23694 <script>
23695 angular.module('initExample', [])
23696 .controller('ExampleController', ['$scope', function($scope) {
23697 $scope.list = [['a', 'b'], ['c', 'd']];
23698 }]);
23699 </script>
23700 <div ng-controller="ExampleController">
23701 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
23702 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
23703 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
23704 </div>
23705 </div>
23706 </div>
23707 </file>
23708 <file name="protractor.js" type="protractor">
23709 it('should alias index positions', function() {
23710 var elements = element.all(by.css('.example-init'));
23711 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
23712 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
23713 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
23714 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
23715 });
23716 </file>
23717 </example>
23718 */
23719var ngInitDirective = ngDirective({
23720 priority: 450,
23721 compile: function() {
23722 return {
23723 pre: function(scope, element, attrs) {
23724 scope.$eval(attrs.ngInit);
23725 }
23726 };
23727 }
23728});
23729
23730/**
23731 * @ngdoc directive
23732 * @name ngNonBindable
23733 * @restrict AC
23734 * @priority 1000
23735 *
23736 * @description
23737 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
23738 * DOM element. This is useful if the element contains what appears to be Angular directives and
23739 * bindings but which should be ignored by Angular. This could be the case if you have a site that
23740 * displays snippets of code, for instance.
23741 *
23742 * @element ANY
23743 *
23744 * @example
23745 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
23746 * but the one wrapped in `ngNonBindable` is left alone.
23747 *
23748 * @example
23749 <example>
23750 <file name="index.html">
23751 <div>Normal: {{1 + 2}}</div>
23752 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
23753 </file>
23754 <file name="protractor.js" type="protractor">
23755 it('should check ng-non-bindable', function() {
23756 expect(element(by.binding('1 + 2')).getText()).toContain('3');
23757 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
23758 });
23759 </file>
23760 </example>
23761 */
23762var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
23763
23764/**
23765 * @ngdoc directive
23766 * @name ngPluralize
23767 * @restrict EA
23768 *
23769 * @description
23770 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
23771 * These rules are bundled with angular.js, but can be overridden
23772 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
23773 * by specifying the mappings between
23774 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23775 * and the strings to be displayed.
23776 *
23777 * # Plural categories and explicit number rules
23778 * There are two
23779 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
23780 * in Angular's default en-US locale: "one" and "other".
23781 *
23782 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
23783 * any number that is not 1), an explicit number rule can only match one number. For example, the
23784 * explicit number rule for "3" matches the number 3. There are examples of plural categories
23785 * and explicit number rules throughout the rest of this documentation.
23786 *
23787 * # Configuring ngPluralize
23788 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
23789 * You can also provide an optional attribute, `offset`.
23790 *
23791 * The value of the `count` attribute can be either a string or an {@link guide/expression
23792 * Angular expression}; these are evaluated on the current scope for its bound value.
23793 *
23794 * The `when` attribute specifies the mappings between plural categories and the actual
23795 * string to be displayed. The value of the attribute should be a JSON object.
23796 *
23797 * The following example shows how to configure ngPluralize:
23798 *
23799 * ```html
23800 * <ng-pluralize count="personCount"
23801 when="{'0': 'Nobody is viewing.',
23802 * 'one': '1 person is viewing.',
23803 * 'other': '{} people are viewing.'}">
23804 * </ng-pluralize>
23805 *```
23806 *
23807 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
23808 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
23809 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
23810 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
23811 * show "a dozen people are viewing".
23812 *
23813 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
23814 * into pluralized strings. In the previous example, Angular will replace `{}` with
23815 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
23816 * for <span ng-non-bindable>{{numberExpression}}</span>.
23817 *
23818 * # Configuring ngPluralize with offset
23819 * The `offset` attribute allows further customization of pluralized text, which can result in
23820 * a better user experience. For example, instead of the message "4 people are viewing this document",
23821 * you might display "John, Kate and 2 others are viewing this document".
23822 * The offset attribute allows you to offset a number by any desired value.
23823 * Let's take a look at an example:
23824 *
23825 * ```html
23826 * <ng-pluralize count="personCount" offset=2
23827 * when="{'0': 'Nobody is viewing.',
23828 * '1': '{{person1}} is viewing.',
23829 * '2': '{{person1}} and {{person2}} are viewing.',
23830 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
23831 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
23832 * </ng-pluralize>
23833 * ```
23834 *
23835 * Notice that we are still using two plural categories(one, other), but we added
23836 * three explicit number rules 0, 1 and 2.
23837 * When one person, perhaps John, views the document, "John is viewing" will be shown.
23838 * When three people view the document, no explicit number rule is found, so
23839 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
23840 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
23841 * is shown.
23842 *
23843 * Note that when you specify offsets, you must provide explicit number rules for
23844 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
23845 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
23846 * plural categories "one" and "other".
23847 *
23848 * @param {string|expression} count The variable to be bound to.
23849 * @param {string} when The mapping between plural category to its corresponding strings.
23850 * @param {number=} offset Offset to deduct from the total number.
23851 *
23852 * @example
23853 <example module="pluralizeExample">
23854 <file name="index.html">
23855 <script>
23856 angular.module('pluralizeExample', [])
23857 .controller('ExampleController', ['$scope', function($scope) {
23858 $scope.person1 = 'Igor';
23859 $scope.person2 = 'Misko';
23860 $scope.personCount = 1;
23861 }]);
23862 </script>
23863 <div ng-controller="ExampleController">
23864 Person 1:<input type="text" ng-model="person1" value="Igor" /><br/>
23865 Person 2:<input type="text" ng-model="person2" value="Misko" /><br/>
23866 Number of People:<input type="text" ng-model="personCount" value="1" /><br/>
23867
23868 <!--- Example with simple pluralization rules for en locale --->
23869 Without Offset:
23870 <ng-pluralize count="personCount"
23871 when="{'0': 'Nobody is viewing.',
23872 'one': '1 person is viewing.',
23873 'other': '{} people are viewing.'}">
23874 </ng-pluralize><br>
23875
23876 <!--- Example with offset --->
23877 With Offset(2):
23878 <ng-pluralize count="personCount" offset=2
23879 when="{'0': 'Nobody is viewing.',
23880 '1': '{{person1}} is viewing.',
23881 '2': '{{person1}} and {{person2}} are viewing.',
23882 'one': '{{person1}}, {{person2}} and one other person are viewing.',
23883 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
23884 </ng-pluralize>
23885 </div>
23886 </file>
23887 <file name="protractor.js" type="protractor">
23888 it('should show correct pluralized string', function() {
23889 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
23890 var withOffset = element.all(by.css('ng-pluralize')).get(1);
23891 var countInput = element(by.model('personCount'));
23892
23893 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
23894 expect(withOffset.getText()).toEqual('Igor is viewing.');
23895
23896 countInput.clear();
23897 countInput.sendKeys('0');
23898
23899 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
23900 expect(withOffset.getText()).toEqual('Nobody is viewing.');
23901
23902 countInput.clear();
23903 countInput.sendKeys('2');
23904
23905 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
23906 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
23907
23908 countInput.clear();
23909 countInput.sendKeys('3');
23910
23911 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
23912 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
23913
23914 countInput.clear();
23915 countInput.sendKeys('4');
23916
23917 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
23918 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
23919 });
23920 it('should show data-bound names', function() {
23921 var withOffset = element.all(by.css('ng-pluralize')).get(1);
23922 var personCount = element(by.model('personCount'));
23923 var person1 = element(by.model('person1'));
23924 var person2 = element(by.model('person2'));
23925 personCount.clear();
23926 personCount.sendKeys('4');
23927 person1.clear();
23928 person1.sendKeys('Di');
23929 person2.clear();
23930 person2.sendKeys('Vojta');
23931 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
23932 });
23933 </file>
23934 </example>
23935 */
23936var ngPluralizeDirective = ['$locale', '$interpolate', function($locale, $interpolate) {
23937 var BRACE = /{}/g,
23938 IS_WHEN = /^when(Minus)?(.+)$/;
23939
23940 return {
23941 restrict: 'EA',
23942 link: function(scope, element, attr) {
23943 var numberExp = attr.count,
23944 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
23945 offset = attr.offset || 0,
23946 whens = scope.$eval(whenExp) || {},
23947 whensExpFns = {},
23948 startSymbol = $interpolate.startSymbol(),
23949 endSymbol = $interpolate.endSymbol(),
23950 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
23951 watchRemover = angular.noop,
23952 lastCount;
23953
23954 forEach(attr, function(expression, attributeName) {
23955 var tmpMatch = IS_WHEN.exec(attributeName);
23956 if (tmpMatch) {
23957 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
23958 whens[whenKey] = element.attr(attr.$attr[attributeName]);
23959 }
23960 });
23961 forEach(whens, function(expression, key) {
23962 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
23963
23964 });
23965
23966 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
23967 var count = parseFloat(newVal);
23968 var countIsNaN = isNaN(count);
23969
23970 if (!countIsNaN && !(count in whens)) {
23971 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
23972 // Otherwise, check it against pluralization rules in $locale service.
23973 count = $locale.pluralCat(count - offset);
23974 }
23975
23976 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
23977 // In JS `NaN !== NaN`, so we have to exlicitly check.
23978 if ((count !== lastCount) && !(countIsNaN && isNaN(lastCount))) {
23979 watchRemover();
23980 watchRemover = scope.$watch(whensExpFns[count], updateElementText);
23981 lastCount = count;
23982 }
23983 });
23984
23985 function updateElementText(newText) {
23986 element.text(newText || '');
23987 }
23988 }
23989 };
23990}];
23991
23992/**
23993 * @ngdoc directive
23994 * @name ngRepeat
23995 *
23996 * @description
23997 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
23998 * instance gets its own scope, where the given loop variable is set to the current collection item,
23999 * and `$index` is set to the item index or key.
24000 *
24001 * Special properties are exposed on the local scope of each template instance, including:
24002 *
24003 * | Variable | Type | Details |
24004 * |-----------|-----------------|-----------------------------------------------------------------------------|
24005 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
24006 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
24007 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
24008 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
24009 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
24010 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
24011 *
24012 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
24013 * This may be useful when, for instance, nesting ngRepeats.
24014 *
24015 * # Special repeat start and end points
24016 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
24017 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
24018 * The **ng-repeat-start** directive works the same as **ng-repeat**, but will repeat all the HTML code (including the tag it's defined on)
24019 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
24020 *
24021 * The example below makes use of this feature:
24022 * ```html
24023 * <header ng-repeat-start="item in items">
24024 * Header {{ item }}
24025 * </header>
24026 * <div class="body">
24027 * Body {{ item }}
24028 * </div>
24029 * <footer ng-repeat-end>
24030 * Footer {{ item }}
24031 * </footer>
24032 * ```
24033 *
24034 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
24035 * ```html
24036 * <header>
24037 * Header A
24038 * </header>
24039 * <div class="body">
24040 * Body A
24041 * </div>
24042 * <footer>
24043 * Footer A
24044 * </footer>
24045 * <header>
24046 * Header B
24047 * </header>
24048 * <div class="body">
24049 * Body B
24050 * </div>
24051 * <footer>
24052 * Footer B
24053 * </footer>
24054 * ```
24055 *
24056 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
24057 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
24058 *
24059 * @animations
24060 * **.enter** - when a new item is added to the list or when an item is revealed after a filter
24061 *
24062 * **.leave** - when an item is removed from the list or when an item is filtered out
24063 *
24064 * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
24065 *
24066 * @element ANY
24067 * @scope
24068 * @priority 1000
24069 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
24070 * formats are currently supported:
24071 *
24072 * * `variable in expression` – where variable is the user defined loop variable and `expression`
24073 * is a scope expression giving the collection to enumerate.
24074 *
24075 * For example: `album in artist.albums`.
24076 *
24077 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
24078 * and `expression` is the scope expression giving the collection to enumerate.
24079 *
24080 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
24081 *
24082 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
24083 * which can be used to associate the objects in the collection with the DOM elements. If no tracking function
24084 * is specified the ng-repeat associates elements by identity in the collection. It is an error to have
24085 * more than one tracking function to resolve to the same key. (This would mean that two distinct objects are
24086 * mapped to the same DOM element, which is not possible.) Filters should be applied to the expression,
24087 * before specifying a tracking expression.
24088 *
24089 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
24090 * will be associated by item identity in the array.
24091 *
24092 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
24093 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
24094 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
24095 * element in the same way in the DOM.
24096 *
24097 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
24098 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
24099 * property is same.
24100 *
24101 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
24102 * to items in conjunction with a tracking expression.
24103 *
24104 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
24105 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
24106 * when a filter is active on the repeater, but the filtered result set is empty.
24107 *
24108 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
24109 * the items have been processed through the filter.
24110 *
24111 * @example
24112 * This example initializes the scope to a list of names and
24113 * then uses `ngRepeat` to display every person:
24114 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24115 <file name="index.html">
24116 <div ng-init="friends = [
24117 {name:'John', age:25, gender:'boy'},
24118 {name:'Jessie', age:30, gender:'girl'},
24119 {name:'Johanna', age:28, gender:'girl'},
24120 {name:'Joy', age:15, gender:'girl'},
24121 {name:'Mary', age:28, gender:'girl'},
24122 {name:'Peter', age:95, gender:'boy'},
24123 {name:'Sebastian', age:50, gender:'boy'},
24124 {name:'Erika', age:27, gender:'girl'},
24125 {name:'Patrick', age:40, gender:'boy'},
24126 {name:'Samantha', age:60, gender:'girl'}
24127 ]">
24128 I have {{friends.length}} friends. They are:
24129 <input type="search" ng-model="q" placeholder="filter friends..." />
24130 <ul class="example-animate-container">
24131 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
24132 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
24133 </li>
24134 <li class="animate-repeat" ng-if="results.length == 0">
24135 <strong>No results found...</strong>
24136 </li>
24137 </ul>
24138 </div>
24139 </file>
24140 <file name="animations.css">
24141 .example-animate-container {
24142 background:white;
24143 border:1px solid black;
24144 list-style:none;
24145 margin:0;
24146 padding:0 10px;
24147 }
24148
24149 .animate-repeat {
24150 line-height:40px;
24151 list-style:none;
24152 box-sizing:border-box;
24153 }
24154
24155 .animate-repeat.ng-move,
24156 .animate-repeat.ng-enter,
24157 .animate-repeat.ng-leave {
24158 -webkit-transition:all linear 0.5s;
24159 transition:all linear 0.5s;
24160 }
24161
24162 .animate-repeat.ng-leave.ng-leave-active,
24163 .animate-repeat.ng-move,
24164 .animate-repeat.ng-enter {
24165 opacity:0;
24166 max-height:0;
24167 }
24168
24169 .animate-repeat.ng-leave,
24170 .animate-repeat.ng-move.ng-move-active,
24171 .animate-repeat.ng-enter.ng-enter-active {
24172 opacity:1;
24173 max-height:40px;
24174 }
24175 </file>
24176 <file name="protractor.js" type="protractor">
24177 var friends = element.all(by.repeater('friend in friends'));
24178
24179 it('should render initial data set', function() {
24180 expect(friends.count()).toBe(10);
24181 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
24182 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
24183 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
24184 expect(element(by.binding('friends.length')).getText())
24185 .toMatch("I have 10 friends. They are:");
24186 });
24187
24188 it('should update repeater when filter predicate changes', function() {
24189 expect(friends.count()).toBe(10);
24190
24191 element(by.model('q')).sendKeys('ma');
24192
24193 expect(friends.count()).toBe(2);
24194 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
24195 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
24196 });
24197 </file>
24198 </example>
24199 */
24200var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
24201 var NG_REMOVED = '$$NG_REMOVED';
24202 var ngRepeatMinErr = minErr('ngRepeat');
24203
24204 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
24205 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
24206 scope[valueIdentifier] = value;
24207 if (keyIdentifier) scope[keyIdentifier] = key;
24208 scope.$index = index;
24209 scope.$first = (index === 0);
24210 scope.$last = (index === (arrayLength - 1));
24211 scope.$middle = !(scope.$first || scope.$last);
24212 // jshint bitwise: false
24213 scope.$odd = !(scope.$even = (index&1) === 0);
24214 // jshint bitwise: true
24215 };
24216
24217 var getBlockStart = function(block) {
24218 return block.clone[0];
24219 };
24220
24221 var getBlockEnd = function(block) {
24222 return block.clone[block.clone.length - 1];
24223 };
24224
24225
24226 return {
24227 restrict: 'A',
24228 multiElement: true,
24229 transclude: 'element',
24230 priority: 1000,
24231 terminal: true,
24232 $$tlb: true,
24233 compile: function ngRepeatCompile($element, $attr) {
24234 var expression = $attr.ngRepeat;
24235 var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
24236
24237 var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
24238
24239 if (!match) {
24240 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
24241 expression);
24242 }
24243
24244 var lhs = match[1];
24245 var rhs = match[2];
24246 var aliasAs = match[3];
24247 var trackByExp = match[4];
24248
24249 match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
24250
24251 if (!match) {
24252 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
24253 lhs);
24254 }
24255 var valueIdentifier = match[3] || match[1];
24256 var keyIdentifier = match[2];
24257
24258 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
24259 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent)$/.test(aliasAs))) {
24260 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
24261 aliasAs);
24262 }
24263
24264 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
24265 var hashFnLocals = {$id: hashKey};
24266
24267 if (trackByExp) {
24268 trackByExpGetter = $parse(trackByExp);
24269 } else {
24270 trackByIdArrayFn = function(key, value) {
24271 return hashKey(value);
24272 };
24273 trackByIdObjFn = function(key) {
24274 return key;
24275 };
24276 }
24277
24278 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
24279
24280 if (trackByExpGetter) {
24281 trackByIdExpFn = function(key, value, index) {
24282 // assign key, value, and $index to the locals so that they can be used in hash functions
24283 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
24284 hashFnLocals[valueIdentifier] = value;
24285 hashFnLocals.$index = index;
24286 return trackByExpGetter($scope, hashFnLocals);
24287 };
24288 }
24289
24290 // Store a list of elements from previous run. This is a hash where key is the item from the
24291 // iterator, and the value is objects with following properties.
24292 // - scope: bound scope
24293 // - element: previous element.
24294 // - index: position
24295 //
24296 // We are using no-proto object so that we don't need to guard against inherited props via
24297 // hasOwnProperty.
24298 var lastBlockMap = createMap();
24299
24300 //watch props
24301 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
24302 var index, length,
24303 previousNode = $element[0], // node that cloned nodes should be inserted after
24304 // initialized to the comment node anchor
24305 nextNode,
24306 // Same as lastBlockMap but it has the current state. It will become the
24307 // lastBlockMap on the next iteration.
24308 nextBlockMap = createMap(),
24309 collectionLength,
24310 key, value, // key/value of iteration
24311 trackById,
24312 trackByIdFn,
24313 collectionKeys,
24314 block, // last object information {scope, element, id}
24315 nextBlockOrder,
24316 elementsToRemove;
24317
24318 if (aliasAs) {
24319 $scope[aliasAs] = collection;
24320 }
24321
24322 if (isArrayLike(collection)) {
24323 collectionKeys = collection;
24324 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
24325 } else {
24326 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
24327 // if object, extract keys, sort them and use to determine order of iteration over obj props
24328 collectionKeys = [];
24329 for (var itemKey in collection) {
24330 if (collection.hasOwnProperty(itemKey) && itemKey.charAt(0) != '$') {
24331 collectionKeys.push(itemKey);
24332 }
24333 }
24334 collectionKeys.sort();
24335 }
24336
24337 collectionLength = collectionKeys.length;
24338 nextBlockOrder = new Array(collectionLength);
24339
24340 // locate existing items
24341 for (index = 0; index < collectionLength; index++) {
24342 key = (collection === collectionKeys) ? index : collectionKeys[index];
24343 value = collection[key];
24344 trackById = trackByIdFn(key, value, index);
24345 if (lastBlockMap[trackById]) {
24346 // found previously seen block
24347 block = lastBlockMap[trackById];
24348 delete lastBlockMap[trackById];
24349 nextBlockMap[trackById] = block;
24350 nextBlockOrder[index] = block;
24351 } else if (nextBlockMap[trackById]) {
24352 // if collision detected. restore lastBlockMap and throw an error
24353 forEach(nextBlockOrder, function(block) {
24354 if (block && block.scope) lastBlockMap[block.id] = block;
24355 });
24356 throw ngRepeatMinErr('dupes',
24357 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
24358 expression, trackById, value);
24359 } else {
24360 // new never before seen block
24361 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
24362 nextBlockMap[trackById] = true;
24363 }
24364 }
24365
24366 // remove leftover items
24367 for (var blockKey in lastBlockMap) {
24368 block = lastBlockMap[blockKey];
24369 elementsToRemove = getBlockNodes(block.clone);
24370 $animate.leave(elementsToRemove);
24371 if (elementsToRemove[0].parentNode) {
24372 // if the element was not removed yet because of pending animation, mark it as deleted
24373 // so that we can ignore it later
24374 for (index = 0, length = elementsToRemove.length; index < length; index++) {
24375 elementsToRemove[index][NG_REMOVED] = true;
24376 }
24377 }
24378 block.scope.$destroy();
24379 }
24380
24381 // we are not using forEach for perf reasons (trying to avoid #call)
24382 for (index = 0; index < collectionLength; index++) {
24383 key = (collection === collectionKeys) ? index : collectionKeys[index];
24384 value = collection[key];
24385 block = nextBlockOrder[index];
24386
24387 if (block.scope) {
24388 // if we have already seen this object, then we need to reuse the
24389 // associated scope/element
24390
24391 nextNode = previousNode;
24392
24393 // skip nodes that are already pending removal via leave animation
24394 do {
24395 nextNode = nextNode.nextSibling;
24396 } while (nextNode && nextNode[NG_REMOVED]);
24397
24398 if (getBlockStart(block) != nextNode) {
24399 // existing item which got moved
24400 $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
24401 }
24402 previousNode = getBlockEnd(block);
24403 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24404 } else {
24405 // new item which we don't know about
24406 $transclude(function ngRepeatTransclude(clone, scope) {
24407 block.scope = scope;
24408 // http://jsperf.com/clone-vs-createcomment
24409 var endNode = ngRepeatEndComment.cloneNode(false);
24410 clone[clone.length++] = endNode;
24411
24412 // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
24413 $animate.enter(clone, null, jqLite(previousNode));
24414 previousNode = endNode;
24415 // Note: We only need the first/last node of the cloned nodes.
24416 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24417 // by a directive with templateUrl when its template arrives.
24418 block.clone = clone;
24419 nextBlockMap[block.id] = block;
24420 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
24421 });
24422 }
24423 }
24424 lastBlockMap = nextBlockMap;
24425 });
24426 };
24427 }
24428 };
24429}];
24430
24431var NG_HIDE_CLASS = 'ng-hide';
24432var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
24433/**
24434 * @ngdoc directive
24435 * @name ngShow
24436 *
24437 * @description
24438 * The `ngShow` directive shows or hides the given HTML element based on the expression
24439 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
24440 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24441 * in AngularJS and sets the display style to none (using an !important flag).
24442 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24443 *
24444 * ```html
24445 * <!-- when $scope.myValue is truthy (element is visible) -->
24446 * <div ng-show="myValue"></div>
24447 *
24448 * <!-- when $scope.myValue is falsy (element is hidden) -->
24449 * <div ng-show="myValue" class="ng-hide"></div>
24450 * ```
24451 *
24452 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
24453 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
24454 * from the element causing the element not to appear hidden.
24455 *
24456 * ## Why is !important used?
24457 *
24458 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24459 * can be easily overridden by heavier selectors. For example, something as simple
24460 * as changing the display style on a HTML list item would make hidden elements appear visible.
24461 * This also becomes a bigger issue when dealing with CSS frameworks.
24462 *
24463 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24464 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24465 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24466 *
24467 * ### Overriding `.ng-hide`
24468 *
24469 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24470 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24471 * class in CSS:
24472 *
24473 * ```css
24474 * .ng-hide {
24475 * /&#42; this is just another form of hiding an element &#42;/
24476 * display: block!important;
24477 * position: absolute;
24478 * top: -9999px;
24479 * left: -9999px;
24480 * }
24481 * ```
24482 *
24483 * By default you don't need to override in CSS anything and the animations will work around the display style.
24484 *
24485 * ## A note about animations with `ngShow`
24486 *
24487 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24488 * is true and false. This system works like the animation system present with ngClass except that
24489 * you must also include the !important flag to override the display property
24490 * so that you can perform an animation when the element is hidden during the time of the animation.
24491 *
24492 * ```css
24493 * //
24494 * //a working example can be found at the bottom of this page
24495 * //
24496 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24497 * /&#42; this is required as of 1.3x to properly
24498 * apply all styling in a show/hide animation &#42;/
24499 * transition: 0s linear all;
24500 * }
24501 *
24502 * .my-element.ng-hide-add-active,
24503 * .my-element.ng-hide-remove-active {
24504 * /&#42; the transition is defined in the active class &#42;/
24505 * transition: 1s linear all;
24506 * }
24507 *
24508 * .my-element.ng-hide-add { ... }
24509 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24510 * .my-element.ng-hide-remove { ... }
24511 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24512 * ```
24513 *
24514 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24515 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24516 *
24517 * @animations
24518 * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
24519 * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
24520 *
24521 * @element ANY
24522 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
24523 * then the element is shown or hidden respectively.
24524 *
24525 * @example
24526 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24527 <file name="index.html">
24528 Click me: <input type="checkbox" ng-model="checked"><br/>
24529 <div>
24530 Show:
24531 <div class="check-element animate-show" ng-show="checked">
24532 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24533 </div>
24534 </div>
24535 <div>
24536 Hide:
24537 <div class="check-element animate-show" ng-hide="checked">
24538 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24539 </div>
24540 </div>
24541 </file>
24542 <file name="glyphicons.css">
24543 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24544 </file>
24545 <file name="animations.css">
24546 .animate-show {
24547 line-height: 20px;
24548 opacity: 1;
24549 padding: 10px;
24550 border: 1px solid black;
24551 background: white;
24552 }
24553
24554 .animate-show.ng-hide-add.ng-hide-add-active,
24555 .animate-show.ng-hide-remove.ng-hide-remove-active {
24556 -webkit-transition: all linear 0.5s;
24557 transition: all linear 0.5s;
24558 }
24559
24560 .animate-show.ng-hide {
24561 line-height: 0;
24562 opacity: 0;
24563 padding: 0 10px;
24564 }
24565
24566 .check-element {
24567 padding: 10px;
24568 border: 1px solid black;
24569 background: white;
24570 }
24571 </file>
24572 <file name="protractor.js" type="protractor">
24573 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24574 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24575
24576 it('should check ng-show / ng-hide', function() {
24577 expect(thumbsUp.isDisplayed()).toBeFalsy();
24578 expect(thumbsDown.isDisplayed()).toBeTruthy();
24579
24580 element(by.model('checked')).click();
24581
24582 expect(thumbsUp.isDisplayed()).toBeTruthy();
24583 expect(thumbsDown.isDisplayed()).toBeFalsy();
24584 });
24585 </file>
24586 </example>
24587 */
24588var ngShowDirective = ['$animate', function($animate) {
24589 return {
24590 restrict: 'A',
24591 multiElement: true,
24592 link: function(scope, element, attr) {
24593 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
24594 // we're adding a temporary, animation-specific class for ng-hide since this way
24595 // we can control when the element is actually displayed on screen without having
24596 // to have a global/greedy CSS selector that breaks when other animations are run.
24597 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
24598 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
24599 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
24600 });
24601 });
24602 }
24603 };
24604}];
24605
24606
24607/**
24608 * @ngdoc directive
24609 * @name ngHide
24610 *
24611 * @description
24612 * The `ngHide` directive shows or hides the given HTML element based on the expression
24613 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
24614 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
24615 * in AngularJS and sets the display style to none (using an !important flag).
24616 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24617 *
24618 * ```html
24619 * <!-- when $scope.myValue is truthy (element is hidden) -->
24620 * <div ng-hide="myValue" class="ng-hide"></div>
24621 *
24622 * <!-- when $scope.myValue is falsy (element is visible) -->
24623 * <div ng-hide="myValue"></div>
24624 * ```
24625 *
24626 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
24627 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
24628 * from the element causing the element not to appear hidden.
24629 *
24630 * ## Why is !important used?
24631 *
24632 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
24633 * can be easily overridden by heavier selectors. For example, something as simple
24634 * as changing the display style on a HTML list item would make hidden elements appear visible.
24635 * This also becomes a bigger issue when dealing with CSS frameworks.
24636 *
24637 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
24638 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
24639 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
24640 *
24641 * ### Overriding `.ng-hide`
24642 *
24643 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
24644 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
24645 * class in CSS:
24646 *
24647 * ```css
24648 * .ng-hide {
24649 * /&#42; this is just another form of hiding an element &#42;/
24650 * display: block!important;
24651 * position: absolute;
24652 * top: -9999px;
24653 * left: -9999px;
24654 * }
24655 * ```
24656 *
24657 * By default you don't need to override in CSS anything and the animations will work around the display style.
24658 *
24659 * ## A note about animations with `ngHide`
24660 *
24661 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
24662 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
24663 * CSS class is added and removed for you instead of your own CSS class.
24664 *
24665 * ```css
24666 * //
24667 * //a working example can be found at the bottom of this page
24668 * //
24669 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
24670 * transition: 0.5s linear all;
24671 * }
24672 *
24673 * .my-element.ng-hide-add { ... }
24674 * .my-element.ng-hide-add.ng-hide-add-active { ... }
24675 * .my-element.ng-hide-remove { ... }
24676 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
24677 * ```
24678 *
24679 * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
24680 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
24681 *
24682 * @animations
24683 * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
24684 * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
24685 *
24686 * @element ANY
24687 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
24688 * the element is shown or hidden respectively.
24689 *
24690 * @example
24691 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24692 <file name="index.html">
24693 Click me: <input type="checkbox" ng-model="checked"><br/>
24694 <div>
24695 Show:
24696 <div class="check-element animate-hide" ng-show="checked">
24697 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
24698 </div>
24699 </div>
24700 <div>
24701 Hide:
24702 <div class="check-element animate-hide" ng-hide="checked">
24703 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
24704 </div>
24705 </div>
24706 </file>
24707 <file name="glyphicons.css">
24708 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
24709 </file>
24710 <file name="animations.css">
24711 .animate-hide {
24712 -webkit-transition: all linear 0.5s;
24713 transition: all linear 0.5s;
24714 line-height: 20px;
24715 opacity: 1;
24716 padding: 10px;
24717 border: 1px solid black;
24718 background: white;
24719 }
24720
24721 .animate-hide.ng-hide {
24722 line-height: 0;
24723 opacity: 0;
24724 padding: 0 10px;
24725 }
24726
24727 .check-element {
24728 padding: 10px;
24729 border: 1px solid black;
24730 background: white;
24731 }
24732 </file>
24733 <file name="protractor.js" type="protractor">
24734 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
24735 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
24736
24737 it('should check ng-show / ng-hide', function() {
24738 expect(thumbsUp.isDisplayed()).toBeFalsy();
24739 expect(thumbsDown.isDisplayed()).toBeTruthy();
24740
24741 element(by.model('checked')).click();
24742
24743 expect(thumbsUp.isDisplayed()).toBeTruthy();
24744 expect(thumbsDown.isDisplayed()).toBeFalsy();
24745 });
24746 </file>
24747 </example>
24748 */
24749var ngHideDirective = ['$animate', function($animate) {
24750 return {
24751 restrict: 'A',
24752 multiElement: true,
24753 link: function(scope, element, attr) {
24754 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
24755 // The comment inside of the ngShowDirective explains why we add and
24756 // remove a temporary class for the show/hide animation
24757 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
24758 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
24759 });
24760 });
24761 }
24762 };
24763}];
24764
24765/**
24766 * @ngdoc directive
24767 * @name ngStyle
24768 * @restrict AC
24769 *
24770 * @description
24771 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
24772 *
24773 * @element ANY
24774 * @param {expression} ngStyle
24775 *
24776 * {@link guide/expression Expression} which evals to an
24777 * object whose keys are CSS style names and values are corresponding values for those CSS
24778 * keys.
24779 *
24780 * Since some CSS style names are not valid keys for an object, they must be quoted.
24781 * See the 'background-color' style in the example below.
24782 *
24783 * @example
24784 <example>
24785 <file name="index.html">
24786 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
24787 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
24788 <input type="button" value="clear" ng-click="myStyle={}">
24789 <br/>
24790 <span ng-style="myStyle">Sample Text</span>
24791 <pre>myStyle={{myStyle}}</pre>
24792 </file>
24793 <file name="style.css">
24794 span {
24795 color: black;
24796 }
24797 </file>
24798 <file name="protractor.js" type="protractor">
24799 var colorSpan = element(by.css('span'));
24800
24801 it('should check ng-style', function() {
24802 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
24803 element(by.css('input[value=\'set color\']')).click();
24804 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
24805 element(by.css('input[value=clear]')).click();
24806 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
24807 });
24808 </file>
24809 </example>
24810 */
24811var ngStyleDirective = ngDirective(function(scope, element, attr) {
24812 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
24813 if (oldStyles && (newStyles !== oldStyles)) {
24814 forEach(oldStyles, function(val, style) { element.css(style, '');});
24815 }
24816 if (newStyles) element.css(newStyles);
24817 }, true);
24818});
24819
24820/**
24821 * @ngdoc directive
24822 * @name ngSwitch
24823 * @restrict EA
24824 *
24825 * @description
24826 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
24827 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
24828 * as specified in the template.
24829 *
24830 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
24831 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
24832 * matches the value obtained from the evaluated expression. In other words, you define a container element
24833 * (where you place the directive), place an expression on the **`on="..."` attribute**
24834 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
24835 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
24836 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
24837 * attribute is displayed.
24838 *
24839 * <div class="alert alert-info">
24840 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
24841 * as literal string values to match against.
24842 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
24843 * value of the expression `$scope.someVal`.
24844 * </div>
24845
24846 * @animations
24847 * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
24848 * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
24849 *
24850 * @usage
24851 *
24852 * ```
24853 * <ANY ng-switch="expression">
24854 * <ANY ng-switch-when="matchValue1">...</ANY>
24855 * <ANY ng-switch-when="matchValue2">...</ANY>
24856 * <ANY ng-switch-default>...</ANY>
24857 * </ANY>
24858 * ```
24859 *
24860 *
24861 * @scope
24862 * @priority 1200
24863 * @param {*} ngSwitch|on expression to match against <tt>ng-switch-when</tt>.
24864 * On child elements add:
24865 *
24866 * * `ngSwitchWhen`: the case statement to match against. If match then this
24867 * case will be displayed. If the same match appears multiple times, all the
24868 * elements will be displayed.
24869 * * `ngSwitchDefault`: the default case when no other case match. If there
24870 * are multiple default cases, all of them will be displayed when no other
24871 * case match.
24872 *
24873 *
24874 * @example
24875 <example module="switchExample" deps="angular-animate.js" animations="true">
24876 <file name="index.html">
24877 <div ng-controller="ExampleController">
24878 <select ng-model="selection" ng-options="item for item in items">
24879 </select>
24880 <tt>selection={{selection}}</tt>
24881 <hr/>
24882 <div class="animate-switch-container"
24883 ng-switch on="selection">
24884 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
24885 <div class="animate-switch" ng-switch-when="home">Home Span</div>
24886 <div class="animate-switch" ng-switch-default>default</div>
24887 </div>
24888 </div>
24889 </file>
24890 <file name="script.js">
24891 angular.module('switchExample', ['ngAnimate'])
24892 .controller('ExampleController', ['$scope', function($scope) {
24893 $scope.items = ['settings', 'home', 'other'];
24894 $scope.selection = $scope.items[0];
24895 }]);
24896 </file>
24897 <file name="animations.css">
24898 .animate-switch-container {
24899 position:relative;
24900 background:white;
24901 border:1px solid black;
24902 height:40px;
24903 overflow:hidden;
24904 }
24905
24906 .animate-switch {
24907 padding:10px;
24908 }
24909
24910 .animate-switch.ng-animate {
24911 -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24912 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24913
24914 position:absolute;
24915 top:0;
24916 left:0;
24917 right:0;
24918 bottom:0;
24919 }
24920
24921 .animate-switch.ng-leave.ng-leave-active,
24922 .animate-switch.ng-enter {
24923 top:-50px;
24924 }
24925 .animate-switch.ng-leave,
24926 .animate-switch.ng-enter.ng-enter-active {
24927 top:0;
24928 }
24929 </file>
24930 <file name="protractor.js" type="protractor">
24931 var switchElem = element(by.css('[ng-switch]'));
24932 var select = element(by.model('selection'));
24933
24934 it('should start in settings', function() {
24935 expect(switchElem.getText()).toMatch(/Settings Div/);
24936 });
24937 it('should change to home', function() {
24938 select.all(by.css('option')).get(1).click();
24939 expect(switchElem.getText()).toMatch(/Home Span/);
24940 });
24941 it('should select default', function() {
24942 select.all(by.css('option')).get(2).click();
24943 expect(switchElem.getText()).toMatch(/default/);
24944 });
24945 </file>
24946 </example>
24947 */
24948var ngSwitchDirective = ['$animate', function($animate) {
24949 return {
24950 restrict: 'EA',
24951 require: 'ngSwitch',
24952
24953 // asks for $scope to fool the BC controller module
24954 controller: ['$scope', function ngSwitchController() {
24955 this.cases = {};
24956 }],
24957 link: function(scope, element, attr, ngSwitchController) {
24958 var watchExpr = attr.ngSwitch || attr.on,
24959 selectedTranscludes = [],
24960 selectedElements = [],
24961 previousLeaveAnimations = [],
24962 selectedScopes = [];
24963
24964 var spliceFactory = function(array, index) {
24965 return function() { array.splice(index, 1); };
24966 };
24967
24968 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
24969 var i, ii;
24970 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
24971 $animate.cancel(previousLeaveAnimations[i]);
24972 }
24973 previousLeaveAnimations.length = 0;
24974
24975 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
24976 var selected = getBlockNodes(selectedElements[i].clone);
24977 selectedScopes[i].$destroy();
24978 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
24979 promise.then(spliceFactory(previousLeaveAnimations, i));
24980 }
24981
24982 selectedElements.length = 0;
24983 selectedScopes.length = 0;
24984
24985 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
24986 forEach(selectedTranscludes, function(selectedTransclude) {
24987 selectedTransclude.transclude(function(caseElement, selectedScope) {
24988 selectedScopes.push(selectedScope);
24989 var anchor = selectedTransclude.element;
24990 caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
24991 var block = { clone: caseElement };
24992
24993 selectedElements.push(block);
24994 $animate.enter(caseElement, anchor.parent(), anchor);
24995 });
24996 });
24997 }
24998 });
24999 }
25000 };
25001}];
25002
25003var ngSwitchWhenDirective = ngDirective({
25004 transclude: 'element',
25005 priority: 1200,
25006 require: '^ngSwitch',
25007 multiElement: true,
25008 link: function(scope, element, attrs, ctrl, $transclude) {
25009 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
25010 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
25011 }
25012});
25013
25014var ngSwitchDefaultDirective = ngDirective({
25015 transclude: 'element',
25016 priority: 1200,
25017 require: '^ngSwitch',
25018 multiElement: true,
25019 link: function(scope, element, attr, ctrl, $transclude) {
25020 ctrl.cases['?'] = (ctrl.cases['?'] || []);
25021 ctrl.cases['?'].push({ transclude: $transclude, element: element });
25022 }
25023});
25024
25025/**
25026 * @ngdoc directive
25027 * @name ngTransclude
25028 * @restrict EAC
25029 *
25030 * @description
25031 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
25032 *
25033 * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
25034 *
25035 * @element ANY
25036 *
25037 * @example
25038 <example module="transcludeExample">
25039 <file name="index.html">
25040 <script>
25041 angular.module('transcludeExample', [])
25042 .directive('pane', function(){
25043 return {
25044 restrict: 'E',
25045 transclude: true,
25046 scope: { title:'@' },
25047 template: '<div style="border: 1px solid black;">' +
25048 '<div style="background-color: gray">{{title}}</div>' +
25049 '<ng-transclude></ng-transclude>' +
25050 '</div>'
25051 };
25052 })
25053 .controller('ExampleController', ['$scope', function($scope) {
25054 $scope.title = 'Lorem Ipsum';
25055 $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
25056 }]);
25057 </script>
25058 <div ng-controller="ExampleController">
25059 <input ng-model="title"><br>
25060 <textarea ng-model="text"></textarea> <br/>
25061 <pane title="{{title}}">{{text}}</pane>
25062 </div>
25063 </file>
25064 <file name="protractor.js" type="protractor">
25065 it('should have transcluded', function() {
25066 var titleElement = element(by.model('title'));
25067 titleElement.clear();
25068 titleElement.sendKeys('TITLE');
25069 var textElement = element(by.model('text'));
25070 textElement.clear();
25071 textElement.sendKeys('TEXT');
25072 expect(element(by.binding('title')).getText()).toEqual('TITLE');
25073 expect(element(by.binding('text')).getText()).toEqual('TEXT');
25074 });
25075 </file>
25076 </example>
25077 *
25078 */
25079var ngTranscludeDirective = ngDirective({
25080 restrict: 'EAC',
25081 link: function($scope, $element, $attrs, controller, $transclude) {
25082 if (!$transclude) {
25083 throw minErr('ngTransclude')('orphan',
25084 'Illegal use of ngTransclude directive in the template! ' +
25085 'No parent directive that requires a transclusion found. ' +
25086 'Element: {0}',
25087 startingTag($element));
25088 }
25089
25090 $transclude(function(clone) {
25091 $element.empty();
25092 $element.append(clone);
25093 });
25094 }
25095});
25096
25097/**
25098 * @ngdoc directive
25099 * @name script
25100 * @restrict E
25101 *
25102 * @description
25103 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
25104 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
25105 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
25106 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
25107 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
25108 *
25109 * @param {string} type Must be set to `'text/ng-template'`.
25110 * @param {string} id Cache name of the template.
25111 *
25112 * @example
25113 <example>
25114 <file name="index.html">
25115 <script type="text/ng-template" id="/tpl.html">
25116 Content of the template.
25117 </script>
25118
25119 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
25120 <div id="tpl-content" ng-include src="currentTpl"></div>
25121 </file>
25122 <file name="protractor.js" type="protractor">
25123 it('should load template defined inside script tag', function() {
25124 element(by.css('#tpl-link')).click();
25125 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
25126 });
25127 </file>
25128 </example>
25129 */
25130var scriptDirective = ['$templateCache', function($templateCache) {
25131 return {
25132 restrict: 'E',
25133 terminal: true,
25134 compile: function(element, attr) {
25135 if (attr.type == 'text/ng-template') {
25136 var templateUrl = attr.id,
25137 text = element[0].text;
25138
25139 $templateCache.put(templateUrl, text);
25140 }
25141 }
25142 };
25143}];
25144
25145var ngOptionsMinErr = minErr('ngOptions');
25146/**
25147 * @ngdoc directive
25148 * @name select
25149 * @restrict E
25150 *
25151 * @description
25152 * HTML `SELECT` element with angular data-binding.
25153 *
25154 * # `ngOptions`
25155 *
25156 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
25157 * elements for the `<select>` element using the array or object obtained by evaluating the
25158 * `ngOptions` comprehension_expression.
25159 *
25160 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
25161 * similar result. However, the `ngOptions` provides some benefits such as reducing memory and
25162 * increasing speed by not creating a new scope for each repeated instance, as well as providing
25163 * more flexibility in how the `select`'s model is assigned via `select as`. `ngOptions` should be
25164 * used when the `select` model needs to be bound to a non-string value. This is because an option
25165 * element can only be bound to string values at present.
25166 *
25167 * When an item in the `<select>` menu is selected, the array element or object property
25168 * represented by the selected option will be bound to the model identified by the `ngModel`
25169 * directive.
25170 *
25171 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
25172 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
25173 * option. See example below for demonstration.
25174 *
25175 * <div class="alert alert-warning">
25176 * **Note:** `ngModel` compares by reference, not value. This is important when binding to an
25177 * array of objects. See an example [in this jsfiddle](http://jsfiddle.net/qWzTb/).
25178 * </div>
25179 *
25180 * ## `select as`
25181 *
25182 * Using `select as` will bind the result of the `select as` expression to the model, but
25183 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
25184 * or property name (for object data sources) of the value within the collection. If a `track by` expression
25185 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
25186 *
25187 * ### `select as` with `trackexpr`
25188 *
25189 * Using `select as` together with `trackexpr` is not recommended. Reasoning:
25190 *
25191 * - Example: &lt;select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"&gt;
25192 * values: [{id: 1, label: 'aLabel', subItem: {name: 'aSubItem'}}, {id: 2, label: 'bLabel', subItem: {name: 'bSubItem'}}],
25193 * $scope.selected = {name: 'aSubItem'};
25194 * - track by is always applied to `value`, with the purpose of preserving the selection,
25195 * (to `item` in this case)
25196 * - to calculate whether an item is selected we do the following:
25197 * 1. apply `track by` to the values in the array, e.g.
25198 * In the example: [1,2]
25199 * 2. apply `track by` to the already selected value in `ngModel`:
25200 * In the example: this is not possible, as `track by` refers to `item.id`, but the selected
25201 * value from `ngModel` is `{name: aSubItem}`.
25202 *
25203 * @param {string} ngModel Assignable angular expression to data-bind to.
25204 * @param {string=} name Property name of the form under which the control is published.
25205 * @param {string=} required The control is considered valid only if value is entered.
25206 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
25207 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
25208 * `required` when you want to data-bind to the `required` attribute.
25209 * @param {comprehension_expression=} ngOptions in one of the following forms:
25210 *
25211 * * for array data sources:
25212 * * `label` **`for`** `value` **`in`** `array`
25213 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
25214 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
25215 * * `select` **`as`** `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
25216 * * for object data sources:
25217 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25218 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
25219 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
25220 * * `select` **`as`** `label` **`group by`** `group`
25221 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
25222 *
25223 * Where:
25224 *
25225 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
25226 * * `value`: local variable which will refer to each item in the `array` or each property value
25227 * of `object` during iteration.
25228 * * `key`: local variable which will refer to a property name in `object` during iteration.
25229 * * `label`: The result of this expression will be the label for `<option>` element. The
25230 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
25231 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
25232 * element. If not specified, `select` expression will default to `value`.
25233 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
25234 * DOM element.
25235 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
25236 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
25237 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
25238 * even when the options are recreated (e.g. reloaded from the server).
25239 *
25240 * @example
25241 <example module="selectExample">
25242 <file name="index.html">
25243 <script>
25244 angular.module('selectExample', [])
25245 .controller('ExampleController', ['$scope', function($scope) {
25246 $scope.colors = [
25247 {name:'black', shade:'dark'},
25248 {name:'white', shade:'light'},
25249 {name:'red', shade:'dark'},
25250 {name:'blue', shade:'dark'},
25251 {name:'yellow', shade:'light'}
25252 ];
25253 $scope.myColor = $scope.colors[2]; // red
25254 }]);
25255 </script>
25256 <div ng-controller="ExampleController">
25257 <ul>
25258 <li ng-repeat="color in colors">
25259 Name: <input ng-model="color.name">
25260 [<a href ng-click="colors.splice($index, 1)">X</a>]
25261 </li>
25262 <li>
25263 [<a href ng-click="colors.push({})">add</a>]
25264 </li>
25265 </ul>
25266 <hr/>
25267 Color (null not allowed):
25268 <select ng-model="myColor" ng-options="color.name for color in colors"></select><br>
25269
25270 Color (null allowed):
25271 <span class="nullable">
25272 <select ng-model="myColor" ng-options="color.name for color in colors">
25273 <option value="">-- choose color --</option>
25274 </select>
25275 </span><br/>
25276
25277 Color grouped by shade:
25278 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
25279 </select><br/>
25280
25281
25282 Select <a href ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</a>.<br>
25283 <hr/>
25284 Currently selected: {{ {selected_color:myColor} }}
25285 <div style="border:solid 1px black; height:20px"
25286 ng-style="{'background-color':myColor.name}">
25287 </div>
25288 </div>
25289 </file>
25290 <file name="protractor.js" type="protractor">
25291 it('should check ng-options', function() {
25292 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
25293 element.all(by.model('myColor')).first().click();
25294 element.all(by.css('select[ng-model="myColor"] option')).first().click();
25295 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
25296 element(by.css('.nullable select[ng-model="myColor"]')).click();
25297 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
25298 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
25299 });
25300 </file>
25301 </example>
25302 */
25303
25304var ngOptionsDirective = valueFn({
25305 restrict: 'A',
25306 terminal: true
25307});
25308
25309// jshint maxlen: false
25310var selectDirective = ['$compile', '$parse', function($compile, $parse) {
25311 //000011111111110000000000022222222220000000000000000000003333333333000000000000004444444444444440000000005555555555555550000000666666666666666000000000000000777777777700000000000000000008888888888
25312 var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/,
25313 nullModelCtrl = {$setViewValue: noop};
25314// jshint maxlen: 100
25315
25316 return {
25317 restrict: 'E',
25318 require: ['select', '?ngModel'],
25319 controller: ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
25320 var self = this,
25321 optionsMap = {},
25322 ngModelCtrl = nullModelCtrl,
25323 nullOption,
25324 unknownOption;
25325
25326
25327 self.databound = $attrs.ngModel;
25328
25329
25330 self.init = function(ngModelCtrl_, nullOption_, unknownOption_) {
25331 ngModelCtrl = ngModelCtrl_;
25332 nullOption = nullOption_;
25333 unknownOption = unknownOption_;
25334 };
25335
25336
25337 self.addOption = function(value, element) {
25338 assertNotHasOwnProperty(value, '"option value"');
25339 optionsMap[value] = true;
25340
25341 if (ngModelCtrl.$viewValue == value) {
25342 $element.val(value);
25343 if (unknownOption.parent()) unknownOption.remove();
25344 }
25345 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
25346 // Adding an <option selected="selected"> element to a <select required="required"> should
25347 // automatically select the new element
25348 if (element && element[0].hasAttribute('selected')) {
25349 element[0].selected = true;
25350 }
25351 };
25352
25353
25354 self.removeOption = function(value) {
25355 if (this.hasOption(value)) {
25356 delete optionsMap[value];
25357 if (ngModelCtrl.$viewValue === value) {
25358 this.renderUnknownOption(value);
25359 }
25360 }
25361 };
25362
25363
25364 self.renderUnknownOption = function(val) {
25365 var unknownVal = '? ' + hashKey(val) + ' ?';
25366 unknownOption.val(unknownVal);
25367 $element.prepend(unknownOption);
25368 $element.val(unknownVal);
25369 unknownOption.prop('selected', true); // needed for IE
25370 };
25371
25372
25373 self.hasOption = function(value) {
25374 return optionsMap.hasOwnProperty(value);
25375 };
25376
25377 $scope.$on('$destroy', function() {
25378 // disable unknown option so that we don't do work when the whole select is being destroyed
25379 self.renderUnknownOption = noop;
25380 });
25381 }],
25382
25383 link: function(scope, element, attr, ctrls) {
25384 // if ngModel is not defined, we don't need to do anything
25385 if (!ctrls[1]) return;
25386
25387 var selectCtrl = ctrls[0],
25388 ngModelCtrl = ctrls[1],
25389 multiple = attr.multiple,
25390 optionsExp = attr.ngOptions,
25391 nullOption = false, // if false, user will not be able to select it (used by ngOptions)
25392 emptyOption,
25393 renderScheduled = false,
25394 // we can't just jqLite('<option>') since jqLite is not smart enough
25395 // to create it in <select> and IE barfs otherwise.
25396 optionTemplate = jqLite(document.createElement('option')),
25397 optGroupTemplate =jqLite(document.createElement('optgroup')),
25398 unknownOption = optionTemplate.clone();
25399
25400 // find "null" option
25401 for (var i = 0, children = element.children(), ii = children.length; i < ii; i++) {
25402 if (children[i].value === '') {
25403 emptyOption = nullOption = children.eq(i);
25404 break;
25405 }
25406 }
25407
25408 selectCtrl.init(ngModelCtrl, nullOption, unknownOption);
25409
25410 // required validator
25411 if (multiple) {
25412 ngModelCtrl.$isEmpty = function(value) {
25413 return !value || value.length === 0;
25414 };
25415 }
25416
25417 if (optionsExp) setupAsOptions(scope, element, ngModelCtrl);
25418 else if (multiple) setupAsMultiple(scope, element, ngModelCtrl);
25419 else setupAsSingle(scope, element, ngModelCtrl, selectCtrl);
25420
25421
25422 ////////////////////////////
25423
25424
25425
25426 function setupAsSingle(scope, selectElement, ngModelCtrl, selectCtrl) {
25427 ngModelCtrl.$render = function() {
25428 var viewValue = ngModelCtrl.$viewValue;
25429
25430 if (selectCtrl.hasOption(viewValue)) {
25431 if (unknownOption.parent()) unknownOption.remove();
25432 selectElement.val(viewValue);
25433 if (viewValue === '') emptyOption.prop('selected', true); // to make IE9 happy
25434 } else {
25435 if (isUndefined(viewValue) && emptyOption) {
25436 selectElement.val('');
25437 } else {
25438 selectCtrl.renderUnknownOption(viewValue);
25439 }
25440 }
25441 };
25442
25443 selectElement.on('change', function() {
25444 scope.$apply(function() {
25445 if (unknownOption.parent()) unknownOption.remove();
25446 ngModelCtrl.$setViewValue(selectElement.val());
25447 });
25448 });
25449 }
25450
25451 function setupAsMultiple(scope, selectElement, ctrl) {
25452 var lastView;
25453 ctrl.$render = function() {
25454 var items = new HashMap(ctrl.$viewValue);
25455 forEach(selectElement.find('option'), function(option) {
25456 option.selected = isDefined(items.get(option.value));
25457 });
25458 };
25459
25460 // we have to do it on each watch since ngModel watches reference, but
25461 // we need to work of an array, so we need to see if anything was inserted/removed
25462 scope.$watch(function selectMultipleWatch() {
25463 if (!equals(lastView, ctrl.$viewValue)) {
25464 lastView = shallowCopy(ctrl.$viewValue);
25465 ctrl.$render();
25466 }
25467 });
25468
25469 selectElement.on('change', function() {
25470 scope.$apply(function() {
25471 var array = [];
25472 forEach(selectElement.find('option'), function(option) {
25473 if (option.selected) {
25474 array.push(option.value);
25475 }
25476 });
25477 ctrl.$setViewValue(array);
25478 });
25479 });
25480 }
25481
25482 function setupAsOptions(scope, selectElement, ctrl) {
25483 var match;
25484
25485 if (!(match = optionsExp.match(NG_OPTIONS_REGEXP))) {
25486 throw ngOptionsMinErr('iexp',
25487 "Expected expression in form of " +
25488 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
25489 " but got '{0}'. Element: {1}",
25490 optionsExp, startingTag(selectElement));
25491 }
25492
25493 var displayFn = $parse(match[2] || match[1]),
25494 valueName = match[4] || match[6],
25495 selectAs = / as /.test(match[0]) && match[1],
25496 selectAsFn = selectAs ? $parse(selectAs) : null,
25497 keyName = match[5],
25498 groupByFn = $parse(match[3] || ''),
25499 valueFn = $parse(match[2] ? match[1] : valueName),
25500 valuesFn = $parse(match[7]),
25501 track = match[8],
25502 trackFn = track ? $parse(match[8]) : null,
25503 trackKeysCache = {},
25504 // This is an array of array of existing option groups in DOM.
25505 // We try to reuse these if possible
25506 // - optionGroupsCache[0] is the options with no option group
25507 // - optionGroupsCache[?][0] is the parent: either the SELECT or OPTGROUP element
25508 optionGroupsCache = [[{element: selectElement, label:''}]],
25509 //re-usable object to represent option's locals
25510 locals = {};
25511
25512 if (nullOption) {
25513 // compile the element since there might be bindings in it
25514 $compile(nullOption)(scope);
25515
25516 // remove the class, which is added automatically because we recompile the element and it
25517 // becomes the compilation root
25518 nullOption.removeClass('ng-scope');
25519
25520 // we need to remove it before calling selectElement.empty() because otherwise IE will
25521 // remove the label from the element. wtf?
25522 nullOption.remove();
25523 }
25524
25525 // clear contents, we'll add what's needed based on the model
25526 selectElement.empty();
25527
25528 selectElement.on('change', selectionChanged);
25529
25530 ctrl.$render = render;
25531
25532 scope.$watchCollection(valuesFn, scheduleRendering);
25533 scope.$watchCollection(getLabels, scheduleRendering);
25534
25535 if (multiple) {
25536 scope.$watchCollection(function() { return ctrl.$modelValue; }, scheduleRendering);
25537 }
25538
25539 // ------------------------------------------------------------------ //
25540
25541 function callExpression(exprFn, key, value) {
25542 locals[valueName] = value;
25543 if (keyName) locals[keyName] = key;
25544 return exprFn(scope, locals);
25545 }
25546
25547 function selectionChanged() {
25548 scope.$apply(function() {
25549 var collection = valuesFn(scope) || [];
25550 var viewValue;
25551 if (multiple) {
25552 viewValue = [];
25553 forEach(selectElement.val(), function(selectedKey) {
25554 selectedKey = trackFn ? trackKeysCache[selectedKey] : selectedKey;
25555 viewValue.push(getViewValue(selectedKey, collection[selectedKey]));
25556 });
25557 } else {
25558 var selectedKey = trackFn ? trackKeysCache[selectElement.val()] : selectElement.val();
25559 viewValue = getViewValue(selectedKey, collection[selectedKey]);
25560 }
25561 ctrl.$setViewValue(viewValue);
25562 render();
25563 });
25564 }
25565
25566 function getViewValue(key, value) {
25567 if (key === '?') {
25568 return undefined;
25569 } else if (key === '') {
25570 return null;
25571 } else {
25572 var viewValueFn = selectAsFn ? selectAsFn : valueFn;
25573 return callExpression(viewValueFn, key, value);
25574 }
25575 }
25576
25577 function getLabels() {
25578 var values = valuesFn(scope);
25579 var toDisplay;
25580 if (values && isArray(values)) {
25581 toDisplay = new Array(values.length);
25582 for (var i = 0, ii = values.length; i < ii; i++) {
25583 toDisplay[i] = callExpression(displayFn, i, values[i]);
25584 }
25585 return toDisplay;
25586 } else if (values) {
25587 // TODO: Add a test for this case
25588 toDisplay = {};
25589 for (var prop in values) {
25590 if (values.hasOwnProperty(prop)) {
25591 toDisplay[prop] = callExpression(displayFn, prop, values[prop]);
25592 }
25593 }
25594 }
25595 return toDisplay;
25596 }
25597
25598 function createIsSelectedFn(viewValue) {
25599 var selectedSet;
25600 if (multiple) {
25601 if (trackFn && isArray(viewValue)) {
25602
25603 selectedSet = new HashMap([]);
25604 for (var trackIndex = 0; trackIndex < viewValue.length; trackIndex++) {
25605 // tracking by key
25606 selectedSet.put(callExpression(trackFn, null, viewValue[trackIndex]), true);
25607 }
25608 } else {
25609 selectedSet = new HashMap(viewValue);
25610 }
25611 } else if (trackFn) {
25612 viewValue = callExpression(trackFn, null, viewValue);
25613 }
25614
25615 return function isSelected(key, value) {
25616 var compareValueFn;
25617 if (trackFn) {
25618 compareValueFn = trackFn;
25619 } else if (selectAsFn) {
25620 compareValueFn = selectAsFn;
25621 } else {
25622 compareValueFn = valueFn;
25623 }
25624
25625 if (multiple) {
25626 return isDefined(selectedSet.remove(callExpression(compareValueFn, key, value)));
25627 } else {
25628 return viewValue === callExpression(compareValueFn, key, value);
25629 }
25630 };
25631 }
25632
25633 function scheduleRendering() {
25634 if (!renderScheduled) {
25635 scope.$$postDigest(render);
25636 renderScheduled = true;
25637 }
25638 }
25639
25640 /**
25641 * A new labelMap is created with each render.
25642 * This function is called for each existing option with added=false,
25643 * and each new option with added=true.
25644 * - Labels that are passed to this method twice,
25645 * (once with added=true and once with added=false) will end up with a value of 0, and
25646 * will cause no change to happen to the corresponding option.
25647 * - Labels that are passed to this method only once with added=false will end up with a
25648 * value of -1 and will eventually be passed to selectCtrl.removeOption()
25649 * - Labels that are passed to this method only once with added=true will end up with a
25650 * value of 1 and will eventually be passed to selectCtrl.addOption()
25651 */
25652 function updateLabelMap(labelMap, label, added) {
25653 labelMap[label] = labelMap[label] || 0;
25654 labelMap[label] += (added ? 1 : -1);
25655 }
25656
25657 function render() {
25658 renderScheduled = false;
25659
25660 // Temporary location for the option groups before we render them
25661 var optionGroups = {'':[]},
25662 optionGroupNames = [''],
25663 optionGroupName,
25664 optionGroup,
25665 option,
25666 existingParent, existingOptions, existingOption,
25667 viewValue = ctrl.$viewValue,
25668 values = valuesFn(scope) || [],
25669 keys = keyName ? sortedKeys(values) : values,
25670 key,
25671 value,
25672 groupLength, length,
25673 groupIndex, index,
25674 labelMap = {},
25675 selected,
25676 isSelected = createIsSelectedFn(viewValue),
25677 anySelected = false,
25678 lastElement,
25679 element,
25680 label,
25681 optionId;
25682
25683 trackKeysCache = {};
25684
25685 // We now build up the list of options we need (we merge later)
25686 for (index = 0; length = keys.length, index < length; index++) {
25687 key = index;
25688 if (keyName) {
25689 key = keys[index];
25690 if (key.charAt(0) === '$') continue;
25691 }
25692 value = values[key];
25693
25694 optionGroupName = callExpression(groupByFn, key, value) || '';
25695 if (!(optionGroup = optionGroups[optionGroupName])) {
25696 optionGroup = optionGroups[optionGroupName] = [];
25697 optionGroupNames.push(optionGroupName);
25698 }
25699
25700 selected = isSelected(key, value);
25701 anySelected = anySelected || selected;
25702
25703 label = callExpression(displayFn, key, value); // what will be seen by the user
25704
25705 // doing displayFn(scope, locals) || '' overwrites zero values
25706 label = isDefined(label) ? label : '';
25707 optionId = trackFn ? trackFn(scope, locals) : (keyName ? keys[index] : index);
25708 if (trackFn) {
25709 trackKeysCache[optionId] = key;
25710 }
25711
25712 optionGroup.push({
25713 // either the index into array or key from object
25714 id: optionId,
25715 label: label,
25716 selected: selected // determine if we should be selected
25717 });
25718 }
25719 if (!multiple) {
25720 if (nullOption || viewValue === null) {
25721 // insert null option if we have a placeholder, or the model is null
25722 optionGroups[''].unshift({id:'', label:'', selected:!anySelected});
25723 } else if (!anySelected) {
25724 // option could not be found, we have to insert the undefined item
25725 optionGroups[''].unshift({id:'?', label:'', selected:true});
25726 }
25727 }
25728
25729 // Now we need to update the list of DOM nodes to match the optionGroups we computed above
25730 for (groupIndex = 0, groupLength = optionGroupNames.length;
25731 groupIndex < groupLength;
25732 groupIndex++) {
25733 // current option group name or '' if no group
25734 optionGroupName = optionGroupNames[groupIndex];
25735
25736 // list of options for that group. (first item has the parent)
25737 optionGroup = optionGroups[optionGroupName];
25738
25739 if (optionGroupsCache.length <= groupIndex) {
25740 // we need to grow the optionGroups
25741 existingParent = {
25742 element: optGroupTemplate.clone().attr('label', optionGroupName),
25743 label: optionGroup.label
25744 };
25745 existingOptions = [existingParent];
25746 optionGroupsCache.push(existingOptions);
25747 selectElement.append(existingParent.element);
25748 } else {
25749 existingOptions = optionGroupsCache[groupIndex];
25750 existingParent = existingOptions[0]; // either SELECT (no group) or OPTGROUP element
25751
25752 // update the OPTGROUP label if not the same.
25753 if (existingParent.label != optionGroupName) {
25754 existingParent.element.attr('label', existingParent.label = optionGroupName);
25755 }
25756 }
25757
25758 lastElement = null; // start at the beginning
25759 for (index = 0, length = optionGroup.length; index < length; index++) {
25760 option = optionGroup[index];
25761 if ((existingOption = existingOptions[index + 1])) {
25762 // reuse elements
25763 lastElement = existingOption.element;
25764 if (existingOption.label !== option.label) {
25765 updateLabelMap(labelMap, existingOption.label, false);
25766 updateLabelMap(labelMap, option.label, true);
25767 lastElement.text(existingOption.label = option.label);
25768 lastElement.prop('label', existingOption.label);
25769 }
25770 if (existingOption.id !== option.id) {
25771 lastElement.val(existingOption.id = option.id);
25772 }
25773 // lastElement.prop('selected') provided by jQuery has side-effects
25774 if (lastElement[0].selected !== option.selected) {
25775 lastElement.prop('selected', (existingOption.selected = option.selected));
25776 if (msie) {
25777 // See #7692
25778 // The selected item wouldn't visually update on IE without this.
25779 // Tested on Win7: IE9, IE10 and IE11. Future IEs should be tested as well
25780 lastElement.prop('selected', existingOption.selected);
25781 }
25782 }
25783 } else {
25784 // grow elements
25785
25786 // if it's a null option
25787 if (option.id === '' && nullOption) {
25788 // put back the pre-compiled element
25789 element = nullOption;
25790 } else {
25791 // jQuery(v1.4.2) Bug: We should be able to chain the method calls, but
25792 // in this version of jQuery on some browser the .text() returns a string
25793 // rather then the element.
25794 (element = optionTemplate.clone())
25795 .val(option.id)
25796 .prop('selected', option.selected)
25797 .attr('selected', option.selected)
25798 .prop('label', option.label)
25799 .text(option.label);
25800 }
25801
25802 existingOptions.push(existingOption = {
25803 element: element,
25804 label: option.label,
25805 id: option.id,
25806 selected: option.selected
25807 });
25808 updateLabelMap(labelMap, option.label, true);
25809 if (lastElement) {
25810 lastElement.after(element);
25811 } else {
25812 existingParent.element.append(element);
25813 }
25814 lastElement = element;
25815 }
25816 }
25817 // remove any excessive OPTIONs in a group
25818 index++; // increment since the existingOptions[0] is parent element not OPTION
25819 while (existingOptions.length > index) {
25820 option = existingOptions.pop();
25821 updateLabelMap(labelMap, option.label, false);
25822 option.element.remove();
25823 }
25824 }
25825 // remove any excessive OPTGROUPs from select
25826 while (optionGroupsCache.length > groupIndex) {
25827 // remove all the labels in the option group
25828 optionGroup = optionGroupsCache.pop();
25829 for (index = 1; index < optionGroup.length; ++index) {
25830 updateLabelMap(labelMap, optionGroup[index].label, false);
25831 }
25832 optionGroup[0].element.remove();
25833 }
25834 forEach(labelMap, function(count, label) {
25835 if (count > 0) {
25836 selectCtrl.addOption(label);
25837 } else if (count < 0) {
25838 selectCtrl.removeOption(label);
25839 }
25840 });
25841 }
25842 }
25843 }
25844 };
25845}];
25846
25847var optionDirective = ['$interpolate', function($interpolate) {
25848 var nullSelectCtrl = {
25849 addOption: noop,
25850 removeOption: noop
25851 };
25852
25853 return {
25854 restrict: 'E',
25855 priority: 100,
25856 compile: function(element, attr) {
25857 if (isUndefined(attr.value)) {
25858 var interpolateFn = $interpolate(element.text(), true);
25859 if (!interpolateFn) {
25860 attr.$set('value', element.text());
25861 }
25862 }
25863
25864 return function(scope, element, attr) {
25865 var selectCtrlName = '$selectController',
25866 parent = element.parent(),
25867 selectCtrl = parent.data(selectCtrlName) ||
25868 parent.parent().data(selectCtrlName); // in case we are in optgroup
25869
25870 if (!selectCtrl || !selectCtrl.databound) {
25871 selectCtrl = nullSelectCtrl;
25872 }
25873
25874 if (interpolateFn) {
25875 scope.$watch(interpolateFn, function interpolateWatchAction(newVal, oldVal) {
25876 attr.$set('value', newVal);
25877 if (oldVal !== newVal) {
25878 selectCtrl.removeOption(oldVal);
25879 }
25880 selectCtrl.addOption(newVal, element);
25881 });
25882 } else {
25883 selectCtrl.addOption(attr.value, element);
25884 }
25885
25886 element.on('$destroy', function() {
25887 selectCtrl.removeOption(attr.value);
25888 });
25889 };
25890 }
25891 };
25892}];
25893
25894var styleDirective = valueFn({
25895 restrict: 'E',
25896 terminal: false
25897});
25898
25899 if (window.angular.bootstrap) {
25900 //AngularJS is already loaded, so we can return here...
25901 console.log('WARNING: Tried to load angular more than once.');
25902 return;
25903 }
25904
25905 //try to bind to jquery now so that one can write jqLite(document).ready()
25906 //but we will rebind on bootstrap again.
25907 bindJQuery();
25908
25909 publishExternalAPI(angular);
25910
25911 jqLite(document).ready(function() {
25912 angularInit(document, bootstrap);
25913 });
25914
25915})(window, document);
25916
25917!window.angular.$$csp() && window.angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}</style>');