commonroutesApp/www/lib/angular/angular.js
www/lib/angular/angular.jsRaw
1/**
2 * @license AngularJS v1.5.3
3 * (c) 2010-2016 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 SKIP_INDEXES = 2;
42
43 var templateArgs = arguments,
44 code = templateArgs[0],
45 message = '[' + (module ? module + ':' : '') + code + '] ',
46 template = templateArgs[1],
47 paramPrefix, i;
48
49 message += template.replace(/\{\d+\}/g, function(match) {
50 var index = +match.slice(1, -1),
51 shiftedIndex = index + SKIP_INDEXES;
52
53 if (shiftedIndex < templateArgs.length) {
54 return toDebugString(templateArgs[shiftedIndex]);
55 }
56
57 return match;
58 });
59
60 message += '\nhttp://errors.angularjs.org/1.5.3/' +
61 (module ? module + '/' : '') + code;
62
63 for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
64 message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
65 encodeURIComponent(toDebugString(templateArgs[i]));
66 }
67
68 return new ErrorConstructor(message);
69 };
70}
71
72/* We need to tell jshint what variables are being exported */
73/* global angular: true,
74 msie: true,
75 jqLite: true,
76 jQuery: true,
77 slice: true,
78 splice: true,
79 push: true,
80 toString: true,
81 ngMinErr: true,
82 angularModule: true,
83 uid: true,
84 REGEX_STRING_REGEXP: true,
85 VALIDITY_STATE_PROPERTY: true,
86
87 lowercase: true,
88 uppercase: true,
89 manualLowercase: true,
90 manualUppercase: true,
91 nodeName_: true,
92 isArrayLike: true,
93 forEach: true,
94 forEachSorted: true,
95 reverseParams: true,
96 nextUid: true,
97 setHashKey: true,
98 extend: true,
99 toInt: true,
100 inherit: true,
101 merge: true,
102 noop: true,
103 identity: true,
104 valueFn: true,
105 isUndefined: true,
106 isDefined: true,
107 isObject: true,
108 isBlankObject: true,
109 isString: true,
110 isNumber: true,
111 isDate: true,
112 isArray: true,
113 isFunction: true,
114 isRegExp: true,
115 isWindow: true,
116 isScope: true,
117 isFile: true,
118 isFormData: true,
119 isBlob: true,
120 isBoolean: true,
121 isPromiseLike: true,
122 trim: true,
123 escapeForRegexp: true,
124 isElement: true,
125 makeMap: true,
126 includes: true,
127 arrayRemove: true,
128 copy: true,
129 shallowCopy: true,
130 equals: true,
131 csp: true,
132 jq: true,
133 concat: true,
134 sliceArgs: true,
135 bind: true,
136 toJsonReplacer: true,
137 toJson: true,
138 fromJson: true,
139 convertTimezoneToLocal: true,
140 timezoneToOffset: true,
141 startingTag: true,
142 tryDecodeURIComponent: true,
143 parseKeyValue: true,
144 toKeyValue: true,
145 encodeUriSegment: true,
146 encodeUriQuery: true,
147 angularInit: true,
148 bootstrap: true,
149 getTestability: true,
150 snake_case: true,
151 bindJQuery: true,
152 assertArg: true,
153 assertArgFn: true,
154 assertNotHasOwnProperty: true,
155 getter: true,
156 getBlockNodes: true,
157 hasOwnProperty: true,
158 createMap: true,
159
160 NODE_TYPE_ELEMENT: true,
161 NODE_TYPE_ATTRIBUTE: true,
162 NODE_TYPE_TEXT: true,
163 NODE_TYPE_COMMENT: true,
164 NODE_TYPE_DOCUMENT: true,
165 NODE_TYPE_DOCUMENT_FRAGMENT: true,
166*/
167
168////////////////////////////////////
169
170/**
171 * @ngdoc module
172 * @name ng
173 * @module ng
174 * @description
175 *
176 * # ng (core module)
177 * The ng module is loaded by default when an AngularJS application is started. The module itself
178 * contains the essential components for an AngularJS application to function. The table below
179 * lists a high level breakdown of each of the services/factories, filters, directives and testing
180 * components available within this core module.
181 *
182 * <div doc-module-components="ng"></div>
183 */
184
185var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
186
187// The name of a form control's ValidityState property.
188// This is used so that it's possible for internal tests to create mock ValidityStates.
189var VALIDITY_STATE_PROPERTY = 'validity';
190
191var hasOwnProperty = Object.prototype.hasOwnProperty;
192
193var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
194var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
195
196
197var manualLowercase = function(s) {
198 /* jshint bitwise: false */
199 return isString(s)
200 ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
201 : s;
202};
203var manualUppercase = function(s) {
204 /* jshint bitwise: false */
205 return isString(s)
206 ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
207 : s;
208};
209
210
211// String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
212// locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
213// with correct but slower alternatives. See https://github.com/angular/angular.js/issues/11387
214if ('i' !== 'I'.toLowerCase()) {
215 lowercase = manualLowercase;
216 uppercase = manualUppercase;
217}
218
219
220var
221 msie, // holds major version number for IE, or NaN if UA is not IE.
222 jqLite, // delay binding since jQuery could be loaded after us.
223 jQuery, // delay binding
224 slice = [].slice,
225 splice = [].splice,
226 push = [].push,
227 toString = Object.prototype.toString,
228 getPrototypeOf = Object.getPrototypeOf,
229 ngMinErr = minErr('ng'),
230
231 /** @name angular */
232 angular = window.angular || (window.angular = {}),
233 angularModule,
234 uid = 0;
235
236/**
237 * documentMode is an IE-only property
238 * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
239 */
240msie = document.documentMode;
241
242
243/**
244 * @private
245 * @param {*} obj
246 * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
247 * String ...)
248 */
249function isArrayLike(obj) {
250
251 // `null`, `undefined` and `window` are not array-like
252 if (obj == null || isWindow(obj)) return false;
253
254 // arrays, strings and jQuery/jqLite objects are array like
255 // * jqLite is either the jQuery or jqLite constructor function
256 // * we have to check the existence of jqLite first as this method is called
257 // via the forEach method when constructing the jqLite object in the first place
258 if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
259
260 // Support: iOS 8.2 (not reproducible in simulator)
261 // "length" in obj used to prevent JIT error (gh-11508)
262 var length = "length" in Object(obj) && obj.length;
263
264 // NodeList objects (with `item` method) and
265 // other objects with suitable length characteristics are array-like
266 return isNumber(length) &&
267 (length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item == 'function');
268
269}
270
271/**
272 * @ngdoc function
273 * @name angular.forEach
274 * @module ng
275 * @kind function
276 *
277 * @description
278 * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
279 * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
280 * is the value of an object property or an array element, `key` is the object property key or
281 * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
282 *
283 * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
284 * using the `hasOwnProperty` method.
285 *
286 * Unlike ES262's
287 * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
288 * providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
289 * return the value provided.
290 *
291 ```js
292 var values = {name: 'misko', gender: 'male'};
293 var log = [];
294 angular.forEach(values, function(value, key) {
295 this.push(key + ': ' + value);
296 }, log);
297 expect(log).toEqual(['name: misko', 'gender: male']);
298 ```
299 *
300 * @param {Object|Array} obj Object to iterate over.
301 * @param {Function} iterator Iterator function.
302 * @param {Object=} context Object to become context (`this`) for the iterator function.
303 * @returns {Object|Array} Reference to `obj`.
304 */
305
306function forEach(obj, iterator, context) {
307 var key, length;
308 if (obj) {
309 if (isFunction(obj)) {
310 for (key in obj) {
311 // Need to check if hasOwnProperty exists,
312 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
313 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
314 iterator.call(context, obj[key], key, obj);
315 }
316 }
317 } else if (isArray(obj) || isArrayLike(obj)) {
318 var isPrimitive = typeof obj !== 'object';
319 for (key = 0, length = obj.length; key < length; key++) {
320 if (isPrimitive || key in obj) {
321 iterator.call(context, obj[key], key, obj);
322 }
323 }
324 } else if (obj.forEach && obj.forEach !== forEach) {
325 obj.forEach(iterator, context, obj);
326 } else if (isBlankObject(obj)) {
327 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
328 for (key in obj) {
329 iterator.call(context, obj[key], key, obj);
330 }
331 } else if (typeof obj.hasOwnProperty === 'function') {
332 // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
333 for (key in obj) {
334 if (obj.hasOwnProperty(key)) {
335 iterator.call(context, obj[key], key, obj);
336 }
337 }
338 } else {
339 // Slow path for objects which do not have a method `hasOwnProperty`
340 for (key in obj) {
341 if (hasOwnProperty.call(obj, key)) {
342 iterator.call(context, obj[key], key, obj);
343 }
344 }
345 }
346 }
347 return obj;
348}
349
350function forEachSorted(obj, iterator, context) {
351 var keys = Object.keys(obj).sort();
352 for (var i = 0; i < keys.length; i++) {
353 iterator.call(context, obj[keys[i]], keys[i]);
354 }
355 return keys;
356}
357
358
359/**
360 * when using forEach the params are value, key, but it is often useful to have key, value.
361 * @param {function(string, *)} iteratorFn
362 * @returns {function(*, string)}
363 */
364function reverseParams(iteratorFn) {
365 return function(value, key) {iteratorFn(key, value);};
366}
367
368/**
369 * A consistent way of creating unique IDs in angular.
370 *
371 * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
372 * we hit number precision issues in JavaScript.
373 *
374 * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
375 *
376 * @returns {number} an unique alpha-numeric string
377 */
378function nextUid() {
379 return ++uid;
380}
381
382
383/**
384 * Set or clear the hashkey for an object.
385 * @param obj object
386 * @param h the hashkey (!truthy to delete the hashkey)
387 */
388function setHashKey(obj, h) {
389 if (h) {
390 obj.$$hashKey = h;
391 } else {
392 delete obj.$$hashKey;
393 }
394}
395
396
397function baseExtend(dst, objs, deep) {
398 var h = dst.$$hashKey;
399
400 for (var i = 0, ii = objs.length; i < ii; ++i) {
401 var obj = objs[i];
402 if (!isObject(obj) && !isFunction(obj)) continue;
403 var keys = Object.keys(obj);
404 for (var j = 0, jj = keys.length; j < jj; j++) {
405 var key = keys[j];
406 var src = obj[key];
407
408 if (deep && isObject(src)) {
409 if (isDate(src)) {
410 dst[key] = new Date(src.valueOf());
411 } else if (isRegExp(src)) {
412 dst[key] = new RegExp(src);
413 } else if (src.nodeName) {
414 dst[key] = src.cloneNode(true);
415 } else if (isElement(src)) {
416 dst[key] = src.clone();
417 } else {
418 if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
419 baseExtend(dst[key], [src], true);
420 }
421 } else {
422 dst[key] = src;
423 }
424 }
425 }
426
427 setHashKey(dst, h);
428 return dst;
429}
430
431/**
432 * @ngdoc function
433 * @name angular.extend
434 * @module ng
435 * @kind function
436 *
437 * @description
438 * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
439 * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
440 * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
441 *
442 * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
443 * {@link angular.merge} for this.
444 *
445 * @param {Object} dst Destination object.
446 * @param {...Object} src Source object(s).
447 * @returns {Object} Reference to `dst`.
448 */
449function extend(dst) {
450 return baseExtend(dst, slice.call(arguments, 1), false);
451}
452
453
454/**
455* @ngdoc function
456* @name angular.merge
457* @module ng
458* @kind function
459*
460* @description
461* Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
462* to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
463* by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
464*
465* Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
466* objects, performing a deep copy.
467*
468* @param {Object} dst Destination object.
469* @param {...Object} src Source object(s).
470* @returns {Object} Reference to `dst`.
471*/
472function merge(dst) {
473 return baseExtend(dst, slice.call(arguments, 1), true);
474}
475
476
477
478function toInt(str) {
479 return parseInt(str, 10);
480}
481
482
483function inherit(parent, extra) {
484 return extend(Object.create(parent), extra);
485}
486
487/**
488 * @ngdoc function
489 * @name angular.noop
490 * @module ng
491 * @kind function
492 *
493 * @description
494 * A function that performs no operations. This function can be useful when writing code in the
495 * functional style.
496 ```js
497 function foo(callback) {
498 var result = calculateResult();
499 (callback || angular.noop)(result);
500 }
501 ```
502 */
503function noop() {}
504noop.$inject = [];
505
506
507/**
508 * @ngdoc function
509 * @name angular.identity
510 * @module ng
511 * @kind function
512 *
513 * @description
514 * A function that returns its first argument. This function is useful when writing code in the
515 * functional style.
516 *
517 ```js
518 function transformer(transformationFn, value) {
519 return (transformationFn || angular.identity)(value);
520 };
521 ```
522 * @param {*} value to be returned.
523 * @returns {*} the value passed in.
524 */
525function identity($) {return $;}
526identity.$inject = [];
527
528
529function valueFn(value) {return function valueRef() {return value;};}
530
531function hasCustomToString(obj) {
532 return isFunction(obj.toString) && obj.toString !== toString;
533}
534
535
536/**
537 * @ngdoc function
538 * @name angular.isUndefined
539 * @module ng
540 * @kind function
541 *
542 * @description
543 * Determines if a reference is undefined.
544 *
545 * @param {*} value Reference to check.
546 * @returns {boolean} True if `value` is undefined.
547 */
548function isUndefined(value) {return typeof value === 'undefined';}
549
550
551/**
552 * @ngdoc function
553 * @name angular.isDefined
554 * @module ng
555 * @kind function
556 *
557 * @description
558 * Determines if a reference is defined.
559 *
560 * @param {*} value Reference to check.
561 * @returns {boolean} True if `value` is defined.
562 */
563function isDefined(value) {return typeof value !== 'undefined';}
564
565
566/**
567 * @ngdoc function
568 * @name angular.isObject
569 * @module ng
570 * @kind function
571 *
572 * @description
573 * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
574 * considered to be objects. Note that JavaScript arrays are objects.
575 *
576 * @param {*} value Reference to check.
577 * @returns {boolean} True if `value` is an `Object` but not `null`.
578 */
579function isObject(value) {
580 // http://jsperf.com/isobject4
581 return value !== null && typeof value === 'object';
582}
583
584
585/**
586 * Determine if a value is an object with a null prototype
587 *
588 * @returns {boolean} True if `value` is an `Object` with a null prototype
589 */
590function isBlankObject(value) {
591 return value !== null && typeof value === 'object' && !getPrototypeOf(value);
592}
593
594
595/**
596 * @ngdoc function
597 * @name angular.isString
598 * @module ng
599 * @kind function
600 *
601 * @description
602 * Determines if a reference is a `String`.
603 *
604 * @param {*} value Reference to check.
605 * @returns {boolean} True if `value` is a `String`.
606 */
607function isString(value) {return typeof value === 'string';}
608
609
610/**
611 * @ngdoc function
612 * @name angular.isNumber
613 * @module ng
614 * @kind function
615 *
616 * @description
617 * Determines if a reference is a `Number`.
618 *
619 * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
620 *
621 * If you wish to exclude these then you can use the native
622 * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
623 * method.
624 *
625 * @param {*} value Reference to check.
626 * @returns {boolean} True if `value` is a `Number`.
627 */
628function isNumber(value) {return typeof value === 'number';}
629
630
631/**
632 * @ngdoc function
633 * @name angular.isDate
634 * @module ng
635 * @kind function
636 *
637 * @description
638 * Determines if a value is a date.
639 *
640 * @param {*} value Reference to check.
641 * @returns {boolean} True if `value` is a `Date`.
642 */
643function isDate(value) {
644 return toString.call(value) === '[object Date]';
645}
646
647
648/**
649 * @ngdoc function
650 * @name angular.isArray
651 * @module ng
652 * @kind function
653 *
654 * @description
655 * Determines if a reference is an `Array`.
656 *
657 * @param {*} value Reference to check.
658 * @returns {boolean} True if `value` is an `Array`.
659 */
660var isArray = Array.isArray;
661
662/**
663 * @ngdoc function
664 * @name angular.isFunction
665 * @module ng
666 * @kind function
667 *
668 * @description
669 * Determines if a reference is a `Function`.
670 *
671 * @param {*} value Reference to check.
672 * @returns {boolean} True if `value` is a `Function`.
673 */
674function isFunction(value) {return typeof value === 'function';}
675
676
677/**
678 * Determines if a value is a regular expression object.
679 *
680 * @private
681 * @param {*} value Reference to check.
682 * @returns {boolean} True if `value` is a `RegExp`.
683 */
684function isRegExp(value) {
685 return toString.call(value) === '[object RegExp]';
686}
687
688
689/**
690 * Checks if `obj` is a window object.
691 *
692 * @private
693 * @param {*} obj Object to check
694 * @returns {boolean} True if `obj` is a window obj.
695 */
696function isWindow(obj) {
697 return obj && obj.window === obj;
698}
699
700
701function isScope(obj) {
702 return obj && obj.$evalAsync && obj.$watch;
703}
704
705
706function isFile(obj) {
707 return toString.call(obj) === '[object File]';
708}
709
710
711function isFormData(obj) {
712 return toString.call(obj) === '[object FormData]';
713}
714
715
716function isBlob(obj) {
717 return toString.call(obj) === '[object Blob]';
718}
719
720
721function isBoolean(value) {
722 return typeof value === 'boolean';
723}
724
725
726function isPromiseLike(obj) {
727 return obj && isFunction(obj.then);
728}
729
730
731var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
732function isTypedArray(value) {
733 return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
734}
735
736function isArrayBuffer(obj) {
737 return toString.call(obj) === '[object ArrayBuffer]';
738}
739
740
741var trim = function(value) {
742 return isString(value) ? value.trim() : value;
743};
744
745// Copied from:
746// http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
747// Prereq: s is a string.
748var escapeForRegexp = function(s) {
749 return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
750 replace(/\x08/g, '\\x08');
751};
752
753
754/**
755 * @ngdoc function
756 * @name angular.isElement
757 * @module ng
758 * @kind function
759 *
760 * @description
761 * Determines if a reference is a DOM element (or wrapped jQuery element).
762 *
763 * @param {*} value Reference to check.
764 * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
765 */
766function isElement(node) {
767 return !!(node &&
768 (node.nodeName // we are a direct element
769 || (node.prop && node.attr && node.find))); // we have an on and find method part of jQuery API
770}
771
772/**
773 * @param str 'key1,key2,...'
774 * @returns {object} in the form of {key1:true, key2:true, ...}
775 */
776function makeMap(str) {
777 var obj = {}, items = str.split(','), i;
778 for (i = 0; i < items.length; i++) {
779 obj[items[i]] = true;
780 }
781 return obj;
782}
783
784
785function nodeName_(element) {
786 return lowercase(element.nodeName || (element[0] && element[0].nodeName));
787}
788
789function includes(array, obj) {
790 return Array.prototype.indexOf.call(array, obj) != -1;
791}
792
793function arrayRemove(array, value) {
794 var index = array.indexOf(value);
795 if (index >= 0) {
796 array.splice(index, 1);
797 }
798 return index;
799}
800
801/**
802 * @ngdoc function
803 * @name angular.copy
804 * @module ng
805 * @kind function
806 *
807 * @description
808 * Creates a deep copy of `source`, which should be an object or an array.
809 *
810 * * If no destination is supplied, a copy of the object or array is created.
811 * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
812 * are deleted and then all elements/properties from the source are copied to it.
813 * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
814 * * If `source` is identical to 'destination' an exception will be thrown.
815 *
816 * @param {*} source The source that will be used to make a copy.
817 * Can be any type, including primitives, `null`, and `undefined`.
818 * @param {(Object|Array)=} destination Destination into which the source is copied. If
819 * provided, must be of the same type as `source`.
820 * @returns {*} The copy or updated `destination`, if `destination` was specified.
821 *
822 * @example
823 <example module="copyExample">
824 <file name="index.html">
825 <div ng-controller="ExampleController">
826 <form novalidate class="simple-form">
827 Name: <input type="text" ng-model="user.name" /><br />
828 E-mail: <input type="email" ng-model="user.email" /><br />
829 Gender: <input type="radio" ng-model="user.gender" value="male" />male
830 <input type="radio" ng-model="user.gender" value="female" />female<br />
831 <button ng-click="reset()">RESET</button>
832 <button ng-click="update(user)">SAVE</button>
833 </form>
834 <pre>form = {{user | json}}</pre>
835 <pre>master = {{master | json}}</pre>
836 </div>
837
838 <script>
839 angular.module('copyExample', [])
840 .controller('ExampleController', ['$scope', function($scope) {
841 $scope.master= {};
842
843 $scope.update = function(user) {
844 // Example with 1 argument
845 $scope.master= angular.copy(user);
846 };
847
848 $scope.reset = function() {
849 // Example with 2 arguments
850 angular.copy($scope.master, $scope.user);
851 };
852
853 $scope.reset();
854 }]);
855 </script>
856 </file>
857 </example>
858 */
859function copy(source, destination) {
860 var stackSource = [];
861 var stackDest = [];
862
863 if (destination) {
864 if (isTypedArray(destination) || isArrayBuffer(destination)) {
865 throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
866 }
867 if (source === destination) {
868 throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
869 }
870
871 // Empty the destination object
872 if (isArray(destination)) {
873 destination.length = 0;
874 } else {
875 forEach(destination, function(value, key) {
876 if (key !== '$$hashKey') {
877 delete destination[key];
878 }
879 });
880 }
881
882 stackSource.push(source);
883 stackDest.push(destination);
884 return copyRecurse(source, destination);
885 }
886
887 return copyElement(source);
888
889 function copyRecurse(source, destination) {
890 var h = destination.$$hashKey;
891 var key;
892 if (isArray(source)) {
893 for (var i = 0, ii = source.length; i < ii; i++) {
894 destination.push(copyElement(source[i]));
895 }
896 } else if (isBlankObject(source)) {
897 // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
898 for (key in source) {
899 destination[key] = copyElement(source[key]);
900 }
901 } else if (source && typeof source.hasOwnProperty === 'function') {
902 // Slow path, which must rely on hasOwnProperty
903 for (key in source) {
904 if (source.hasOwnProperty(key)) {
905 destination[key] = copyElement(source[key]);
906 }
907 }
908 } else {
909 // Slowest path --- hasOwnProperty can't be called as a method
910 for (key in source) {
911 if (hasOwnProperty.call(source, key)) {
912 destination[key] = copyElement(source[key]);
913 }
914 }
915 }
916 setHashKey(destination, h);
917 return destination;
918 }
919
920 function copyElement(source) {
921 // Simple values
922 if (!isObject(source)) {
923 return source;
924 }
925
926 // Already copied values
927 var index = stackSource.indexOf(source);
928 if (index !== -1) {
929 return stackDest[index];
930 }
931
932 if (isWindow(source) || isScope(source)) {
933 throw ngMinErr('cpws',
934 "Can't copy! Making copies of Window or Scope instances is not supported.");
935 }
936
937 var needsRecurse = false;
938 var destination = copyType(source);
939
940 if (destination === undefined) {
941 destination = isArray(source) ? [] : Object.create(getPrototypeOf(source));
942 needsRecurse = true;
943 }
944
945 stackSource.push(source);
946 stackDest.push(destination);
947
948 return needsRecurse
949 ? copyRecurse(source, destination)
950 : destination;
951 }
952
953 function copyType(source) {
954 switch (toString.call(source)) {
955 case '[object Int8Array]':
956 case '[object Int16Array]':
957 case '[object Int32Array]':
958 case '[object Float32Array]':
959 case '[object Float64Array]':
960 case '[object Uint8Array]':
961 case '[object Uint8ClampedArray]':
962 case '[object Uint16Array]':
963 case '[object Uint32Array]':
964 return new source.constructor(copyElement(source.buffer));
965
966 case '[object ArrayBuffer]':
967 //Support: IE10
968 if (!source.slice) {
969 var copied = new ArrayBuffer(source.byteLength);
970 new Uint8Array(copied).set(new Uint8Array(source));
971 return copied;
972 }
973 return source.slice(0);
974
975 case '[object Boolean]':
976 case '[object Number]':
977 case '[object String]':
978 case '[object Date]':
979 return new source.constructor(source.valueOf());
980
981 case '[object RegExp]':
982 var re = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
983 re.lastIndex = source.lastIndex;
984 return re;
985
986 case '[object Blob]':
987 return new source.constructor([source], {type: source.type});
988 }
989
990 if (isFunction(source.cloneNode)) {
991 return source.cloneNode(true);
992 }
993 }
994}
995
996/**
997 * Creates a shallow copy of an object, an array or a primitive.
998 *
999 * Assumes that there are no proto properties for objects.
1000 */
1001function shallowCopy(src, dst) {
1002 if (isArray(src)) {
1003 dst = dst || [];
1004
1005 for (var i = 0, ii = src.length; i < ii; i++) {
1006 dst[i] = src[i];
1007 }
1008 } else if (isObject(src)) {
1009 dst = dst || {};
1010
1011 for (var key in src) {
1012 if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1013 dst[key] = src[key];
1014 }
1015 }
1016 }
1017
1018 return dst || src;
1019}
1020
1021
1022/**
1023 * @ngdoc function
1024 * @name angular.equals
1025 * @module ng
1026 * @kind function
1027 *
1028 * @description
1029 * Determines if two objects or two values are equivalent. Supports value types, regular
1030 * expressions, arrays and objects.
1031 *
1032 * Two objects or values are considered equivalent if at least one of the following is true:
1033 *
1034 * * Both objects or values pass `===` comparison.
1035 * * Both objects or values are of the same type and all of their properties are equal by
1036 * comparing them with `angular.equals`.
1037 * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1038 * * Both values represent the same regular expression (In JavaScript,
1039 * /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1040 * representation matches).
1041 *
1042 * During a property comparison, properties of `function` type and properties with names
1043 * that begin with `$` are ignored.
1044 *
1045 * Scope and DOMWindow objects are being compared only by identify (`===`).
1046 *
1047 * @param {*} o1 Object or value to compare.
1048 * @param {*} o2 Object or value to compare.
1049 * @returns {boolean} True if arguments are equal.
1050 */
1051function equals(o1, o2) {
1052 if (o1 === o2) return true;
1053 if (o1 === null || o2 === null) return false;
1054 if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1055 var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1056 if (t1 == t2 && t1 == 'object') {
1057 if (isArray(o1)) {
1058 if (!isArray(o2)) return false;
1059 if ((length = o1.length) == o2.length) {
1060 for (key = 0; key < length; key++) {
1061 if (!equals(o1[key], o2[key])) return false;
1062 }
1063 return true;
1064 }
1065 } else if (isDate(o1)) {
1066 if (!isDate(o2)) return false;
1067 return equals(o1.getTime(), o2.getTime());
1068 } else if (isRegExp(o1)) {
1069 if (!isRegExp(o2)) return false;
1070 return o1.toString() == o2.toString();
1071 } else {
1072 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1073 isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1074 keySet = createMap();
1075 for (key in o1) {
1076 if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1077 if (!equals(o1[key], o2[key])) return false;
1078 keySet[key] = true;
1079 }
1080 for (key in o2) {
1081 if (!(key in keySet) &&
1082 key.charAt(0) !== '$' &&
1083 isDefined(o2[key]) &&
1084 !isFunction(o2[key])) return false;
1085 }
1086 return true;
1087 }
1088 }
1089 return false;
1090}
1091
1092var csp = function() {
1093 if (!isDefined(csp.rules)) {
1094
1095
1096 var ngCspElement = (document.querySelector('[ng-csp]') ||
1097 document.querySelector('[data-ng-csp]'));
1098
1099 if (ngCspElement) {
1100 var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1101 ngCspElement.getAttribute('data-ng-csp');
1102 csp.rules = {
1103 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1104 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1105 };
1106 } else {
1107 csp.rules = {
1108 noUnsafeEval: noUnsafeEval(),
1109 noInlineStyle: false
1110 };
1111 }
1112 }
1113
1114 return csp.rules;
1115
1116 function noUnsafeEval() {
1117 try {
1118 /* jshint -W031, -W054 */
1119 new Function('');
1120 /* jshint +W031, +W054 */
1121 return false;
1122 } catch (e) {
1123 return true;
1124 }
1125 }
1126};
1127
1128/**
1129 * @ngdoc directive
1130 * @module ng
1131 * @name ngJq
1132 *
1133 * @element ANY
1134 * @param {string=} ngJq the name of the library available under `window`
1135 * to be used for angular.element
1136 * @description
1137 * Use this directive to force the angular.element library. This should be
1138 * used to force either jqLite by leaving ng-jq blank or setting the name of
1139 * the jquery variable under window (eg. jQuery).
1140 *
1141 * Since angular looks for this directive when it is loaded (doesn't wait for the
1142 * DOMContentLoaded event), it must be placed on an element that comes before the script
1143 * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1144 * others ignored.
1145 *
1146 * @example
1147 * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1148 ```html
1149 <!doctype html>
1150 <html ng-app ng-jq>
1151 ...
1152 ...
1153 </html>
1154 ```
1155 * @example
1156 * This example shows how to use a jQuery based library of a different name.
1157 * The library name must be available at the top most 'window'.
1158 ```html
1159 <!doctype html>
1160 <html ng-app ng-jq="jQueryLib">
1161 ...
1162 ...
1163 </html>
1164 ```
1165 */
1166var jq = function() {
1167 if (isDefined(jq.name_)) return jq.name_;
1168 var el;
1169 var i, ii = ngAttrPrefixes.length, prefix, name;
1170 for (i = 0; i < ii; ++i) {
1171 prefix = ngAttrPrefixes[i];
1172 if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1173 name = el.getAttribute(prefix + 'jq');
1174 break;
1175 }
1176 }
1177
1178 return (jq.name_ = name);
1179};
1180
1181function concat(array1, array2, index) {
1182 return array1.concat(slice.call(array2, index));
1183}
1184
1185function sliceArgs(args, startIndex) {
1186 return slice.call(args, startIndex || 0);
1187}
1188
1189
1190/* jshint -W101 */
1191/**
1192 * @ngdoc function
1193 * @name angular.bind
1194 * @module ng
1195 * @kind function
1196 *
1197 * @description
1198 * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1199 * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1200 * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1201 * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1202 *
1203 * @param {Object} self Context which `fn` should be evaluated in.
1204 * @param {function()} fn Function to be bound.
1205 * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1206 * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1207 */
1208/* jshint +W101 */
1209function bind(self, fn) {
1210 var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1211 if (isFunction(fn) && !(fn instanceof RegExp)) {
1212 return curryArgs.length
1213 ? function() {
1214 return arguments.length
1215 ? fn.apply(self, concat(curryArgs, arguments, 0))
1216 : fn.apply(self, curryArgs);
1217 }
1218 : function() {
1219 return arguments.length
1220 ? fn.apply(self, arguments)
1221 : fn.call(self);
1222 };
1223 } else {
1224 // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1225 return fn;
1226 }
1227}
1228
1229
1230function toJsonReplacer(key, value) {
1231 var val = value;
1232
1233 if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1234 val = undefined;
1235 } else if (isWindow(value)) {
1236 val = '$WINDOW';
1237 } else if (value && document === value) {
1238 val = '$DOCUMENT';
1239 } else if (isScope(value)) {
1240 val = '$SCOPE';
1241 }
1242
1243 return val;
1244}
1245
1246
1247/**
1248 * @ngdoc function
1249 * @name angular.toJson
1250 * @module ng
1251 * @kind function
1252 *
1253 * @description
1254 * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1255 * stripped since angular uses this notation internally.
1256 *
1257 * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1258 * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1259 * If set to an integer, the JSON output will contain that many spaces per indentation.
1260 * @returns {string|undefined} JSON-ified string representing `obj`.
1261 */
1262function toJson(obj, pretty) {
1263 if (isUndefined(obj)) return undefined;
1264 if (!isNumber(pretty)) {
1265 pretty = pretty ? 2 : null;
1266 }
1267 return JSON.stringify(obj, toJsonReplacer, pretty);
1268}
1269
1270
1271/**
1272 * @ngdoc function
1273 * @name angular.fromJson
1274 * @module ng
1275 * @kind function
1276 *
1277 * @description
1278 * Deserializes a JSON string.
1279 *
1280 * @param {string} json JSON string to deserialize.
1281 * @returns {Object|Array|string|number} Deserialized JSON string.
1282 */
1283function fromJson(json) {
1284 return isString(json)
1285 ? JSON.parse(json)
1286 : json;
1287}
1288
1289
1290var ALL_COLONS = /:/g;
1291function timezoneToOffset(timezone, fallback) {
1292 // IE/Edge do not "understand" colon (`:`) in timezone
1293 timezone = timezone.replace(ALL_COLONS, '');
1294 var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1295 return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1296}
1297
1298
1299function addDateMinutes(date, minutes) {
1300 date = new Date(date.getTime());
1301 date.setMinutes(date.getMinutes() + minutes);
1302 return date;
1303}
1304
1305
1306function convertTimezoneToLocal(date, timezone, reverse) {
1307 reverse = reverse ? -1 : 1;
1308 var dateTimezoneOffset = date.getTimezoneOffset();
1309 var timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
1310 return addDateMinutes(date, reverse * (timezoneOffset - dateTimezoneOffset));
1311}
1312
1313
1314/**
1315 * @returns {string} Returns the string representation of the element.
1316 */
1317function startingTag(element) {
1318 element = jqLite(element).clone();
1319 try {
1320 // turns out IE does not let you set .html() on elements which
1321 // are not allowed to have children. So we just ignore it.
1322 element.empty();
1323 } catch (e) {}
1324 var elemHtml = jqLite('<div>').append(element).html();
1325 try {
1326 return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1327 elemHtml.
1328 match(/^(<[^>]+>)/)[1].
1329 replace(/^<([\w\-]+)/, function(match, nodeName) {return '<' + lowercase(nodeName);});
1330 } catch (e) {
1331 return lowercase(elemHtml);
1332 }
1333
1334}
1335
1336
1337/////////////////////////////////////////////////
1338
1339/**
1340 * Tries to decode the URI component without throwing an exception.
1341 *
1342 * @private
1343 * @param str value potential URI component to check.
1344 * @returns {boolean} True if `value` can be decoded
1345 * with the decodeURIComponent function.
1346 */
1347function tryDecodeURIComponent(value) {
1348 try {
1349 return decodeURIComponent(value);
1350 } catch (e) {
1351 // Ignore any invalid uri component
1352 }
1353}
1354
1355
1356/**
1357 * Parses an escaped url query string into key-value pairs.
1358 * @returns {Object.<string,boolean|Array>}
1359 */
1360function parseKeyValue(/**string*/keyValue) {
1361 var obj = {};
1362 forEach((keyValue || "").split('&'), function(keyValue) {
1363 var splitPoint, key, val;
1364 if (keyValue) {
1365 key = keyValue = keyValue.replace(/\+/g,'%20');
1366 splitPoint = keyValue.indexOf('=');
1367 if (splitPoint !== -1) {
1368 key = keyValue.substring(0, splitPoint);
1369 val = keyValue.substring(splitPoint + 1);
1370 }
1371 key = tryDecodeURIComponent(key);
1372 if (isDefined(key)) {
1373 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1374 if (!hasOwnProperty.call(obj, key)) {
1375 obj[key] = val;
1376 } else if (isArray(obj[key])) {
1377 obj[key].push(val);
1378 } else {
1379 obj[key] = [obj[key],val];
1380 }
1381 }
1382 }
1383 });
1384 return obj;
1385}
1386
1387function toKeyValue(obj) {
1388 var parts = [];
1389 forEach(obj, function(value, key) {
1390 if (isArray(value)) {
1391 forEach(value, function(arrayValue) {
1392 parts.push(encodeUriQuery(key, true) +
1393 (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1394 });
1395 } else {
1396 parts.push(encodeUriQuery(key, true) +
1397 (value === true ? '' : '=' + encodeUriQuery(value, true)));
1398 }
1399 });
1400 return parts.length ? parts.join('&') : '';
1401}
1402
1403
1404/**
1405 * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1406 * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1407 * segments:
1408 * segment = *pchar
1409 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1410 * pct-encoded = "%" HEXDIG HEXDIG
1411 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1412 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1413 * / "*" / "+" / "," / ";" / "="
1414 */
1415function encodeUriSegment(val) {
1416 return encodeUriQuery(val, true).
1417 replace(/%26/gi, '&').
1418 replace(/%3D/gi, '=').
1419 replace(/%2B/gi, '+');
1420}
1421
1422
1423/**
1424 * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1425 * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1426 * encoded per http://tools.ietf.org/html/rfc3986:
1427 * query = *( pchar / "/" / "?" )
1428 * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
1429 * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
1430 * pct-encoded = "%" HEXDIG HEXDIG
1431 * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
1432 * / "*" / "+" / "," / ";" / "="
1433 */
1434function encodeUriQuery(val, pctEncodeSpaces) {
1435 return encodeURIComponent(val).
1436 replace(/%40/gi, '@').
1437 replace(/%3A/gi, ':').
1438 replace(/%24/g, '$').
1439 replace(/%2C/gi, ',').
1440 replace(/%3B/gi, ';').
1441 replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1442}
1443
1444var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1445
1446function getNgAttribute(element, ngAttr) {
1447 var attr, i, ii = ngAttrPrefixes.length;
1448 for (i = 0; i < ii; ++i) {
1449 attr = ngAttrPrefixes[i] + ngAttr;
1450 if (isString(attr = element.getAttribute(attr))) {
1451 return attr;
1452 }
1453 }
1454 return null;
1455}
1456
1457/**
1458 * @ngdoc directive
1459 * @name ngApp
1460 * @module ng
1461 *
1462 * @element ANY
1463 * @param {angular.Module} ngApp an optional application
1464 * {@link angular.module module} name to load.
1465 * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1466 * created in "strict-di" mode. This means that the application will fail to invoke functions which
1467 * do not use explicit function annotation (and are thus unsuitable for minification), as described
1468 * in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1469 * tracking down the root of these bugs.
1470 *
1471 * @description
1472 *
1473 * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1474 * designates the **root element** of the application and is typically placed near the root element
1475 * of the page - e.g. on the `<body>` or `<html>` tags.
1476 *
1477 * There are a few things to keep in mind when using `ngApp`:
1478 * - only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1479 * found in the document will be used to define the root element to auto-bootstrap as an
1480 * application. To run multiple applications in an HTML document you must manually bootstrap them using
1481 * {@link angular.bootstrap} instead.
1482 * - AngularJS applications cannot be nested within each other.
1483 * - Do not use a directive that uses {@link ng.$compile#transclusion transclusion} on the same element as `ngApp`.
1484 * This includes directives such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and
1485 * {@link ngRoute.ngView `ngView`}.
1486 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1487 * causing animations to stop working and making the injector inaccessible from outside the app.
1488 *
1489 * You can specify an **AngularJS module** to be used as the root module for the application. This
1490 * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1491 * should contain the application code needed or have dependencies on other modules that will
1492 * contain the code. See {@link angular.module} for more information.
1493 *
1494 * In the example below if the `ngApp` directive were not placed on the `html` element then the
1495 * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1496 * would not be resolved to `3`.
1497 *
1498 * `ngApp` is the easiest, and most common way to bootstrap an application.
1499 *
1500 <example module="ngAppDemo">
1501 <file name="index.html">
1502 <div ng-controller="ngAppDemoController">
1503 I can add: {{a}} + {{b}} = {{ a+b }}
1504 </div>
1505 </file>
1506 <file name="script.js">
1507 angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1508 $scope.a = 1;
1509 $scope.b = 2;
1510 });
1511 </file>
1512 </example>
1513 *
1514 * Using `ngStrictDi`, you would see something like this:
1515 *
1516 <example ng-app-included="true">
1517 <file name="index.html">
1518 <div ng-app="ngAppStrictDemo" ng-strict-di>
1519 <div ng-controller="GoodController1">
1520 I can add: {{a}} + {{b}} = {{ a+b }}
1521
1522 <p>This renders because the controller does not fail to
1523 instantiate, by using explicit annotation style (see
1524 script.js for details)
1525 </p>
1526 </div>
1527
1528 <div ng-controller="GoodController2">
1529 Name: <input ng-model="name"><br />
1530 Hello, {{name}}!
1531
1532 <p>This renders because the controller does not fail to
1533 instantiate, by using explicit annotation style
1534 (see script.js for details)
1535 </p>
1536 </div>
1537
1538 <div ng-controller="BadController">
1539 I can add: {{a}} + {{b}} = {{ a+b }}
1540
1541 <p>The controller could not be instantiated, due to relying
1542 on automatic function annotations (which are disabled in
1543 strict mode). As such, the content of this section is not
1544 interpolated, and there should be an error in your web console.
1545 </p>
1546 </div>
1547 </div>
1548 </file>
1549 <file name="script.js">
1550 angular.module('ngAppStrictDemo', [])
1551 // BadController will fail to instantiate, due to relying on automatic function annotation,
1552 // rather than an explicit annotation
1553 .controller('BadController', function($scope) {
1554 $scope.a = 1;
1555 $scope.b = 2;
1556 })
1557 // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1558 // due to using explicit annotations using the array style and $inject property, respectively.
1559 .controller('GoodController1', ['$scope', function($scope) {
1560 $scope.a = 1;
1561 $scope.b = 2;
1562 }])
1563 .controller('GoodController2', GoodController2);
1564 function GoodController2($scope) {
1565 $scope.name = "World";
1566 }
1567 GoodController2.$inject = ['$scope'];
1568 </file>
1569 <file name="style.css">
1570 div[ng-controller] {
1571 margin-bottom: 1em;
1572 -webkit-border-radius: 4px;
1573 border-radius: 4px;
1574 border: 1px solid;
1575 padding: .5em;
1576 }
1577 div[ng-controller^=Good] {
1578 border-color: #d6e9c6;
1579 background-color: #dff0d8;
1580 color: #3c763d;
1581 }
1582 div[ng-controller^=Bad] {
1583 border-color: #ebccd1;
1584 background-color: #f2dede;
1585 color: #a94442;
1586 margin-bottom: 0;
1587 }
1588 </file>
1589 </example>
1590 */
1591function angularInit(element, bootstrap) {
1592 var appElement,
1593 module,
1594 config = {};
1595
1596 // The element `element` has priority over any other element
1597 forEach(ngAttrPrefixes, function(prefix) {
1598 var name = prefix + 'app';
1599
1600 if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1601 appElement = element;
1602 module = element.getAttribute(name);
1603 }
1604 });
1605 forEach(ngAttrPrefixes, function(prefix) {
1606 var name = prefix + 'app';
1607 var candidate;
1608
1609 if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1610 appElement = candidate;
1611 module = candidate.getAttribute(name);
1612 }
1613 });
1614 if (appElement) {
1615 config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1616 bootstrap(appElement, module ? [module] : [], config);
1617 }
1618}
1619
1620/**
1621 * @ngdoc function
1622 * @name angular.bootstrap
1623 * @module ng
1624 * @description
1625 * Use this function to manually start up angular application.
1626 *
1627 * For more information, see the {@link guide/bootstrap Bootstrap guide}.
1628 *
1629 * Angular will detect if it has been loaded into the browser more than once and only allow the
1630 * first loaded script to be bootstrapped and will report a warning to the browser console for
1631 * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1632 * multiple instances of Angular try to work on the DOM.
1633 *
1634 * <div class="alert alert-warning">
1635 * **Note:** Protractor based end-to-end tests cannot use this function to bootstrap manually.
1636 * They must use {@link ng.directive:ngApp ngApp}.
1637 * </div>
1638 *
1639 * <div class="alert alert-warning">
1640 * **Note:** Do not bootstrap the app on an element with a directive that uses {@link ng.$compile#transclusion transclusion},
1641 * such as {@link ng.ngIf `ngIf`}, {@link ng.ngInclude `ngInclude`} and {@link ngRoute.ngView `ngView`}.
1642 * Doing this misplaces the app {@link ng.$rootElement `$rootElement`} and the app's {@link auto.$injector injector},
1643 * causing animations to stop working and making the injector inaccessible from outside the app.
1644 * </div>
1645 *
1646 * ```html
1647 * <!doctype html>
1648 * <html>
1649 * <body>
1650 * <div ng-controller="WelcomeController">
1651 * {{greeting}}
1652 * </div>
1653 *
1654 * <script src="angular.js"></script>
1655 * <script>
1656 * var app = angular.module('demo', [])
1657 * .controller('WelcomeController', function($scope) {
1658 * $scope.greeting = 'Welcome!';
1659 * });
1660 * angular.bootstrap(document, ['demo']);
1661 * </script>
1662 * </body>
1663 * </html>
1664 * ```
1665 *
1666 * @param {DOMElement} element DOM element which is the root of angular application.
1667 * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1668 * Each item in the array should be the name of a predefined module or a (DI annotated)
1669 * function that will be invoked by the injector as a `config` block.
1670 * See: {@link angular.module modules}
1671 * @param {Object=} config an object for defining configuration options for the application. The
1672 * following keys are supported:
1673 *
1674 * * `strictDi` - disable automatic function annotation for the application. This is meant to
1675 * assist in finding bugs which break minified code. Defaults to `false`.
1676 *
1677 * @returns {auto.$injector} Returns the newly created injector for this app.
1678 */
1679function bootstrap(element, modules, config) {
1680 if (!isObject(config)) config = {};
1681 var defaultConfig = {
1682 strictDi: false
1683 };
1684 config = extend(defaultConfig, config);
1685 var doBootstrap = function() {
1686 element = jqLite(element);
1687
1688 if (element.injector()) {
1689 var tag = (element[0] === document) ? 'document' : startingTag(element);
1690 //Encode angle brackets to prevent input from being sanitized to empty string #8683
1691 throw ngMinErr(
1692 'btstrpd',
1693 "App Already Bootstrapped with this Element '{0}'",
1694 tag.replace(/</,'<').replace(/>/,'>'));
1695 }
1696
1697 modules = modules || [];
1698 modules.unshift(['$provide', function($provide) {
1699 $provide.value('$rootElement', element);
1700 }]);
1701
1702 if (config.debugInfoEnabled) {
1703 // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1704 modules.push(['$compileProvider', function($compileProvider) {
1705 $compileProvider.debugInfoEnabled(true);
1706 }]);
1707 }
1708
1709 modules.unshift('ng');
1710 var injector = createInjector(modules, config.strictDi);
1711 injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1712 function bootstrapApply(scope, element, compile, injector) {
1713 scope.$apply(function() {
1714 element.data('$injector', injector);
1715 compile(element)(scope);
1716 });
1717 }]
1718 );
1719 return injector;
1720 };
1721
1722 var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1723 var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1724
1725 if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1726 config.debugInfoEnabled = true;
1727 window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1728 }
1729
1730 if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1731 return doBootstrap();
1732 }
1733
1734 window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1735 angular.resumeBootstrap = function(extraModules) {
1736 forEach(extraModules, function(module) {
1737 modules.push(module);
1738 });
1739 return doBootstrap();
1740 };
1741
1742 if (isFunction(angular.resumeDeferredBootstrap)) {
1743 angular.resumeDeferredBootstrap();
1744 }
1745}
1746
1747/**
1748 * @ngdoc function
1749 * @name angular.reloadWithDebugInfo
1750 * @module ng
1751 * @description
1752 * Use this function to reload the current application with debug information turned on.
1753 * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1754 *
1755 * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1756 */
1757function reloadWithDebugInfo() {
1758 window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1759 window.location.reload();
1760}
1761
1762/**
1763 * @name angular.getTestability
1764 * @module ng
1765 * @description
1766 * Get the testability service for the instance of Angular on the given
1767 * element.
1768 * @param {DOMElement} element DOM element which is the root of angular application.
1769 */
1770function getTestability(rootElement) {
1771 var injector = angular.element(rootElement).injector();
1772 if (!injector) {
1773 throw ngMinErr('test',
1774 'no injector found for element argument to getTestability');
1775 }
1776 return injector.get('$$testability');
1777}
1778
1779var SNAKE_CASE_REGEXP = /[A-Z]/g;
1780function snake_case(name, separator) {
1781 separator = separator || '_';
1782 return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1783 return (pos ? separator : '') + letter.toLowerCase();
1784 });
1785}
1786
1787var bindJQueryFired = false;
1788function bindJQuery() {
1789 var originalCleanData;
1790
1791 if (bindJQueryFired) {
1792 return;
1793 }
1794
1795 // bind to jQuery if present;
1796 var jqName = jq();
1797 jQuery = isUndefined(jqName) ? window.jQuery : // use jQuery (if present)
1798 !jqName ? undefined : // use jqLite
1799 window[jqName]; // use jQuery specified by `ngJq`
1800
1801 // Use jQuery if it exists with proper functionality, otherwise default to us.
1802 // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1803 // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1804 // versions. It will not work for sure with jQuery <1.7, though.
1805 if (jQuery && jQuery.fn.on) {
1806 jqLite = jQuery;
1807 extend(jQuery.fn, {
1808 scope: JQLitePrototype.scope,
1809 isolateScope: JQLitePrototype.isolateScope,
1810 controller: JQLitePrototype.controller,
1811 injector: JQLitePrototype.injector,
1812 inheritedData: JQLitePrototype.inheritedData
1813 });
1814
1815 // All nodes removed from the DOM via various jQuery APIs like .remove()
1816 // are passed through jQuery.cleanData. Monkey-patch this method to fire
1817 // the $destroy event on all removed nodes.
1818 originalCleanData = jQuery.cleanData;
1819 jQuery.cleanData = function(elems) {
1820 var events;
1821 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1822 events = jQuery._data(elem, "events");
1823 if (events && events.$destroy) {
1824 jQuery(elem).triggerHandler('$destroy');
1825 }
1826 }
1827 originalCleanData(elems);
1828 };
1829 } else {
1830 jqLite = JQLite;
1831 }
1832
1833 angular.element = jqLite;
1834
1835 // Prevent double-proxying.
1836 bindJQueryFired = true;
1837}
1838
1839/**
1840 * throw error if the argument is falsy.
1841 */
1842function assertArg(arg, name, reason) {
1843 if (!arg) {
1844 throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1845 }
1846 return arg;
1847}
1848
1849function assertArgFn(arg, name, acceptArrayAnnotation) {
1850 if (acceptArrayAnnotation && isArray(arg)) {
1851 arg = arg[arg.length - 1];
1852 }
1853
1854 assertArg(isFunction(arg), name, 'not a function, got ' +
1855 (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1856 return arg;
1857}
1858
1859/**
1860 * throw error if the name given is hasOwnProperty
1861 * @param {String} name the name to test
1862 * @param {String} context the context in which the name is used, such as module or directive
1863 */
1864function assertNotHasOwnProperty(name, context) {
1865 if (name === 'hasOwnProperty') {
1866 throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1867 }
1868}
1869
1870/**
1871 * Return the value accessible from the object by path. Any undefined traversals are ignored
1872 * @param {Object} obj starting object
1873 * @param {String} path path to traverse
1874 * @param {boolean} [bindFnToScope=true]
1875 * @returns {Object} value as accessible by path
1876 */
1877//TODO(misko): this function needs to be removed
1878function getter(obj, path, bindFnToScope) {
1879 if (!path) return obj;
1880 var keys = path.split('.');
1881 var key;
1882 var lastInstance = obj;
1883 var len = keys.length;
1884
1885 for (var i = 0; i < len; i++) {
1886 key = keys[i];
1887 if (obj) {
1888 obj = (lastInstance = obj)[key];
1889 }
1890 }
1891 if (!bindFnToScope && isFunction(obj)) {
1892 return bind(lastInstance, obj);
1893 }
1894 return obj;
1895}
1896
1897/**
1898 * Return the DOM siblings between the first and last node in the given array.
1899 * @param {Array} array like object
1900 * @returns {Array} the inputted object or a jqLite collection containing the nodes
1901 */
1902function getBlockNodes(nodes) {
1903 // TODO(perf): update `nodes` instead of creating a new object?
1904 var node = nodes[0];
1905 var endNode = nodes[nodes.length - 1];
1906 var blockNodes;
1907
1908 for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1909 if (blockNodes || nodes[i] !== node) {
1910 if (!blockNodes) {
1911 blockNodes = jqLite(slice.call(nodes, 0, i));
1912 }
1913 blockNodes.push(node);
1914 }
1915 }
1916
1917 return blockNodes || nodes;
1918}
1919
1920
1921/**
1922 * Creates a new object without a prototype. This object is useful for lookup without having to
1923 * guard against prototypically inherited properties via hasOwnProperty.
1924 *
1925 * Related micro-benchmarks:
1926 * - http://jsperf.com/object-create2
1927 * - http://jsperf.com/proto-map-lookup/2
1928 * - http://jsperf.com/for-in-vs-object-keys2
1929 *
1930 * @returns {Object}
1931 */
1932function createMap() {
1933 return Object.create(null);
1934}
1935
1936var NODE_TYPE_ELEMENT = 1;
1937var NODE_TYPE_ATTRIBUTE = 2;
1938var NODE_TYPE_TEXT = 3;
1939var NODE_TYPE_COMMENT = 8;
1940var NODE_TYPE_DOCUMENT = 9;
1941var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1942
1943/**
1944 * @ngdoc type
1945 * @name angular.Module
1946 * @module ng
1947 * @description
1948 *
1949 * Interface for configuring angular {@link angular.module modules}.
1950 */
1951
1952function setupModuleLoader(window) {
1953
1954 var $injectorMinErr = minErr('$injector');
1955 var ngMinErr = minErr('ng');
1956
1957 function ensure(obj, name, factory) {
1958 return obj[name] || (obj[name] = factory());
1959 }
1960
1961 var angular = ensure(window, 'angular', Object);
1962
1963 // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
1964 angular.$$minErr = angular.$$minErr || minErr;
1965
1966 return ensure(angular, 'module', function() {
1967 /** @type {Object.<string, angular.Module>} */
1968 var modules = {};
1969
1970 /**
1971 * @ngdoc function
1972 * @name angular.module
1973 * @module ng
1974 * @description
1975 *
1976 * The `angular.module` is a global place for creating, registering and retrieving Angular
1977 * modules.
1978 * All modules (angular core or 3rd party) that should be available to an application must be
1979 * registered using this mechanism.
1980 *
1981 * Passing one argument retrieves an existing {@link angular.Module},
1982 * whereas passing more than one argument creates a new {@link angular.Module}
1983 *
1984 *
1985 * # Module
1986 *
1987 * A module is a collection of services, directives, controllers, filters, and configuration information.
1988 * `angular.module` is used to configure the {@link auto.$injector $injector}.
1989 *
1990 * ```js
1991 * // Create a new module
1992 * var myModule = angular.module('myModule', []);
1993 *
1994 * // register a new service
1995 * myModule.value('appName', 'MyCoolApp');
1996 *
1997 * // configure existing services inside initialization blocks.
1998 * myModule.config(['$locationProvider', function($locationProvider) {
1999 * // Configure existing providers
2000 * $locationProvider.hashPrefix('!');
2001 * }]);
2002 * ```
2003 *
2004 * Then you can create an injector and load your modules like this:
2005 *
2006 * ```js
2007 * var injector = angular.injector(['ng', 'myModule'])
2008 * ```
2009 *
2010 * However it's more likely that you'll just use
2011 * {@link ng.directive:ngApp ngApp} or
2012 * {@link angular.bootstrap} to simplify this process for you.
2013 *
2014 * @param {!string} name The name of the module to create or retrieve.
2015 * @param {!Array.<string>=} requires If specified then new module is being created. If
2016 * unspecified then the module is being retrieved for further configuration.
2017 * @param {Function=} configFn Optional configuration function for the module. Same as
2018 * {@link angular.Module#config Module#config()}.
2019 * @returns {angular.Module} new module with the {@link angular.Module} api.
2020 */
2021 return function module(name, requires, configFn) {
2022 var assertNotHasOwnProperty = function(name, context) {
2023 if (name === 'hasOwnProperty') {
2024 throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2025 }
2026 };
2027
2028 assertNotHasOwnProperty(name, 'module');
2029 if (requires && modules.hasOwnProperty(name)) {
2030 modules[name] = null;
2031 }
2032 return ensure(modules, name, function() {
2033 if (!requires) {
2034 throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2035 "the module name or forgot to load it. If registering a module ensure that you " +
2036 "specify the dependencies as the second argument.", name);
2037 }
2038
2039 /** @type {!Array.<Array.<*>>} */
2040 var invokeQueue = [];
2041
2042 /** @type {!Array.<Function>} */
2043 var configBlocks = [];
2044
2045 /** @type {!Array.<Function>} */
2046 var runBlocks = [];
2047
2048 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2049
2050 /** @type {angular.Module} */
2051 var moduleInstance = {
2052 // Private state
2053 _invokeQueue: invokeQueue,
2054 _configBlocks: configBlocks,
2055 _runBlocks: runBlocks,
2056
2057 /**
2058 * @ngdoc property
2059 * @name angular.Module#requires
2060 * @module ng
2061 *
2062 * @description
2063 * Holds the list of modules which the injector will load before the current module is
2064 * loaded.
2065 */
2066 requires: requires,
2067
2068 /**
2069 * @ngdoc property
2070 * @name angular.Module#name
2071 * @module ng
2072 *
2073 * @description
2074 * Name of the module.
2075 */
2076 name: name,
2077
2078
2079 /**
2080 * @ngdoc method
2081 * @name angular.Module#provider
2082 * @module ng
2083 * @param {string} name service name
2084 * @param {Function} providerType Construction function for creating new instance of the
2085 * service.
2086 * @description
2087 * See {@link auto.$provide#provider $provide.provider()}.
2088 */
2089 provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2090
2091 /**
2092 * @ngdoc method
2093 * @name angular.Module#factory
2094 * @module ng
2095 * @param {string} name service name
2096 * @param {Function} providerFunction Function for creating new instance of the service.
2097 * @description
2098 * See {@link auto.$provide#factory $provide.factory()}.
2099 */
2100 factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2101
2102 /**
2103 * @ngdoc method
2104 * @name angular.Module#service
2105 * @module ng
2106 * @param {string} name service name
2107 * @param {Function} constructor A constructor function that will be instantiated.
2108 * @description
2109 * See {@link auto.$provide#service $provide.service()}.
2110 */
2111 service: invokeLaterAndSetModuleName('$provide', 'service'),
2112
2113 /**
2114 * @ngdoc method
2115 * @name angular.Module#value
2116 * @module ng
2117 * @param {string} name service name
2118 * @param {*} object Service instance object.
2119 * @description
2120 * See {@link auto.$provide#value $provide.value()}.
2121 */
2122 value: invokeLater('$provide', 'value'),
2123
2124 /**
2125 * @ngdoc method
2126 * @name angular.Module#constant
2127 * @module ng
2128 * @param {string} name constant name
2129 * @param {*} object Constant value.
2130 * @description
2131 * Because the constants are fixed, they get applied before other provide methods.
2132 * See {@link auto.$provide#constant $provide.constant()}.
2133 */
2134 constant: invokeLater('$provide', 'constant', 'unshift'),
2135
2136 /**
2137 * @ngdoc method
2138 * @name angular.Module#decorator
2139 * @module ng
2140 * @param {string} The name of the service to decorate.
2141 * @param {Function} This function will be invoked when the service needs to be
2142 * instantiated and should return the decorated service instance.
2143 * @description
2144 * See {@link auto.$provide#decorator $provide.decorator()}.
2145 */
2146 decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2147
2148 /**
2149 * @ngdoc method
2150 * @name angular.Module#animation
2151 * @module ng
2152 * @param {string} name animation name
2153 * @param {Function} animationFactory Factory function for creating new instance of an
2154 * animation.
2155 * @description
2156 *
2157 * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2158 *
2159 *
2160 * Defines an animation hook that can be later used with
2161 * {@link $animate $animate} service and directives that use this service.
2162 *
2163 * ```js
2164 * module.animation('.animation-name', function($inject1, $inject2) {
2165 * return {
2166 * eventName : function(element, done) {
2167 * //code to run the animation
2168 * //once complete, then run done()
2169 * return function cancellationFunction(element) {
2170 * //code to cancel the animation
2171 * }
2172 * }
2173 * }
2174 * })
2175 * ```
2176 *
2177 * See {@link ng.$animateProvider#register $animateProvider.register()} and
2178 * {@link ngAnimate ngAnimate module} for more information.
2179 */
2180 animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2181
2182 /**
2183 * @ngdoc method
2184 * @name angular.Module#filter
2185 * @module ng
2186 * @param {string} name Filter name - this must be a valid angular expression identifier
2187 * @param {Function} filterFactory Factory function for creating new instance of filter.
2188 * @description
2189 * See {@link ng.$filterProvider#register $filterProvider.register()}.
2190 *
2191 * <div class="alert alert-warning">
2192 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2193 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2194 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2195 * (`myapp_subsection_filterx`).
2196 * </div>
2197 */
2198 filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2199
2200 /**
2201 * @ngdoc method
2202 * @name angular.Module#controller
2203 * @module ng
2204 * @param {string|Object} name Controller name, or an object map of controllers where the
2205 * keys are the names and the values are the constructors.
2206 * @param {Function} constructor Controller constructor function.
2207 * @description
2208 * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2209 */
2210 controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2211
2212 /**
2213 * @ngdoc method
2214 * @name angular.Module#directive
2215 * @module ng
2216 * @param {string|Object} name Directive name, or an object map of directives where the
2217 * keys are the names and the values are the factories.
2218 * @param {Function} directiveFactory Factory function for creating new instance of
2219 * directives.
2220 * @description
2221 * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2222 */
2223 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2224
2225 /**
2226 * @ngdoc method
2227 * @name angular.Module#component
2228 * @module ng
2229 * @param {string} name Name of the component in camel-case (i.e. myComp which will match as my-comp)
2230 * @param {Object} options Component definition object (a simplified
2231 * {@link ng.$compile#directive-definition-object directive definition object})
2232 *
2233 * @description
2234 * See {@link ng.$compileProvider#component $compileProvider.component()}.
2235 */
2236 component: invokeLaterAndSetModuleName('$compileProvider', 'component'),
2237
2238 /**
2239 * @ngdoc method
2240 * @name angular.Module#config
2241 * @module ng
2242 * @param {Function} configFn Execute this function on module load. Useful for service
2243 * configuration.
2244 * @description
2245 * Use this method to register work which needs to be performed on module loading.
2246 * For more about how to configure services, see
2247 * {@link providers#provider-recipe Provider Recipe}.
2248 */
2249 config: config,
2250
2251 /**
2252 * @ngdoc method
2253 * @name angular.Module#run
2254 * @module ng
2255 * @param {Function} initializationFn Execute this function after injector creation.
2256 * Useful for application initialization.
2257 * @description
2258 * Use this method to register work which should be performed when the injector is done
2259 * loading all modules.
2260 */
2261 run: function(block) {
2262 runBlocks.push(block);
2263 return this;
2264 }
2265 };
2266
2267 if (configFn) {
2268 config(configFn);
2269 }
2270
2271 return moduleInstance;
2272
2273 /**
2274 * @param {string} provider
2275 * @param {string} method
2276 * @param {String=} insertMethod
2277 * @returns {angular.Module}
2278 */
2279 function invokeLater(provider, method, insertMethod, queue) {
2280 if (!queue) queue = invokeQueue;
2281 return function() {
2282 queue[insertMethod || 'push']([provider, method, arguments]);
2283 return moduleInstance;
2284 };
2285 }
2286
2287 /**
2288 * @param {string} provider
2289 * @param {string} method
2290 * @returns {angular.Module}
2291 */
2292 function invokeLaterAndSetModuleName(provider, method) {
2293 return function(recipeName, factoryFunction) {
2294 if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2295 invokeQueue.push([provider, method, arguments]);
2296 return moduleInstance;
2297 };
2298 }
2299 });
2300 };
2301 });
2302
2303}
2304
2305/* global: toDebugString: true */
2306
2307function serializeObject(obj) {
2308 var seen = [];
2309
2310 return JSON.stringify(obj, function(key, val) {
2311 val = toJsonReplacer(key, val);
2312 if (isObject(val)) {
2313
2314 if (seen.indexOf(val) >= 0) return '...';
2315
2316 seen.push(val);
2317 }
2318 return val;
2319 });
2320}
2321
2322function toDebugString(obj) {
2323 if (typeof obj === 'function') {
2324 return obj.toString().replace(/ \{[\s\S]*$/, '');
2325 } else if (isUndefined(obj)) {
2326 return 'undefined';
2327 } else if (typeof obj !== 'string') {
2328 return serializeObject(obj);
2329 }
2330 return obj;
2331}
2332
2333/* global angularModule: true,
2334 version: true,
2335
2336 $CompileProvider,
2337
2338 htmlAnchorDirective,
2339 inputDirective,
2340 inputDirective,
2341 formDirective,
2342 scriptDirective,
2343 selectDirective,
2344 styleDirective,
2345 optionDirective,
2346 ngBindDirective,
2347 ngBindHtmlDirective,
2348 ngBindTemplateDirective,
2349 ngClassDirective,
2350 ngClassEvenDirective,
2351 ngClassOddDirective,
2352 ngCloakDirective,
2353 ngControllerDirective,
2354 ngFormDirective,
2355 ngHideDirective,
2356 ngIfDirective,
2357 ngIncludeDirective,
2358 ngIncludeFillContentDirective,
2359 ngInitDirective,
2360 ngNonBindableDirective,
2361 ngPluralizeDirective,
2362 ngRepeatDirective,
2363 ngShowDirective,
2364 ngStyleDirective,
2365 ngSwitchDirective,
2366 ngSwitchWhenDirective,
2367 ngSwitchDefaultDirective,
2368 ngOptionsDirective,
2369 ngTranscludeDirective,
2370 ngModelDirective,
2371 ngListDirective,
2372 ngChangeDirective,
2373 patternDirective,
2374 patternDirective,
2375 requiredDirective,
2376 requiredDirective,
2377 minlengthDirective,
2378 minlengthDirective,
2379 maxlengthDirective,
2380 maxlengthDirective,
2381 ngValueDirective,
2382 ngModelOptionsDirective,
2383 ngAttributeAliasDirectives,
2384 ngEventDirectives,
2385
2386 $AnchorScrollProvider,
2387 $AnimateProvider,
2388 $CoreAnimateCssProvider,
2389 $$CoreAnimateJsProvider,
2390 $$CoreAnimateQueueProvider,
2391 $$AnimateRunnerFactoryProvider,
2392 $$AnimateAsyncRunFactoryProvider,
2393 $BrowserProvider,
2394 $CacheFactoryProvider,
2395 $ControllerProvider,
2396 $DateProvider,
2397 $DocumentProvider,
2398 $ExceptionHandlerProvider,
2399 $FilterProvider,
2400 $$ForceReflowProvider,
2401 $InterpolateProvider,
2402 $IntervalProvider,
2403 $$HashMapProvider,
2404 $HttpProvider,
2405 $HttpParamSerializerProvider,
2406 $HttpParamSerializerJQLikeProvider,
2407 $HttpBackendProvider,
2408 $xhrFactoryProvider,
2409 $LocationProvider,
2410 $LogProvider,
2411 $ParseProvider,
2412 $RootScopeProvider,
2413 $QProvider,
2414 $$QProvider,
2415 $$SanitizeUriProvider,
2416 $SceProvider,
2417 $SceDelegateProvider,
2418 $SnifferProvider,
2419 $TemplateCacheProvider,
2420 $TemplateRequestProvider,
2421 $$TestabilityProvider,
2422 $TimeoutProvider,
2423 $$RAFProvider,
2424 $WindowProvider,
2425 $$jqLiteProvider,
2426 $$CookieReaderProvider
2427*/
2428
2429
2430/**
2431 * @ngdoc object
2432 * @name angular.version
2433 * @module ng
2434 * @description
2435 * An object that contains information about the current AngularJS version.
2436 *
2437 * This object has the following properties:
2438 *
2439 * - `full` – `{string}` – Full version string, such as "0.9.18".
2440 * - `major` – `{number}` – Major version number, such as "0".
2441 * - `minor` – `{number}` – Minor version number, such as "9".
2442 * - `dot` – `{number}` – Dot version number, such as "18".
2443 * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2444 */
2445var version = {
2446 full: '1.5.3', // all of these placeholder strings will be replaced by grunt's
2447 major: 1, // package task
2448 minor: 5,
2449 dot: 3,
2450 codeName: 'diplohaplontic-meiosis'
2451};
2452
2453
2454function publishExternalAPI(angular) {
2455 extend(angular, {
2456 'bootstrap': bootstrap,
2457 'copy': copy,
2458 'extend': extend,
2459 'merge': merge,
2460 'equals': equals,
2461 'element': jqLite,
2462 'forEach': forEach,
2463 'injector': createInjector,
2464 'noop': noop,
2465 'bind': bind,
2466 'toJson': toJson,
2467 'fromJson': fromJson,
2468 'identity': identity,
2469 'isUndefined': isUndefined,
2470 'isDefined': isDefined,
2471 'isString': isString,
2472 'isFunction': isFunction,
2473 'isObject': isObject,
2474 'isNumber': isNumber,
2475 'isElement': isElement,
2476 'isArray': isArray,
2477 'version': version,
2478 'isDate': isDate,
2479 'lowercase': lowercase,
2480 'uppercase': uppercase,
2481 'callbacks': {counter: 0},
2482 'getTestability': getTestability,
2483 '$$minErr': minErr,
2484 '$$csp': csp,
2485 'reloadWithDebugInfo': reloadWithDebugInfo
2486 });
2487
2488 angularModule = setupModuleLoader(window);
2489
2490 angularModule('ng', ['ngLocale'], ['$provide',
2491 function ngModule($provide) {
2492 // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2493 $provide.provider({
2494 $$sanitizeUri: $$SanitizeUriProvider
2495 });
2496 $provide.provider('$compile', $CompileProvider).
2497 directive({
2498 a: htmlAnchorDirective,
2499 input: inputDirective,
2500 textarea: inputDirective,
2501 form: formDirective,
2502 script: scriptDirective,
2503 select: selectDirective,
2504 style: styleDirective,
2505 option: optionDirective,
2506 ngBind: ngBindDirective,
2507 ngBindHtml: ngBindHtmlDirective,
2508 ngBindTemplate: ngBindTemplateDirective,
2509 ngClass: ngClassDirective,
2510 ngClassEven: ngClassEvenDirective,
2511 ngClassOdd: ngClassOddDirective,
2512 ngCloak: ngCloakDirective,
2513 ngController: ngControllerDirective,
2514 ngForm: ngFormDirective,
2515 ngHide: ngHideDirective,
2516 ngIf: ngIfDirective,
2517 ngInclude: ngIncludeDirective,
2518 ngInit: ngInitDirective,
2519 ngNonBindable: ngNonBindableDirective,
2520 ngPluralize: ngPluralizeDirective,
2521 ngRepeat: ngRepeatDirective,
2522 ngShow: ngShowDirective,
2523 ngStyle: ngStyleDirective,
2524 ngSwitch: ngSwitchDirective,
2525 ngSwitchWhen: ngSwitchWhenDirective,
2526 ngSwitchDefault: ngSwitchDefaultDirective,
2527 ngOptions: ngOptionsDirective,
2528 ngTransclude: ngTranscludeDirective,
2529 ngModel: ngModelDirective,
2530 ngList: ngListDirective,
2531 ngChange: ngChangeDirective,
2532 pattern: patternDirective,
2533 ngPattern: patternDirective,
2534 required: requiredDirective,
2535 ngRequired: requiredDirective,
2536 minlength: minlengthDirective,
2537 ngMinlength: minlengthDirective,
2538 maxlength: maxlengthDirective,
2539 ngMaxlength: maxlengthDirective,
2540 ngValue: ngValueDirective,
2541 ngModelOptions: ngModelOptionsDirective
2542 }).
2543 directive({
2544 ngInclude: ngIncludeFillContentDirective
2545 }).
2546 directive(ngAttributeAliasDirectives).
2547 directive(ngEventDirectives);
2548 $provide.provider({
2549 $anchorScroll: $AnchorScrollProvider,
2550 $animate: $AnimateProvider,
2551 $animateCss: $CoreAnimateCssProvider,
2552 $$animateJs: $$CoreAnimateJsProvider,
2553 $$animateQueue: $$CoreAnimateQueueProvider,
2554 $$AnimateRunner: $$AnimateRunnerFactoryProvider,
2555 $$animateAsyncRun: $$AnimateAsyncRunFactoryProvider,
2556 $browser: $BrowserProvider,
2557 $cacheFactory: $CacheFactoryProvider,
2558 $controller: $ControllerProvider,
2559 $document: $DocumentProvider,
2560 $exceptionHandler: $ExceptionHandlerProvider,
2561 $filter: $FilterProvider,
2562 $$forceReflow: $$ForceReflowProvider,
2563 $interpolate: $InterpolateProvider,
2564 $interval: $IntervalProvider,
2565 $http: $HttpProvider,
2566 $httpParamSerializer: $HttpParamSerializerProvider,
2567 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2568 $httpBackend: $HttpBackendProvider,
2569 $xhrFactory: $xhrFactoryProvider,
2570 $location: $LocationProvider,
2571 $log: $LogProvider,
2572 $parse: $ParseProvider,
2573 $rootScope: $RootScopeProvider,
2574 $q: $QProvider,
2575 $$q: $$QProvider,
2576 $sce: $SceProvider,
2577 $sceDelegate: $SceDelegateProvider,
2578 $sniffer: $SnifferProvider,
2579 $templateCache: $TemplateCacheProvider,
2580 $templateRequest: $TemplateRequestProvider,
2581 $$testability: $$TestabilityProvider,
2582 $timeout: $TimeoutProvider,
2583 $window: $WindowProvider,
2584 $$rAF: $$RAFProvider,
2585 $$jqLite: $$jqLiteProvider,
2586 $$HashMap: $$HashMapProvider,
2587 $$cookieReader: $$CookieReaderProvider
2588 });
2589 }
2590 ]);
2591}
2592
2593/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2594 * Any commits to this file should be reviewed with security in mind. *
2595 * Changes to this file can potentially create security vulnerabilities. *
2596 * An approval from 2 Core members with history of modifying *
2597 * this file is required. *
2598 * *
2599 * Does the change somehow allow for arbitrary javascript to be executed? *
2600 * Or allows for someone to change the prototype of built-in objects? *
2601 * Or gives undesired access to variables likes document or window? *
2602 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2603
2604/* global JQLitePrototype: true,
2605 addEventListenerFn: true,
2606 removeEventListenerFn: true,
2607 BOOLEAN_ATTR: true,
2608 ALIASED_ATTR: true,
2609*/
2610
2611//////////////////////////////////
2612//JQLite
2613//////////////////////////////////
2614
2615/**
2616 * @ngdoc function
2617 * @name angular.element
2618 * @module ng
2619 * @kind function
2620 *
2621 * @description
2622 * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2623 *
2624 * If jQuery is available, `angular.element` is an alias for the
2625 * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2626 * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or **jqLite**.
2627 *
2628 * jqLite is a tiny, API-compatible subset of jQuery that allows
2629 * Angular to manipulate the DOM in a cross-browser compatible way. jqLite implements only the most
2630 * commonly needed functionality with the goal of having a very small footprint.
2631 *
2632 * To use `jQuery`, simply ensure it is loaded before the `angular.js` file. You can also use the
2633 * {@link ngJq `ngJq`} directive to specify that jqlite should be used over jQuery, or to use a
2634 * specific version of jQuery if multiple versions exist on the page.
2635 *
2636 * <div class="alert alert-info">**Note:** All element references in Angular are always wrapped with jQuery or
2637 * jqLite (such as the element argument in a directive's compile / link function). They are never raw DOM references.</div>
2638 *
2639 * <div class="alert alert-warning">**Note:** Keep in mind that this function will not find elements
2640 * by tag name / CSS selector. For lookups by tag name, try instead `angular.element(document).find(...)`
2641 * or `$document.find()`, or use the standard DOM APIs, e.g. `document.querySelectorAll()`.</div>
2642 *
2643 * ## Angular's jqLite
2644 * jqLite provides only the following jQuery methods:
2645 *
2646 * - [`addClass()`](http://api.jquery.com/addClass/)
2647 * - [`after()`](http://api.jquery.com/after/)
2648 * - [`append()`](http://api.jquery.com/append/)
2649 * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2650 * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2651 * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2652 * - [`clone()`](http://api.jquery.com/clone/)
2653 * - [`contents()`](http://api.jquery.com/contents/)
2654 * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`.
2655 * As a setter, does not convert numbers to strings or append 'px', and also does not have automatic property prefixing.
2656 * - [`data()`](http://api.jquery.com/data/)
2657 * - [`detach()`](http://api.jquery.com/detach/)
2658 * - [`empty()`](http://api.jquery.com/empty/)
2659 * - [`eq()`](http://api.jquery.com/eq/)
2660 * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2661 * - [`hasClass()`](http://api.jquery.com/hasClass/)
2662 * - [`html()`](http://api.jquery.com/html/)
2663 * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2664 * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2665 * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2666 * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2667 * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2668 * - [`prepend()`](http://api.jquery.com/prepend/)
2669 * - [`prop()`](http://api.jquery.com/prop/)
2670 * - [`ready()`](http://api.jquery.com/ready/)
2671 * - [`remove()`](http://api.jquery.com/remove/)
2672 * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2673 * - [`removeClass()`](http://api.jquery.com/removeClass/)
2674 * - [`removeData()`](http://api.jquery.com/removeData/)
2675 * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2676 * - [`text()`](http://api.jquery.com/text/)
2677 * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2678 * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2679 * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2680 * - [`val()`](http://api.jquery.com/val/)
2681 * - [`wrap()`](http://api.jquery.com/wrap/)
2682 *
2683 * ## jQuery/jqLite Extras
2684 * Angular also provides the following additional methods and events to both jQuery and jqLite:
2685 *
2686 * ### Events
2687 * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2688 * on all DOM nodes being removed. This can be used to clean up any 3rd party bindings to the DOM
2689 * element before it is removed.
2690 *
2691 * ### Methods
2692 * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2693 * retrieves controller associated with the `ngController` directive. If `name` is provided as
2694 * camelCase directive name, then the controller for this directive will be retrieved (e.g.
2695 * `'ngModel'`).
2696 * - `injector()` - retrieves the injector of the current element or its parent.
2697 * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2698 * element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2699 * be enabled.
2700 * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2701 * current element. This getter should be used only on elements that contain a directive which starts a new isolate
2702 * scope. Calling `scope()` on this element always returns the original non-isolate scope.
2703 * Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2704 * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2705 * parent element is reached.
2706 *
2707 * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2708 * @returns {Object} jQuery object.
2709 */
2710
2711JQLite.expando = 'ng339';
2712
2713var jqCache = JQLite.cache = {},
2714 jqId = 1,
2715 addEventListenerFn = function(element, type, fn) {
2716 element.addEventListener(type, fn, false);
2717 },
2718 removeEventListenerFn = function(element, type, fn) {
2719 element.removeEventListener(type, fn, false);
2720 };
2721
2722/*
2723 * !!! This is an undocumented "private" function !!!
2724 */
2725JQLite._data = function(node) {
2726 //jQuery always returns an object on cache miss
2727 return this.cache[node[this.expando]] || {};
2728};
2729
2730function jqNextId() { return ++jqId; }
2731
2732
2733var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2734var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2735var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2736var jqLiteMinErr = minErr('jqLite');
2737
2738/**
2739 * Converts snake_case to camelCase.
2740 * Also there is special case for Moz prefix starting with upper case letter.
2741 * @param name Name to normalize
2742 */
2743function camelCase(name) {
2744 return name.
2745 replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2746 return offset ? letter.toUpperCase() : letter;
2747 }).
2748 replace(MOZ_HACK_REGEXP, 'Moz$1');
2749}
2750
2751var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2752var HTML_REGEXP = /<|&#?\w+;/;
2753var TAG_NAME_REGEXP = /<([\w:-]+)/;
2754var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2755
2756var wrapMap = {
2757 'option': [1, '<select multiple="multiple">', '</select>'],
2758
2759 'thead': [1, '<table>', '</table>'],
2760 'col': [2, '<table><colgroup>', '</colgroup></table>'],
2761 'tr': [2, '<table><tbody>', '</tbody></table>'],
2762 'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2763 '_default': [0, "", ""]
2764};
2765
2766wrapMap.optgroup = wrapMap.option;
2767wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2768wrapMap.th = wrapMap.td;
2769
2770
2771function jqLiteIsTextNode(html) {
2772 return !HTML_REGEXP.test(html);
2773}
2774
2775function jqLiteAcceptsData(node) {
2776 // The window object can accept data but has no nodeType
2777 // Otherwise we are only interested in elements (1) and documents (9)
2778 var nodeType = node.nodeType;
2779 return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2780}
2781
2782function jqLiteHasData(node) {
2783 for (var key in jqCache[node.ng339]) {
2784 return true;
2785 }
2786 return false;
2787}
2788
2789function jqLiteCleanData(nodes) {
2790 for (var i = 0, ii = nodes.length; i < ii; i++) {
2791 jqLiteRemoveData(nodes[i]);
2792 }
2793}
2794
2795function jqLiteBuildFragment(html, context) {
2796 var tmp, tag, wrap,
2797 fragment = context.createDocumentFragment(),
2798 nodes = [], i;
2799
2800 if (jqLiteIsTextNode(html)) {
2801 // Convert non-html into a text node
2802 nodes.push(context.createTextNode(html));
2803 } else {
2804 // Convert html into DOM nodes
2805 tmp = tmp || fragment.appendChild(context.createElement("div"));
2806 tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2807 wrap = wrapMap[tag] || wrapMap._default;
2808 tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2809
2810 // Descend through wrappers to the right content
2811 i = wrap[0];
2812 while (i--) {
2813 tmp = tmp.lastChild;
2814 }
2815
2816 nodes = concat(nodes, tmp.childNodes);
2817
2818 tmp = fragment.firstChild;
2819 tmp.textContent = "";
2820 }
2821
2822 // Remove wrapper from fragment
2823 fragment.textContent = "";
2824 fragment.innerHTML = ""; // Clear inner HTML
2825 forEach(nodes, function(node) {
2826 fragment.appendChild(node);
2827 });
2828
2829 return fragment;
2830}
2831
2832function jqLiteParseHTML(html, context) {
2833 context = context || document;
2834 var parsed;
2835
2836 if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2837 return [context.createElement(parsed[1])];
2838 }
2839
2840 if ((parsed = jqLiteBuildFragment(html, context))) {
2841 return parsed.childNodes;
2842 }
2843
2844 return [];
2845}
2846
2847function jqLiteWrapNode(node, wrapper) {
2848 var parent = node.parentNode;
2849
2850 if (parent) {
2851 parent.replaceChild(wrapper, node);
2852 }
2853
2854 wrapper.appendChild(node);
2855}
2856
2857
2858// IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2859var jqLiteContains = Node.prototype.contains || function(arg) {
2860 // jshint bitwise: false
2861 return !!(this.compareDocumentPosition(arg) & 16);
2862 // jshint bitwise: true
2863};
2864
2865/////////////////////////////////////////////
2866function JQLite(element) {
2867 if (element instanceof JQLite) {
2868 return element;
2869 }
2870
2871 var argIsString;
2872
2873 if (isString(element)) {
2874 element = trim(element);
2875 argIsString = true;
2876 }
2877 if (!(this instanceof JQLite)) {
2878 if (argIsString && element.charAt(0) != '<') {
2879 throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2880 }
2881 return new JQLite(element);
2882 }
2883
2884 if (argIsString) {
2885 jqLiteAddNodes(this, jqLiteParseHTML(element));
2886 } else {
2887 jqLiteAddNodes(this, element);
2888 }
2889}
2890
2891function jqLiteClone(element) {
2892 return element.cloneNode(true);
2893}
2894
2895function jqLiteDealoc(element, onlyDescendants) {
2896 if (!onlyDescendants) jqLiteRemoveData(element);
2897
2898 if (element.querySelectorAll) {
2899 var descendants = element.querySelectorAll('*');
2900 for (var i = 0, l = descendants.length; i < l; i++) {
2901 jqLiteRemoveData(descendants[i]);
2902 }
2903 }
2904}
2905
2906function jqLiteOff(element, type, fn, unsupported) {
2907 if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2908
2909 var expandoStore = jqLiteExpandoStore(element);
2910 var events = expandoStore && expandoStore.events;
2911 var handle = expandoStore && expandoStore.handle;
2912
2913 if (!handle) return; //no listeners registered
2914
2915 if (!type) {
2916 for (type in events) {
2917 if (type !== '$destroy') {
2918 removeEventListenerFn(element, type, handle);
2919 }
2920 delete events[type];
2921 }
2922 } else {
2923
2924 var removeHandler = function(type) {
2925 var listenerFns = events[type];
2926 if (isDefined(fn)) {
2927 arrayRemove(listenerFns || [], fn);
2928 }
2929 if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2930 removeEventListenerFn(element, type, handle);
2931 delete events[type];
2932 }
2933 };
2934
2935 forEach(type.split(' '), function(type) {
2936 removeHandler(type);
2937 if (MOUSE_EVENT_MAP[type]) {
2938 removeHandler(MOUSE_EVENT_MAP[type]);
2939 }
2940 });
2941 }
2942}
2943
2944function jqLiteRemoveData(element, name) {
2945 var expandoId = element.ng339;
2946 var expandoStore = expandoId && jqCache[expandoId];
2947
2948 if (expandoStore) {
2949 if (name) {
2950 delete expandoStore.data[name];
2951 return;
2952 }
2953
2954 if (expandoStore.handle) {
2955 if (expandoStore.events.$destroy) {
2956 expandoStore.handle({}, '$destroy');
2957 }
2958 jqLiteOff(element);
2959 }
2960 delete jqCache[expandoId];
2961 element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2962 }
2963}
2964
2965
2966function jqLiteExpandoStore(element, createIfNecessary) {
2967 var expandoId = element.ng339,
2968 expandoStore = expandoId && jqCache[expandoId];
2969
2970 if (createIfNecessary && !expandoStore) {
2971 element.ng339 = expandoId = jqNextId();
2972 expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2973 }
2974
2975 return expandoStore;
2976}
2977
2978
2979function jqLiteData(element, key, value) {
2980 if (jqLiteAcceptsData(element)) {
2981
2982 var isSimpleSetter = isDefined(value);
2983 var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2984 var massGetter = !key;
2985 var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2986 var data = expandoStore && expandoStore.data;
2987
2988 if (isSimpleSetter) { // data('key', value)
2989 data[key] = value;
2990 } else {
2991 if (massGetter) { // data()
2992 return data;
2993 } else {
2994 if (isSimpleGetter) { // data('key')
2995 // don't force creation of expandoStore if it doesn't exist yet
2996 return data && data[key];
2997 } else { // mass-setter: data({key1: val1, key2: val2})
2998 extend(data, key);
2999 }
3000 }
3001 }
3002 }
3003}
3004
3005function jqLiteHasClass(element, selector) {
3006 if (!element.getAttribute) return false;
3007 return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3008 indexOf(" " + selector + " ") > -1);
3009}
3010
3011function jqLiteRemoveClass(element, cssClasses) {
3012 if (cssClasses && element.setAttribute) {
3013 forEach(cssClasses.split(' '), function(cssClass) {
3014 element.setAttribute('class', trim(
3015 (" " + (element.getAttribute('class') || '') + " ")
3016 .replace(/[\n\t]/g, " ")
3017 .replace(" " + trim(cssClass) + " ", " "))
3018 );
3019 });
3020 }
3021}
3022
3023function jqLiteAddClass(element, cssClasses) {
3024 if (cssClasses && element.setAttribute) {
3025 var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3026 .replace(/[\n\t]/g, " ");
3027
3028 forEach(cssClasses.split(' '), function(cssClass) {
3029 cssClass = trim(cssClass);
3030 if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3031 existingClasses += cssClass + ' ';
3032 }
3033 });
3034
3035 element.setAttribute('class', trim(existingClasses));
3036 }
3037}
3038
3039
3040function jqLiteAddNodes(root, elements) {
3041 // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3042
3043 if (elements) {
3044
3045 // if a Node (the most common case)
3046 if (elements.nodeType) {
3047 root[root.length++] = elements;
3048 } else {
3049 var length = elements.length;
3050
3051 // if an Array or NodeList and not a Window
3052 if (typeof length === 'number' && elements.window !== elements) {
3053 if (length) {
3054 for (var i = 0; i < length; i++) {
3055 root[root.length++] = elements[i];
3056 }
3057 }
3058 } else {
3059 root[root.length++] = elements;
3060 }
3061 }
3062 }
3063}
3064
3065
3066function jqLiteController(element, name) {
3067 return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3068}
3069
3070function jqLiteInheritedData(element, name, value) {
3071 // if element is the document object work with the html element instead
3072 // this makes $(document).scope() possible
3073 if (element.nodeType == NODE_TYPE_DOCUMENT) {
3074 element = element.documentElement;
3075 }
3076 var names = isArray(name) ? name : [name];
3077
3078 while (element) {
3079 for (var i = 0, ii = names.length; i < ii; i++) {
3080 if (isDefined(value = jqLite.data(element, names[i]))) return value;
3081 }
3082
3083 // If dealing with a document fragment node with a host element, and no parent, use the host
3084 // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3085 // to lookup parent controllers.
3086 element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3087 }
3088}
3089
3090function jqLiteEmpty(element) {
3091 jqLiteDealoc(element, true);
3092 while (element.firstChild) {
3093 element.removeChild(element.firstChild);
3094 }
3095}
3096
3097function jqLiteRemove(element, keepData) {
3098 if (!keepData) jqLiteDealoc(element);
3099 var parent = element.parentNode;
3100 if (parent) parent.removeChild(element);
3101}
3102
3103
3104function jqLiteDocumentLoaded(action, win) {
3105 win = win || window;
3106 if (win.document.readyState === 'complete') {
3107 // Force the action to be run async for consistent behavior
3108 // from the action's point of view
3109 // i.e. it will definitely not be in a $apply
3110 win.setTimeout(action);
3111 } else {
3112 // No need to unbind this handler as load is only ever called once
3113 jqLite(win).on('load', action);
3114 }
3115}
3116
3117//////////////////////////////////////////
3118// Functions which are declared directly.
3119//////////////////////////////////////////
3120var JQLitePrototype = JQLite.prototype = {
3121 ready: function(fn) {
3122 var fired = false;
3123
3124 function trigger() {
3125 if (fired) return;
3126 fired = true;
3127 fn();
3128 }
3129
3130 // check if document is already loaded
3131 if (document.readyState === 'complete') {
3132 setTimeout(trigger);
3133 } else {
3134 this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3135 // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3136 // jshint -W064
3137 JQLite(window).on('load', trigger); // fallback to window.onload for others
3138 // jshint +W064
3139 }
3140 },
3141 toString: function() {
3142 var value = [];
3143 forEach(this, function(e) { value.push('' + e);});
3144 return '[' + value.join(', ') + ']';
3145 },
3146
3147 eq: function(index) {
3148 return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3149 },
3150
3151 length: 0,
3152 push: push,
3153 sort: [].sort,
3154 splice: [].splice
3155};
3156
3157//////////////////////////////////////////
3158// Functions iterating getter/setters.
3159// these functions return self on setter and
3160// value on get.
3161//////////////////////////////////////////
3162var BOOLEAN_ATTR = {};
3163forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3164 BOOLEAN_ATTR[lowercase(value)] = value;
3165});
3166var BOOLEAN_ELEMENTS = {};
3167forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3168 BOOLEAN_ELEMENTS[value] = true;
3169});
3170var ALIASED_ATTR = {
3171 'ngMinlength': 'minlength',
3172 'ngMaxlength': 'maxlength',
3173 'ngMin': 'min',
3174 'ngMax': 'max',
3175 'ngPattern': 'pattern'
3176};
3177
3178function getBooleanAttrName(element, name) {
3179 // check dom last since we will most likely fail on name
3180 var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3181
3182 // booleanAttr is here twice to minimize DOM access
3183 return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3184}
3185
3186function getAliasedAttrName(name) {
3187 return ALIASED_ATTR[name];
3188}
3189
3190forEach({
3191 data: jqLiteData,
3192 removeData: jqLiteRemoveData,
3193 hasData: jqLiteHasData,
3194 cleanData: jqLiteCleanData
3195}, function(fn, name) {
3196 JQLite[name] = fn;
3197});
3198
3199forEach({
3200 data: jqLiteData,
3201 inheritedData: jqLiteInheritedData,
3202
3203 scope: function(element) {
3204 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3205 return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3206 },
3207
3208 isolateScope: function(element) {
3209 // Can't use jqLiteData here directly so we stay compatible with jQuery!
3210 return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3211 },
3212
3213 controller: jqLiteController,
3214
3215 injector: function(element) {
3216 return jqLiteInheritedData(element, '$injector');
3217 },
3218
3219 removeAttr: function(element, name) {
3220 element.removeAttribute(name);
3221 },
3222
3223 hasClass: jqLiteHasClass,
3224
3225 css: function(element, name, value) {
3226 name = camelCase(name);
3227
3228 if (isDefined(value)) {
3229 element.style[name] = value;
3230 } else {
3231 return element.style[name];
3232 }
3233 },
3234
3235 attr: function(element, name, value) {
3236 var nodeType = element.nodeType;
3237 if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3238 return;
3239 }
3240 var lowercasedName = lowercase(name);
3241 if (BOOLEAN_ATTR[lowercasedName]) {
3242 if (isDefined(value)) {
3243 if (!!value) {
3244 element[name] = true;
3245 element.setAttribute(name, lowercasedName);
3246 } else {
3247 element[name] = false;
3248 element.removeAttribute(lowercasedName);
3249 }
3250 } else {
3251 return (element[name] ||
3252 (element.attributes.getNamedItem(name) || noop).specified)
3253 ? lowercasedName
3254 : undefined;
3255 }
3256 } else if (isDefined(value)) {
3257 element.setAttribute(name, value);
3258 } else if (element.getAttribute) {
3259 // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3260 // some elements (e.g. Document) don't have get attribute, so return undefined
3261 var ret = element.getAttribute(name, 2);
3262 // normalize non-existing attributes to undefined (as jQuery)
3263 return ret === null ? undefined : ret;
3264 }
3265 },
3266
3267 prop: function(element, name, value) {
3268 if (isDefined(value)) {
3269 element[name] = value;
3270 } else {
3271 return element[name];
3272 }
3273 },
3274
3275 text: (function() {
3276 getText.$dv = '';
3277 return getText;
3278
3279 function getText(element, value) {
3280 if (isUndefined(value)) {
3281 var nodeType = element.nodeType;
3282 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3283 }
3284 element.textContent = value;
3285 }
3286 })(),
3287
3288 val: function(element, value) {
3289 if (isUndefined(value)) {
3290 if (element.multiple && nodeName_(element) === 'select') {
3291 var result = [];
3292 forEach(element.options, function(option) {
3293 if (option.selected) {
3294 result.push(option.value || option.text);
3295 }
3296 });
3297 return result.length === 0 ? null : result;
3298 }
3299 return element.value;
3300 }
3301 element.value = value;
3302 },
3303
3304 html: function(element, value) {
3305 if (isUndefined(value)) {
3306 return element.innerHTML;
3307 }
3308 jqLiteDealoc(element, true);
3309 element.innerHTML = value;
3310 },
3311
3312 empty: jqLiteEmpty
3313}, function(fn, name) {
3314 /**
3315 * Properties: writes return selection, reads return first value
3316 */
3317 JQLite.prototype[name] = function(arg1, arg2) {
3318 var i, key;
3319 var nodeCount = this.length;
3320
3321 // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3322 // in a way that survives minification.
3323 // jqLiteEmpty takes no arguments but is a setter.
3324 if (fn !== jqLiteEmpty &&
3325 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3326 if (isObject(arg1)) {
3327
3328 // we are a write, but the object properties are the key/values
3329 for (i = 0; i < nodeCount; i++) {
3330 if (fn === jqLiteData) {
3331 // data() takes the whole object in jQuery
3332 fn(this[i], arg1);
3333 } else {
3334 for (key in arg1) {
3335 fn(this[i], key, arg1[key]);
3336 }
3337 }
3338 }
3339 // return self for chaining
3340 return this;
3341 } else {
3342 // we are a read, so read the first child.
3343 // TODO: do we still need this?
3344 var value = fn.$dv;
3345 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3346 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3347 for (var j = 0; j < jj; j++) {
3348 var nodeValue = fn(this[j], arg1, arg2);
3349 value = value ? value + nodeValue : nodeValue;
3350 }
3351 return value;
3352 }
3353 } else {
3354 // we are a write, so apply to all children
3355 for (i = 0; i < nodeCount; i++) {
3356 fn(this[i], arg1, arg2);
3357 }
3358 // return self for chaining
3359 return this;
3360 }
3361 };
3362});
3363
3364function createEventHandler(element, events) {
3365 var eventHandler = function(event, type) {
3366 // jQuery specific api
3367 event.isDefaultPrevented = function() {
3368 return event.defaultPrevented;
3369 };
3370
3371 var eventFns = events[type || event.type];
3372 var eventFnsLength = eventFns ? eventFns.length : 0;
3373
3374 if (!eventFnsLength) return;
3375
3376 if (isUndefined(event.immediatePropagationStopped)) {
3377 var originalStopImmediatePropagation = event.stopImmediatePropagation;
3378 event.stopImmediatePropagation = function() {
3379 event.immediatePropagationStopped = true;
3380
3381 if (event.stopPropagation) {
3382 event.stopPropagation();
3383 }
3384
3385 if (originalStopImmediatePropagation) {
3386 originalStopImmediatePropagation.call(event);
3387 }
3388 };
3389 }
3390
3391 event.isImmediatePropagationStopped = function() {
3392 return event.immediatePropagationStopped === true;
3393 };
3394
3395 // Some events have special handlers that wrap the real handler
3396 var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3397
3398 // Copy event handlers in case event handlers array is modified during execution.
3399 if ((eventFnsLength > 1)) {
3400 eventFns = shallowCopy(eventFns);
3401 }
3402
3403 for (var i = 0; i < eventFnsLength; i++) {
3404 if (!event.isImmediatePropagationStopped()) {
3405 handlerWrapper(element, event, eventFns[i]);
3406 }
3407 }
3408 };
3409
3410 // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3411 // events on `element`
3412 eventHandler.elem = element;
3413 return eventHandler;
3414}
3415
3416function defaultHandlerWrapper(element, event, handler) {
3417 handler.call(element, event);
3418}
3419
3420function specialMouseHandlerWrapper(target, event, handler) {
3421 // Refer to jQuery's implementation of mouseenter & mouseleave
3422 // Read about mouseenter and mouseleave:
3423 // http://www.quirksmode.org/js/events_mouse.html#link8
3424 var related = event.relatedTarget;
3425 // For mousenter/leave call the handler if related is outside the target.
3426 // NB: No relatedTarget if the mouse left/entered the browser window
3427 if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3428 handler.call(target, event);
3429 }
3430}
3431
3432//////////////////////////////////////////
3433// Functions iterating traversal.
3434// These functions chain results into a single
3435// selector.
3436//////////////////////////////////////////
3437forEach({
3438 removeData: jqLiteRemoveData,
3439
3440 on: function jqLiteOn(element, type, fn, unsupported) {
3441 if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3442
3443 // Do not add event handlers to non-elements because they will not be cleaned up.
3444 if (!jqLiteAcceptsData(element)) {
3445 return;
3446 }
3447
3448 var expandoStore = jqLiteExpandoStore(element, true);
3449 var events = expandoStore.events;
3450 var handle = expandoStore.handle;
3451
3452 if (!handle) {
3453 handle = expandoStore.handle = createEventHandler(element, events);
3454 }
3455
3456 // http://jsperf.com/string-indexof-vs-split
3457 var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3458 var i = types.length;
3459
3460 var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3461 var eventFns = events[type];
3462
3463 if (!eventFns) {
3464 eventFns = events[type] = [];
3465 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3466 if (type !== '$destroy' && !noEventListener) {
3467 addEventListenerFn(element, type, handle);
3468 }
3469 }
3470
3471 eventFns.push(fn);
3472 };
3473
3474 while (i--) {
3475 type = types[i];
3476 if (MOUSE_EVENT_MAP[type]) {
3477 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3478 addHandler(type, undefined, true);
3479 } else {
3480 addHandler(type);
3481 }
3482 }
3483 },
3484
3485 off: jqLiteOff,
3486
3487 one: function(element, type, fn) {
3488 element = jqLite(element);
3489
3490 //add the listener twice so that when it is called
3491 //you can remove the original function and still be
3492 //able to call element.off(ev, fn) normally
3493 element.on(type, function onFn() {
3494 element.off(type, fn);
3495 element.off(type, onFn);
3496 });
3497 element.on(type, fn);
3498 },
3499
3500 replaceWith: function(element, replaceNode) {
3501 var index, parent = element.parentNode;
3502 jqLiteDealoc(element);
3503 forEach(new JQLite(replaceNode), function(node) {
3504 if (index) {
3505 parent.insertBefore(node, index.nextSibling);
3506 } else {
3507 parent.replaceChild(node, element);
3508 }
3509 index = node;
3510 });
3511 },
3512
3513 children: function(element) {
3514 var children = [];
3515 forEach(element.childNodes, function(element) {
3516 if (element.nodeType === NODE_TYPE_ELEMENT) {
3517 children.push(element);
3518 }
3519 });
3520 return children;
3521 },
3522
3523 contents: function(element) {
3524 return element.contentDocument || element.childNodes || [];
3525 },
3526
3527 append: function(element, node) {
3528 var nodeType = element.nodeType;
3529 if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3530
3531 node = new JQLite(node);
3532
3533 for (var i = 0, ii = node.length; i < ii; i++) {
3534 var child = node[i];
3535 element.appendChild(child);
3536 }
3537 },
3538
3539 prepend: function(element, node) {
3540 if (element.nodeType === NODE_TYPE_ELEMENT) {
3541 var index = element.firstChild;
3542 forEach(new JQLite(node), function(child) {
3543 element.insertBefore(child, index);
3544 });
3545 }
3546 },
3547
3548 wrap: function(element, wrapNode) {
3549 jqLiteWrapNode(element, jqLite(wrapNode).eq(0).clone()[0]);
3550 },
3551
3552 remove: jqLiteRemove,
3553
3554 detach: function(element) {
3555 jqLiteRemove(element, true);
3556 },
3557
3558 after: function(element, newElement) {
3559 var index = element, parent = element.parentNode;
3560 newElement = new JQLite(newElement);
3561
3562 for (var i = 0, ii = newElement.length; i < ii; i++) {
3563 var node = newElement[i];
3564 parent.insertBefore(node, index.nextSibling);
3565 index = node;
3566 }
3567 },
3568
3569 addClass: jqLiteAddClass,
3570 removeClass: jqLiteRemoveClass,
3571
3572 toggleClass: function(element, selector, condition) {
3573 if (selector) {
3574 forEach(selector.split(' '), function(className) {
3575 var classCondition = condition;
3576 if (isUndefined(classCondition)) {
3577 classCondition = !jqLiteHasClass(element, className);
3578 }
3579 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3580 });
3581 }
3582 },
3583
3584 parent: function(element) {
3585 var parent = element.parentNode;
3586 return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3587 },
3588
3589 next: function(element) {
3590 return element.nextElementSibling;
3591 },
3592
3593 find: function(element, selector) {
3594 if (element.getElementsByTagName) {
3595 return element.getElementsByTagName(selector);
3596 } else {
3597 return [];
3598 }
3599 },
3600
3601 clone: jqLiteClone,
3602
3603 triggerHandler: function(element, event, extraParameters) {
3604
3605 var dummyEvent, eventFnsCopy, handlerArgs;
3606 var eventName = event.type || event;
3607 var expandoStore = jqLiteExpandoStore(element);
3608 var events = expandoStore && expandoStore.events;
3609 var eventFns = events && events[eventName];
3610
3611 if (eventFns) {
3612 // Create a dummy event to pass to the handlers
3613 dummyEvent = {
3614 preventDefault: function() { this.defaultPrevented = true; },
3615 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3616 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3617 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3618 stopPropagation: noop,
3619 type: eventName,
3620 target: element
3621 };
3622
3623 // If a custom event was provided then extend our dummy event with it
3624 if (event.type) {
3625 dummyEvent = extend(dummyEvent, event);
3626 }
3627
3628 // Copy event handlers in case event handlers array is modified during execution.
3629 eventFnsCopy = shallowCopy(eventFns);
3630 handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3631
3632 forEach(eventFnsCopy, function(fn) {
3633 if (!dummyEvent.isImmediatePropagationStopped()) {
3634 fn.apply(element, handlerArgs);
3635 }
3636 });
3637 }
3638 }
3639}, function(fn, name) {
3640 /**
3641 * chaining functions
3642 */
3643 JQLite.prototype[name] = function(arg1, arg2, arg3) {
3644 var value;
3645
3646 for (var i = 0, ii = this.length; i < ii; i++) {
3647 if (isUndefined(value)) {
3648 value = fn(this[i], arg1, arg2, arg3);
3649 if (isDefined(value)) {
3650 // any function which returns a value needs to be wrapped
3651 value = jqLite(value);
3652 }
3653 } else {
3654 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3655 }
3656 }
3657 return isDefined(value) ? value : this;
3658 };
3659
3660 // bind legacy bind/unbind to on/off
3661 JQLite.prototype.bind = JQLite.prototype.on;
3662 JQLite.prototype.unbind = JQLite.prototype.off;
3663});
3664
3665
3666// Provider for private $$jqLite service
3667function $$jqLiteProvider() {
3668 this.$get = function $$jqLite() {
3669 return extend(JQLite, {
3670 hasClass: function(node, classes) {
3671 if (node.attr) node = node[0];
3672 return jqLiteHasClass(node, classes);
3673 },
3674 addClass: function(node, classes) {
3675 if (node.attr) node = node[0];
3676 return jqLiteAddClass(node, classes);
3677 },
3678 removeClass: function(node, classes) {
3679 if (node.attr) node = node[0];
3680 return jqLiteRemoveClass(node, classes);
3681 }
3682 });
3683 };
3684}
3685
3686/**
3687 * Computes a hash of an 'obj'.
3688 * Hash of a:
3689 * string is string
3690 * number is number as string
3691 * object is either result of calling $$hashKey function on the object or uniquely generated id,
3692 * that is also assigned to the $$hashKey property of the object.
3693 *
3694 * @param obj
3695 * @returns {string} hash string such that the same input will have the same hash string.
3696 * The resulting string key is in 'type:hashKey' format.
3697 */
3698function hashKey(obj, nextUidFn) {
3699 var key = obj && obj.$$hashKey;
3700
3701 if (key) {
3702 if (typeof key === 'function') {
3703 key = obj.$$hashKey();
3704 }
3705 return key;
3706 }
3707
3708 var objType = typeof obj;
3709 if (objType == 'function' || (objType == 'object' && obj !== null)) {
3710 key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3711 } else {
3712 key = objType + ':' + obj;
3713 }
3714
3715 return key;
3716}
3717
3718/**
3719 * HashMap which can use objects as keys
3720 */
3721function HashMap(array, isolatedUid) {
3722 if (isolatedUid) {
3723 var uid = 0;
3724 this.nextUid = function() {
3725 return ++uid;
3726 };
3727 }
3728 forEach(array, this.put, this);
3729}
3730HashMap.prototype = {
3731 /**
3732 * Store key value pair
3733 * @param key key to store can be any type
3734 * @param value value to store can be any type
3735 */
3736 put: function(key, value) {
3737 this[hashKey(key, this.nextUid)] = value;
3738 },
3739
3740 /**
3741 * @param key
3742 * @returns {Object} the value for the key
3743 */
3744 get: function(key) {
3745 return this[hashKey(key, this.nextUid)];
3746 },
3747
3748 /**
3749 * Remove the key/value pair
3750 * @param key
3751 */
3752 remove: function(key) {
3753 var value = this[key = hashKey(key, this.nextUid)];
3754 delete this[key];
3755 return value;
3756 }
3757};
3758
3759var $$HashMapProvider = [function() {
3760 this.$get = [function() {
3761 return HashMap;
3762 }];
3763}];
3764
3765/**
3766 * @ngdoc function
3767 * @module ng
3768 * @name angular.injector
3769 * @kind function
3770 *
3771 * @description
3772 * Creates an injector object that can be used for retrieving services as well as for
3773 * dependency injection (see {@link guide/di dependency injection}).
3774 *
3775 * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3776 * {@link angular.module}. The `ng` module must be explicitly added.
3777 * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3778 * disallows argument name annotation inference.
3779 * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3780 *
3781 * @example
3782 * Typical usage
3783 * ```js
3784 * // create an injector
3785 * var $injector = angular.injector(['ng']);
3786 *
3787 * // use the injector to kick off your application
3788 * // use the type inference to auto inject arguments, or use implicit injection
3789 * $injector.invoke(function($rootScope, $compile, $document) {
3790 * $compile($document)($rootScope);
3791 * $rootScope.$digest();
3792 * });
3793 * ```
3794 *
3795 * Sometimes you want to get access to the injector of a currently running Angular app
3796 * from outside Angular. Perhaps, you want to inject and compile some markup after the
3797 * application has been bootstrapped. You can do this using the extra `injector()` added
3798 * to JQuery/jqLite elements. See {@link angular.element}.
3799 *
3800 * *This is fairly rare but could be the case if a third party library is injecting the
3801 * markup.*
3802 *
3803 * In the following example a new block of HTML containing a `ng-controller`
3804 * directive is added to the end of the document body by JQuery. We then compile and link
3805 * it into the current AngularJS scope.
3806 *
3807 * ```js
3808 * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3809 * $(document.body).append($div);
3810 *
3811 * angular.element(document).injector().invoke(function($compile) {
3812 * var scope = angular.element($div).scope();
3813 * $compile($div)(scope);
3814 * });
3815 * ```
3816 */
3817
3818
3819/**
3820 * @ngdoc module
3821 * @name auto
3822 * @description
3823 *
3824 * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3825 */
3826
3827var ARROW_ARG = /^([^\(]+?)=>/;
3828var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3829var FN_ARG_SPLIT = /,/;
3830var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3831var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3832var $injectorMinErr = minErr('$injector');
3833
3834function extractArgs(fn) {
3835 var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3836 args = fnText.match(ARROW_ARG) || fnText.match(FN_ARGS);
3837 return args;
3838}
3839
3840function anonFn(fn) {
3841 // For anonymous functions, showing at the very least the function signature can help in
3842 // debugging.
3843 var args = extractArgs(fn);
3844 if (args) {
3845 return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3846 }
3847 return 'fn';
3848}
3849
3850function annotate(fn, strictDi, name) {
3851 var $inject,
3852 argDecl,
3853 last;
3854
3855 if (typeof fn === 'function') {
3856 if (!($inject = fn.$inject)) {
3857 $inject = [];
3858 if (fn.length) {
3859 if (strictDi) {
3860 if (!isString(name) || !name) {
3861 name = fn.name || anonFn(fn);
3862 }
3863 throw $injectorMinErr('strictdi',
3864 '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3865 }
3866 argDecl = extractArgs(fn);
3867 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3868 arg.replace(FN_ARG, function(all, underscore, name) {
3869 $inject.push(name);
3870 });
3871 });
3872 }
3873 fn.$inject = $inject;
3874 }
3875 } else if (isArray(fn)) {
3876 last = fn.length - 1;
3877 assertArgFn(fn[last], 'fn');
3878 $inject = fn.slice(0, last);
3879 } else {
3880 assertArgFn(fn, 'fn', true);
3881 }
3882 return $inject;
3883}
3884
3885///////////////////////////////////////
3886
3887/**
3888 * @ngdoc service
3889 * @name $injector
3890 *
3891 * @description
3892 *
3893 * `$injector` is used to retrieve object instances as defined by
3894 * {@link auto.$provide provider}, instantiate types, invoke methods,
3895 * and load modules.
3896 *
3897 * The following always holds true:
3898 *
3899 * ```js
3900 * var $injector = angular.injector();
3901 * expect($injector.get('$injector')).toBe($injector);
3902 * expect($injector.invoke(function($injector) {
3903 * return $injector;
3904 * })).toBe($injector);
3905 * ```
3906 *
3907 * # Injection Function Annotation
3908 *
3909 * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3910 * following are all valid ways of annotating function with injection arguments and are equivalent.
3911 *
3912 * ```js
3913 * // inferred (only works if code not minified/obfuscated)
3914 * $injector.invoke(function(serviceA){});
3915 *
3916 * // annotated
3917 * function explicit(serviceA) {};
3918 * explicit.$inject = ['serviceA'];
3919 * $injector.invoke(explicit);
3920 *
3921 * // inline
3922 * $injector.invoke(['serviceA', function(serviceA){}]);
3923 * ```
3924 *
3925 * ## Inference
3926 *
3927 * In JavaScript calling `toString()` on a function returns the function definition. The definition
3928 * can then be parsed and the function arguments can be extracted. This method of discovering
3929 * annotations is disallowed when the injector is in strict mode.
3930 * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3931 * argument names.
3932 *
3933 * ## `$inject` Annotation
3934 * By adding an `$inject` property onto a function the injection parameters can be specified.
3935 *
3936 * ## Inline
3937 * As an array of injection names, where the last item in the array is the function to call.
3938 */
3939
3940/**
3941 * @ngdoc method
3942 * @name $injector#get
3943 *
3944 * @description
3945 * Return an instance of the service.
3946 *
3947 * @param {string} name The name of the instance to retrieve.
3948 * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3949 * @return {*} The instance.
3950 */
3951
3952/**
3953 * @ngdoc method
3954 * @name $injector#invoke
3955 *
3956 * @description
3957 * Invoke the method and supply the method arguments from the `$injector`.
3958 *
3959 * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3960 * injected according to the {@link guide/di $inject Annotation} rules.
3961 * @param {Object=} self The `this` for the invoked method.
3962 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3963 * object first, before the `$injector` is consulted.
3964 * @returns {*} the value returned by the invoked `fn` function.
3965 */
3966
3967/**
3968 * @ngdoc method
3969 * @name $injector#has
3970 *
3971 * @description
3972 * Allows the user to query if the particular service exists.
3973 *
3974 * @param {string} name Name of the service to query.
3975 * @returns {boolean} `true` if injector has given service.
3976 */
3977
3978/**
3979 * @ngdoc method
3980 * @name $injector#instantiate
3981 * @description
3982 * Create a new instance of JS type. The method takes a constructor function, invokes the new
3983 * operator, and supplies all of the arguments to the constructor function as specified by the
3984 * constructor annotation.
3985 *
3986 * @param {Function} Type Annotated constructor function.
3987 * @param {Object=} locals Optional object. If preset then any argument names are read from this
3988 * object first, before the `$injector` is consulted.
3989 * @returns {Object} new instance of `Type`.
3990 */
3991
3992/**
3993 * @ngdoc method
3994 * @name $injector#annotate
3995 *
3996 * @description
3997 * Returns an array of service names which the function is requesting for injection. This API is
3998 * used by the injector to determine which services need to be injected into the function when the
3999 * function is invoked. There are three ways in which the function can be annotated with the needed
4000 * dependencies.
4001 *
4002 * # Argument names
4003 *
4004 * The simplest form is to extract the dependencies from the arguments of the function. This is done
4005 * by converting the function into a string using `toString()` method and extracting the argument
4006 * names.
4007 * ```js
4008 * // Given
4009 * function MyController($scope, $route) {
4010 * // ...
4011 * }
4012 *
4013 * // Then
4014 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4015 * ```
4016 *
4017 * You can disallow this method by using strict injection mode.
4018 *
4019 * This method does not work with code minification / obfuscation. For this reason the following
4020 * annotation strategies are supported.
4021 *
4022 * # The `$inject` property
4023 *
4024 * If a function has an `$inject` property and its value is an array of strings, then the strings
4025 * represent names of services to be injected into the function.
4026 * ```js
4027 * // Given
4028 * var MyController = function(obfuscatedScope, obfuscatedRoute) {
4029 * // ...
4030 * }
4031 * // Define function dependencies
4032 * MyController['$inject'] = ['$scope', '$route'];
4033 *
4034 * // Then
4035 * expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4036 * ```
4037 *
4038 * # The array notation
4039 *
4040 * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4041 * is very inconvenient. In these situations using the array notation to specify the dependencies in
4042 * a way that survives minification is a better choice:
4043 *
4044 * ```js
4045 * // We wish to write this (not minification / obfuscation safe)
4046 * injector.invoke(function($compile, $rootScope) {
4047 * // ...
4048 * });
4049 *
4050 * // We are forced to write break inlining
4051 * var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4052 * // ...
4053 * };
4054 * tmpFn.$inject = ['$compile', '$rootScope'];
4055 * injector.invoke(tmpFn);
4056 *
4057 * // To better support inline function the inline annotation is supported
4058 * injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4059 * // ...
4060 * }]);
4061 *
4062 * // Therefore
4063 * expect(injector.annotate(
4064 * ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4065 * ).toEqual(['$compile', '$rootScope']);
4066 * ```
4067 *
4068 * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4069 * be retrieved as described above.
4070 *
4071 * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4072 *
4073 * @returns {Array.<string>} The names of the services which the function requires.
4074 */
4075
4076
4077
4078
4079/**
4080 * @ngdoc service
4081 * @name $provide
4082 *
4083 * @description
4084 *
4085 * The {@link auto.$provide $provide} service has a number of methods for registering components
4086 * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4087 * {@link angular.Module}.
4088 *
4089 * An Angular **service** is a singleton object created by a **service factory**. These **service
4090 * factories** are functions which, in turn, are created by a **service provider**.
4091 * The **service providers** are constructor functions. When instantiated they must contain a
4092 * property called `$get`, which holds the **service factory** function.
4093 *
4094 * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4095 * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4096 * function to get the instance of the **service**.
4097 *
4098 * Often services have no configuration options and there is no need to add methods to the service
4099 * provider. The provider will be no more than a constructor function with a `$get` property. For
4100 * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4101 * services without specifying a provider.
4102 *
4103 * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4104 * {@link auto.$injector $injector}
4105 * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4106 * providers and services.
4107 * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4108 * services, not providers.
4109 * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4110 * that will be wrapped in a **service provider** object, whose `$get` property will contain the
4111 * given factory function.
4112 * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4113 * that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4114 * a new object using the given constructor function.
4115 *
4116 * See the individual methods for more information and examples.
4117 */
4118
4119/**
4120 * @ngdoc method
4121 * @name $provide#provider
4122 * @description
4123 *
4124 * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4125 * are constructor functions, whose instances are responsible for "providing" a factory for a
4126 * service.
4127 *
4128 * Service provider names start with the name of the service they provide followed by `Provider`.
4129 * For example, the {@link ng.$log $log} service has a provider called
4130 * {@link ng.$logProvider $logProvider}.
4131 *
4132 * Service provider objects can have additional methods which allow configuration of the provider
4133 * and its service. Importantly, you can configure what kind of service is created by the `$get`
4134 * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4135 * method {@link ng.$logProvider#debugEnabled debugEnabled}
4136 * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4137 * console or not.
4138 *
4139 * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4140 'Provider'` key.
4141 * @param {(Object|function())} provider If the provider is:
4142 *
4143 * - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4144 * {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4145 * - `Constructor`: a new instance of the provider will be created using
4146 * {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4147 *
4148 * @returns {Object} registered provider instance
4149
4150 * @example
4151 *
4152 * The following example shows how to create a simple event tracking service and register it using
4153 * {@link auto.$provide#provider $provide.provider()}.
4154 *
4155 * ```js
4156 * // Define the eventTracker provider
4157 * function EventTrackerProvider() {
4158 * var trackingUrl = '/track';
4159 *
4160 * // A provider method for configuring where the tracked events should been saved
4161 * this.setTrackingUrl = function(url) {
4162 * trackingUrl = url;
4163 * };
4164 *
4165 * // The service factory function
4166 * this.$get = ['$http', function($http) {
4167 * var trackedEvents = {};
4168 * return {
4169 * // Call this to track an event
4170 * event: function(event) {
4171 * var count = trackedEvents[event] || 0;
4172 * count += 1;
4173 * trackedEvents[event] = count;
4174 * return count;
4175 * },
4176 * // Call this to save the tracked events to the trackingUrl
4177 * save: function() {
4178 * $http.post(trackingUrl, trackedEvents);
4179 * }
4180 * };
4181 * }];
4182 * }
4183 *
4184 * describe('eventTracker', function() {
4185 * var postSpy;
4186 *
4187 * beforeEach(module(function($provide) {
4188 * // Register the eventTracker provider
4189 * $provide.provider('eventTracker', EventTrackerProvider);
4190 * }));
4191 *
4192 * beforeEach(module(function(eventTrackerProvider) {
4193 * // Configure eventTracker provider
4194 * eventTrackerProvider.setTrackingUrl('/custom-track');
4195 * }));
4196 *
4197 * it('tracks events', inject(function(eventTracker) {
4198 * expect(eventTracker.event('login')).toEqual(1);
4199 * expect(eventTracker.event('login')).toEqual(2);
4200 * }));
4201 *
4202 * it('saves to the tracking url', inject(function(eventTracker, $http) {
4203 * postSpy = spyOn($http, 'post');
4204 * eventTracker.event('login');
4205 * eventTracker.save();
4206 * expect(postSpy).toHaveBeenCalled();
4207 * expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4208 * expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4209 * expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4210 * }));
4211 * });
4212 * ```
4213 */
4214
4215/**
4216 * @ngdoc method
4217 * @name $provide#factory
4218 * @description
4219 *
4220 * Register a **service factory**, which will be called to return the service instance.
4221 * This is short for registering a service where its provider consists of only a `$get` property,
4222 * which is the given service factory function.
4223 * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4224 * configure your service in a provider.
4225 *
4226 * @param {string} name The name of the instance.
4227 * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4228 * Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4229 * @returns {Object} registered provider instance
4230 *
4231 * @example
4232 * Here is an example of registering a service
4233 * ```js
4234 * $provide.factory('ping', ['$http', function($http) {
4235 * return function ping() {
4236 * return $http.send('/ping');
4237 * };
4238 * }]);
4239 * ```
4240 * You would then inject and use this service like this:
4241 * ```js
4242 * someModule.controller('Ctrl', ['ping', function(ping) {
4243 * ping();
4244 * }]);
4245 * ```
4246 */
4247
4248
4249/**
4250 * @ngdoc method
4251 * @name $provide#service
4252 * @description
4253 *
4254 * Register a **service constructor**, which will be invoked with `new` to create the service
4255 * instance.
4256 * This is short for registering a service where its provider's `$get` property is a factory
4257 * function that returns an instance instantiated by the injector from the service constructor
4258 * function.
4259 *
4260 * Internally it looks a bit like this:
4261 *
4262 * ```
4263 * {
4264 * $get: function() {
4265 * return $injector.instantiate(constructor);
4266 * }
4267 * }
4268 * ```
4269 *
4270 *
4271 * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4272 * as a type/class.
4273 *
4274 * @param {string} name The name of the instance.
4275 * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4276 * that will be instantiated.
4277 * @returns {Object} registered provider instance
4278 *
4279 * @example
4280 * Here is an example of registering a service using
4281 * {@link auto.$provide#service $provide.service(class)}.
4282 * ```js
4283 * var Ping = function($http) {
4284 * this.$http = $http;
4285 * };
4286 *
4287 * Ping.$inject = ['$http'];
4288 *
4289 * Ping.prototype.send = function() {
4290 * return this.$http.get('/ping');
4291 * };
4292 * $provide.service('ping', Ping);
4293 * ```
4294 * You would then inject and use this service like this:
4295 * ```js
4296 * someModule.controller('Ctrl', ['ping', function(ping) {
4297 * ping.send();
4298 * }]);
4299 * ```
4300 */
4301
4302
4303/**
4304 * @ngdoc method
4305 * @name $provide#value
4306 * @description
4307 *
4308 * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4309 * number, an array, an object or a function. This is short for registering a service where its
4310 * provider's `$get` property is a factory function that takes no arguments and returns the **value
4311 * service**. That also means it is not possible to inject other services into a value service.
4312 *
4313 * Value services are similar to constant services, except that they cannot be injected into a
4314 * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4315 * an Angular {@link auto.$provide#decorator decorator}.
4316 *
4317 * @param {string} name The name of the instance.
4318 * @param {*} value The value.
4319 * @returns {Object} registered provider instance
4320 *
4321 * @example
4322 * Here are some examples of creating value services.
4323 * ```js
4324 * $provide.value('ADMIN_USER', 'admin');
4325 *
4326 * $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4327 *
4328 * $provide.value('halfOf', function(value) {
4329 * return value / 2;
4330 * });
4331 * ```
4332 */
4333
4334
4335/**
4336 * @ngdoc method
4337 * @name $provide#constant
4338 * @description
4339 *
4340 * Register a **constant service** with the {@link auto.$injector $injector}, such as a string,
4341 * a number, an array, an object or a function. Like the {@link auto.$provide#value value}, it is not
4342 * possible to inject other services into a constant.
4343 *
4344 * But unlike {@link auto.$provide#value value}, a constant can be
4345 * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4346 * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4347 *
4348 * @param {string} name The name of the constant.
4349 * @param {*} value The constant value.
4350 * @returns {Object} registered instance
4351 *
4352 * @example
4353 * Here a some examples of creating constants:
4354 * ```js
4355 * $provide.constant('SHARD_HEIGHT', 306);
4356 *
4357 * $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4358 *
4359 * $provide.constant('double', function(value) {
4360 * return value * 2;
4361 * });
4362 * ```
4363 */
4364
4365
4366/**
4367 * @ngdoc method
4368 * @name $provide#decorator
4369 * @description
4370 *
4371 * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4372 * intercepts the creation of a service, allowing it to override or modify the behavior of the
4373 * service. The object returned by the decorator may be the original service, or a new service
4374 * object which replaces or wraps and delegates to the original service.
4375 *
4376 * @param {string} name The name of the service to decorate.
4377 * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4378 * instantiated and should return the decorated service instance. The function is called using
4379 * the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4380 * Local injection arguments:
4381 *
4382 * * `$delegate` - The original service instance, which can be monkey patched, configured,
4383 * decorated or delegated to.
4384 *
4385 * @example
4386 * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4387 * calls to {@link ng.$log#error $log.warn()}.
4388 * ```js
4389 * $provide.decorator('$log', ['$delegate', function($delegate) {
4390 * $delegate.warn = $delegate.error;
4391 * return $delegate;
4392 * }]);
4393 * ```
4394 */
4395
4396
4397function createInjector(modulesToLoad, strictDi) {
4398 strictDi = (strictDi === true);
4399 var INSTANTIATING = {},
4400 providerSuffix = 'Provider',
4401 path = [],
4402 loadedModules = new HashMap([], true),
4403 providerCache = {
4404 $provide: {
4405 provider: supportObject(provider),
4406 factory: supportObject(factory),
4407 service: supportObject(service),
4408 value: supportObject(value),
4409 constant: supportObject(constant),
4410 decorator: decorator
4411 }
4412 },
4413 providerInjector = (providerCache.$injector =
4414 createInternalInjector(providerCache, function(serviceName, caller) {
4415 if (angular.isString(caller)) {
4416 path.push(caller);
4417 }
4418 throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4419 })),
4420 instanceCache = {},
4421 protoInstanceInjector =
4422 createInternalInjector(instanceCache, function(serviceName, caller) {
4423 var provider = providerInjector.get(serviceName + providerSuffix, caller);
4424 return instanceInjector.invoke(
4425 provider.$get, provider, undefined, serviceName);
4426 }),
4427 instanceInjector = protoInstanceInjector;
4428
4429 providerCache['$injector' + providerSuffix] = { $get: valueFn(protoInstanceInjector) };
4430 var runBlocks = loadModules(modulesToLoad);
4431 instanceInjector = protoInstanceInjector.get('$injector');
4432 instanceInjector.strictDi = strictDi;
4433 forEach(runBlocks, function(fn) { if (fn) instanceInjector.invoke(fn); });
4434
4435 return instanceInjector;
4436
4437 ////////////////////////////////////
4438 // $provider
4439 ////////////////////////////////////
4440
4441 function supportObject(delegate) {
4442 return function(key, value) {
4443 if (isObject(key)) {
4444 forEach(key, reverseParams(delegate));
4445 } else {
4446 return delegate(key, value);
4447 }
4448 };
4449 }
4450
4451 function provider(name, provider_) {
4452 assertNotHasOwnProperty(name, 'service');
4453 if (isFunction(provider_) || isArray(provider_)) {
4454 provider_ = providerInjector.instantiate(provider_);
4455 }
4456 if (!provider_.$get) {
4457 throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4458 }
4459 return providerCache[name + providerSuffix] = provider_;
4460 }
4461
4462 function enforceReturnValue(name, factory) {
4463 return function enforcedReturnValue() {
4464 var result = instanceInjector.invoke(factory, this);
4465 if (isUndefined(result)) {
4466 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4467 }
4468 return result;
4469 };
4470 }
4471
4472 function factory(name, factoryFn, enforce) {
4473 return provider(name, {
4474 $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4475 });
4476 }
4477
4478 function service(name, constructor) {
4479 return factory(name, ['$injector', function($injector) {
4480 return $injector.instantiate(constructor);
4481 }]);
4482 }
4483
4484 function value(name, val) { return factory(name, valueFn(val), false); }
4485
4486 function constant(name, value) {
4487 assertNotHasOwnProperty(name, 'constant');
4488 providerCache[name] = value;
4489 instanceCache[name] = value;
4490 }
4491
4492 function decorator(serviceName, decorFn) {
4493 var origProvider = providerInjector.get(serviceName + providerSuffix),
4494 orig$get = origProvider.$get;
4495
4496 origProvider.$get = function() {
4497 var origInstance = instanceInjector.invoke(orig$get, origProvider);
4498 return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4499 };
4500 }
4501
4502 ////////////////////////////////////
4503 // Module Loading
4504 ////////////////////////////////////
4505 function loadModules(modulesToLoad) {
4506 assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4507 var runBlocks = [], moduleFn;
4508 forEach(modulesToLoad, function(module) {
4509 if (loadedModules.get(module)) return;
4510 loadedModules.put(module, true);
4511
4512 function runInvokeQueue(queue) {
4513 var i, ii;
4514 for (i = 0, ii = queue.length; i < ii; i++) {
4515 var invokeArgs = queue[i],
4516 provider = providerInjector.get(invokeArgs[0]);
4517
4518 provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4519 }
4520 }
4521
4522 try {
4523 if (isString(module)) {
4524 moduleFn = angularModule(module);
4525 runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4526 runInvokeQueue(moduleFn._invokeQueue);
4527 runInvokeQueue(moduleFn._configBlocks);
4528 } else if (isFunction(module)) {
4529 runBlocks.push(providerInjector.invoke(module));
4530 } else if (isArray(module)) {
4531 runBlocks.push(providerInjector.invoke(module));
4532 } else {
4533 assertArgFn(module, 'module');
4534 }
4535 } catch (e) {
4536 if (isArray(module)) {
4537 module = module[module.length - 1];
4538 }
4539 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4540 // Safari & FF's stack traces don't contain error.message content
4541 // unlike those of Chrome and IE
4542 // So if stack doesn't contain message, we create a new string that contains both.
4543 // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4544 /* jshint -W022 */
4545 e = e.message + '\n' + e.stack;
4546 }
4547 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4548 module, e.stack || e.message || e);
4549 }
4550 });
4551 return runBlocks;
4552 }
4553
4554 ////////////////////////////////////
4555 // internal Injector
4556 ////////////////////////////////////
4557
4558 function createInternalInjector(cache, factory) {
4559
4560 function getService(serviceName, caller) {
4561 if (cache.hasOwnProperty(serviceName)) {
4562 if (cache[serviceName] === INSTANTIATING) {
4563 throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4564 serviceName + ' <- ' + path.join(' <- '));
4565 }
4566 return cache[serviceName];
4567 } else {
4568 try {
4569 path.unshift(serviceName);
4570 cache[serviceName] = INSTANTIATING;
4571 return cache[serviceName] = factory(serviceName, caller);
4572 } catch (err) {
4573 if (cache[serviceName] === INSTANTIATING) {
4574 delete cache[serviceName];
4575 }
4576 throw err;
4577 } finally {
4578 path.shift();
4579 }
4580 }
4581 }
4582
4583
4584 function injectionArgs(fn, locals, serviceName) {
4585 var args = [],
4586 $inject = createInjector.$$annotate(fn, strictDi, serviceName);
4587
4588 for (var i = 0, length = $inject.length; i < length; i++) {
4589 var key = $inject[i];
4590 if (typeof key !== 'string') {
4591 throw $injectorMinErr('itkn',
4592 'Incorrect injection token! Expected service name as string, got {0}', key);
4593 }
4594 args.push(locals && locals.hasOwnProperty(key) ? locals[key] :
4595 getService(key, serviceName));
4596 }
4597 return args;
4598 }
4599
4600 function isClass(func) {
4601 // IE 9-11 do not support classes and IE9 leaks with the code below.
4602 if (msie <= 11) {
4603 return false;
4604 }
4605 // Workaround for MS Edge.
4606 // Check https://connect.microsoft.com/IE/Feedback/Details/2211653
4607 return typeof func === 'function'
4608 && /^(?:class\s|constructor\()/.test(Function.prototype.toString.call(func));
4609 }
4610
4611 function invoke(fn, self, locals, serviceName) {
4612 if (typeof locals === 'string') {
4613 serviceName = locals;
4614 locals = null;
4615 }
4616
4617 var args = injectionArgs(fn, locals, serviceName);
4618 if (isArray(fn)) {
4619 fn = fn[fn.length - 1];
4620 }
4621
4622 if (!isClass(fn)) {
4623 // http://jsperf.com/angularjs-invoke-apply-vs-switch
4624 // #5388
4625 return fn.apply(self, args);
4626 } else {
4627 args.unshift(null);
4628 return new (Function.prototype.bind.apply(fn, args))();
4629 }
4630 }
4631
4632
4633 function instantiate(Type, locals, serviceName) {
4634 // Check if Type is annotated and use just the given function at n-1 as parameter
4635 // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4636 var ctor = (isArray(Type) ? Type[Type.length - 1] : Type);
4637 var args = injectionArgs(Type, locals, serviceName);
4638 // Empty object at position 0 is ignored for invocation with `new`, but required.
4639 args.unshift(null);
4640 return new (Function.prototype.bind.apply(ctor, args))();
4641 }
4642
4643
4644 return {
4645 invoke: invoke,
4646 instantiate: instantiate,
4647 get: getService,
4648 annotate: createInjector.$$annotate,
4649 has: function(name) {
4650 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4651 }
4652 };
4653 }
4654}
4655
4656createInjector.$$annotate = annotate;
4657
4658/**
4659 * @ngdoc provider
4660 * @name $anchorScrollProvider
4661 *
4662 * @description
4663 * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4664 * {@link ng.$location#hash $location.hash()} changes.
4665 */
4666function $AnchorScrollProvider() {
4667
4668 var autoScrollingEnabled = true;
4669
4670 /**
4671 * @ngdoc method
4672 * @name $anchorScrollProvider#disableAutoScrolling
4673 *
4674 * @description
4675 * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4676 * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4677 * Use this method to disable automatic scrolling.
4678 *
4679 * If automatic scrolling is disabled, one must explicitly call
4680 * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4681 * current hash.
4682 */
4683 this.disableAutoScrolling = function() {
4684 autoScrollingEnabled = false;
4685 };
4686
4687 /**
4688 * @ngdoc service
4689 * @name $anchorScroll
4690 * @kind function
4691 * @requires $window
4692 * @requires $location
4693 * @requires $rootScope
4694 *
4695 * @description
4696 * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4697 * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4698 * in the
4699 * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4700 *
4701 * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4702 * match any anchor whenever it changes. This can be disabled by calling
4703 * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4704 *
4705 * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4706 * vertical scroll-offset (either fixed or dynamic).
4707 *
4708 * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4709 * {@link ng.$location#hash $location.hash()} will be used.
4710 *
4711 * @property {(number|function|jqLite)} yOffset
4712 * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4713 * positioned elements at the top of the page, such as navbars, headers etc.
4714 *
4715 * `yOffset` can be specified in various ways:
4716 * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4717 * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4718 * a number representing the offset (in pixels).<br /><br />
4719 * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4720 * the top of the page to the element's bottom will be used as offset.<br />
4721 * **Note**: The element will be taken into account only as long as its `position` is set to
4722 * `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4723 * their height and/or positioning according to the viewport's size.
4724 *
4725 * <br />
4726 * <div class="alert alert-warning">
4727 * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4728 * not some child element.
4729 * </div>
4730 *
4731 * @example
4732 <example module="anchorScrollExample">
4733 <file name="index.html">
4734 <div id="scrollArea" ng-controller="ScrollController">
4735 <a ng-click="gotoBottom()">Go to bottom</a>
4736 <a id="bottom"></a> You're at the bottom!
4737 </div>
4738 </file>
4739 <file name="script.js">
4740 angular.module('anchorScrollExample', [])
4741 .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4742 function ($scope, $location, $anchorScroll) {
4743 $scope.gotoBottom = function() {
4744 // set the location.hash to the id of
4745 // the element you wish to scroll to.
4746 $location.hash('bottom');
4747
4748 // call $anchorScroll()
4749 $anchorScroll();
4750 };
4751 }]);
4752 </file>
4753 <file name="style.css">
4754 #scrollArea {
4755 height: 280px;
4756 overflow: auto;
4757 }
4758
4759 #bottom {
4760 display: block;
4761 margin-top: 2000px;
4762 }
4763 </file>
4764 </example>
4765 *
4766 * <hr />
4767 * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4768 * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4769 *
4770 * @example
4771 <example module="anchorScrollOffsetExample">
4772 <file name="index.html">
4773 <div class="fixed-header" ng-controller="headerCtrl">
4774 <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4775 Go to anchor {{x}}
4776 </a>
4777 </div>
4778 <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4779 Anchor {{x}} of 5
4780 </div>
4781 </file>
4782 <file name="script.js">
4783 angular.module('anchorScrollOffsetExample', [])
4784 .run(['$anchorScroll', function($anchorScroll) {
4785 $anchorScroll.yOffset = 50; // always scroll by 50 extra pixels
4786 }])
4787 .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4788 function ($anchorScroll, $location, $scope) {
4789 $scope.gotoAnchor = function(x) {
4790 var newHash = 'anchor' + x;
4791 if ($location.hash() !== newHash) {
4792 // set the $location.hash to `newHash` and
4793 // $anchorScroll will automatically scroll to it
4794 $location.hash('anchor' + x);
4795 } else {
4796 // call $anchorScroll() explicitly,
4797 // since $location.hash hasn't changed
4798 $anchorScroll();
4799 }
4800 };
4801 }
4802 ]);
4803 </file>
4804 <file name="style.css">
4805 body {
4806 padding-top: 50px;
4807 }
4808
4809 .anchor {
4810 border: 2px dashed DarkOrchid;
4811 padding: 10px 10px 200px 10px;
4812 }
4813
4814 .fixed-header {
4815 background-color: rgba(0, 0, 0, 0.2);
4816 height: 50px;
4817 position: fixed;
4818 top: 0; left: 0; right: 0;
4819 }
4820
4821 .fixed-header > a {
4822 display: inline-block;
4823 margin: 5px 15px;
4824 }
4825 </file>
4826 </example>
4827 */
4828 this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4829 var document = $window.document;
4830
4831 // Helper function to get first anchor from a NodeList
4832 // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4833 // and working in all supported browsers.)
4834 function getFirstAnchor(list) {
4835 var result = null;
4836 Array.prototype.some.call(list, function(element) {
4837 if (nodeName_(element) === 'a') {
4838 result = element;
4839 return true;
4840 }
4841 });
4842 return result;
4843 }
4844
4845 function getYOffset() {
4846
4847 var offset = scroll.yOffset;
4848
4849 if (isFunction(offset)) {
4850 offset = offset();
4851 } else if (isElement(offset)) {
4852 var elem = offset[0];
4853 var style = $window.getComputedStyle(elem);
4854 if (style.position !== 'fixed') {
4855 offset = 0;
4856 } else {
4857 offset = elem.getBoundingClientRect().bottom;
4858 }
4859 } else if (!isNumber(offset)) {
4860 offset = 0;
4861 }
4862
4863 return offset;
4864 }
4865
4866 function scrollTo(elem) {
4867 if (elem) {
4868 elem.scrollIntoView();
4869
4870 var offset = getYOffset();
4871
4872 if (offset) {
4873 // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4874 // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4875 // top of the viewport.
4876 //
4877 // IF the number of pixels from the top of `elem` to the end of the page's content is less
4878 // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4879 // way down the page.
4880 //
4881 // This is often the case for elements near the bottom of the page.
4882 //
4883 // In such cases we do not need to scroll the whole `offset` up, just the difference between
4884 // the top of the element and the offset, which is enough to align the top of `elem` at the
4885 // desired position.
4886 var elemTop = elem.getBoundingClientRect().top;
4887 $window.scrollBy(0, elemTop - offset);
4888 }
4889 } else {
4890 $window.scrollTo(0, 0);
4891 }
4892 }
4893
4894 function scroll(hash) {
4895 hash = isString(hash) ? hash : $location.hash();
4896 var elm;
4897
4898 // empty hash, scroll to the top of the page
4899 if (!hash) scrollTo(null);
4900
4901 // element with given id
4902 else if ((elm = document.getElementById(hash))) scrollTo(elm);
4903
4904 // first anchor with given name :-D
4905 else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4906
4907 // no element and hash == 'top', scroll to the top of the page
4908 else if (hash === 'top') scrollTo(null);
4909 }
4910
4911 // does not scroll when user clicks on anchor link that is currently on
4912 // (no url change, no $location.hash() change), browser native does scroll
4913 if (autoScrollingEnabled) {
4914 $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4915 function autoScrollWatchAction(newVal, oldVal) {
4916 // skip the initial scroll if $location.hash is empty
4917 if (newVal === oldVal && newVal === '') return;
4918
4919 jqLiteDocumentLoaded(function() {
4920 $rootScope.$evalAsync(scroll);
4921 });
4922 });
4923 }
4924
4925 return scroll;
4926 }];
4927}
4928
4929var $animateMinErr = minErr('$animate');
4930var ELEMENT_NODE = 1;
4931var NG_ANIMATE_CLASSNAME = 'ng-animate';
4932
4933function mergeClasses(a,b) {
4934 if (!a && !b) return '';
4935 if (!a) return b;
4936 if (!b) return a;
4937 if (isArray(a)) a = a.join(' ');
4938 if (isArray(b)) b = b.join(' ');
4939 return a + ' ' + b;
4940}
4941
4942function extractElementNode(element) {
4943 for (var i = 0; i < element.length; i++) {
4944 var elm = element[i];
4945 if (elm.nodeType === ELEMENT_NODE) {
4946 return elm;
4947 }
4948 }
4949}
4950
4951function splitClasses(classes) {
4952 if (isString(classes)) {
4953 classes = classes.split(' ');
4954 }
4955
4956 // Use createMap() to prevent class assumptions involving property names in
4957 // Object.prototype
4958 var obj = createMap();
4959 forEach(classes, function(klass) {
4960 // sometimes the split leaves empty string values
4961 // incase extra spaces were applied to the options
4962 if (klass.length) {
4963 obj[klass] = true;
4964 }
4965 });
4966 return obj;
4967}
4968
4969// if any other type of options value besides an Object value is
4970// passed into the $animate.method() animation then this helper code
4971// will be run which will ignore it. While this patch is not the
4972// greatest solution to this, a lot of existing plugins depend on
4973// $animate to either call the callback (< 1.2) or return a promise
4974// that can be changed. This helper function ensures that the options
4975// are wiped clean incase a callback function is provided.
4976function prepareAnimateOptions(options) {
4977 return isObject(options)
4978 ? options
4979 : {};
4980}
4981
4982var $$CoreAnimateJsProvider = function() {
4983 this.$get = noop;
4984};
4985
4986// this is prefixed with Core since it conflicts with
4987// the animateQueueProvider defined in ngAnimate/animateQueue.js
4988var $$CoreAnimateQueueProvider = function() {
4989 var postDigestQueue = new HashMap();
4990 var postDigestElements = [];
4991
4992 this.$get = ['$$AnimateRunner', '$rootScope',
4993 function($$AnimateRunner, $rootScope) {
4994 return {
4995 enabled: noop,
4996 on: noop,
4997 off: noop,
4998 pin: noop,
4999
5000 push: function(element, event, options, domOperation) {
5001 domOperation && domOperation();
5002
5003 options = options || {};
5004 options.from && element.css(options.from);
5005 options.to && element.css(options.to);
5006
5007 if (options.addClass || options.removeClass) {
5008 addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
5009 }
5010
5011 var runner = new $$AnimateRunner(); // jshint ignore:line
5012
5013 // since there are no animations to run the runner needs to be
5014 // notified that the animation call is complete.
5015 runner.complete();
5016 return runner;
5017 }
5018 };
5019
5020
5021 function updateData(data, classes, value) {
5022 var changed = false;
5023 if (classes) {
5024 classes = isString(classes) ? classes.split(' ') :
5025 isArray(classes) ? classes : [];
5026 forEach(classes, function(className) {
5027 if (className) {
5028 changed = true;
5029 data[className] = value;
5030 }
5031 });
5032 }
5033 return changed;
5034 }
5035
5036 function handleCSSClassChanges() {
5037 forEach(postDigestElements, function(element) {
5038 var data = postDigestQueue.get(element);
5039 if (data) {
5040 var existing = splitClasses(element.attr('class'));
5041 var toAdd = '';
5042 var toRemove = '';
5043 forEach(data, function(status, className) {
5044 var hasClass = !!existing[className];
5045 if (status !== hasClass) {
5046 if (status) {
5047 toAdd += (toAdd.length ? ' ' : '') + className;
5048 } else {
5049 toRemove += (toRemove.length ? ' ' : '') + className;
5050 }
5051 }
5052 });
5053
5054 forEach(element, function(elm) {
5055 toAdd && jqLiteAddClass(elm, toAdd);
5056 toRemove && jqLiteRemoveClass(elm, toRemove);
5057 });
5058 postDigestQueue.remove(element);
5059 }
5060 });
5061 postDigestElements.length = 0;
5062 }
5063
5064
5065 function addRemoveClassesPostDigest(element, add, remove) {
5066 var data = postDigestQueue.get(element) || {};
5067
5068 var classesAdded = updateData(data, add, true);
5069 var classesRemoved = updateData(data, remove, false);
5070
5071 if (classesAdded || classesRemoved) {
5072
5073 postDigestQueue.put(element, data);
5074 postDigestElements.push(element);
5075
5076 if (postDigestElements.length === 1) {
5077 $rootScope.$$postDigest(handleCSSClassChanges);
5078 }
5079 }
5080 }
5081 }];
5082};
5083
5084/**
5085 * @ngdoc provider
5086 * @name $animateProvider
5087 *
5088 * @description
5089 * Default implementation of $animate that doesn't perform any animations, instead just
5090 * synchronously performs DOM updates and resolves the returned runner promise.
5091 *
5092 * In order to enable animations the `ngAnimate` module has to be loaded.
5093 *
5094 * To see the functional implementation check out `src/ngAnimate/animate.js`.
5095 */
5096var $AnimateProvider = ['$provide', function($provide) {
5097 var provider = this;
5098
5099 this.$$registeredAnimations = Object.create(null);
5100
5101 /**
5102 * @ngdoc method
5103 * @name $animateProvider#register
5104 *
5105 * @description
5106 * Registers a new injectable animation factory function. The factory function produces the
5107 * animation object which contains callback functions for each event that is expected to be
5108 * animated.
5109 *
5110 * * `eventFn`: `function(element, ... , doneFunction, options)`
5111 * The element to animate, the `doneFunction` and the options fed into the animation. Depending
5112 * on the type of animation additional arguments will be injected into the animation function. The
5113 * list below explains the function signatures for the different animation methods:
5114 *
5115 * - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5116 * - addClass: function(element, addedClasses, doneFunction, options)
5117 * - removeClass: function(element, removedClasses, doneFunction, options)
5118 * - enter, leave, move: function(element, doneFunction, options)
5119 * - animate: function(element, fromStyles, toStyles, doneFunction, options)
5120 *
5121 * Make sure to trigger the `doneFunction` once the animation is fully complete.
5122 *
5123 * ```js
5124 * return {
5125 * //enter, leave, move signature
5126 * eventFn : function(element, done, options) {
5127 * //code to run the animation
5128 * //once complete, then run done()
5129 * return function endFunction(wasCancelled) {
5130 * //code to cancel the animation
5131 * }
5132 * }
5133 * }
5134 * ```
5135 *
5136 * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5137 * @param {Function} factory The factory function that will be executed to return the animation
5138 * object.
5139 */
5140 this.register = function(name, factory) {
5141 if (name && name.charAt(0) !== '.') {
5142 throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5143 }
5144
5145 var key = name + '-animation';
5146 provider.$$registeredAnimations[name.substr(1)] = key;
5147 $provide.factory(key, factory);
5148 };
5149
5150 /**
5151 * @ngdoc method
5152 * @name $animateProvider#classNameFilter
5153 *
5154 * @description
5155 * Sets and/or returns the CSS class regular expression that is checked when performing
5156 * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5157 * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5158 * When setting the `classNameFilter` value, animations will only be performed on elements
5159 * that successfully match the filter expression. This in turn can boost performance
5160 * for low-powered devices as well as applications containing a lot of structural operations.
5161 * @param {RegExp=} expression The className expression which will be checked against all animations
5162 * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5163 */
5164 this.classNameFilter = function(expression) {
5165 if (arguments.length === 1) {
5166 this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5167 if (this.$$classNameFilter) {
5168 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5169 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5170 throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5171
5172 }
5173 }
5174 }
5175 return this.$$classNameFilter;
5176 };
5177
5178 this.$get = ['$$animateQueue', function($$animateQueue) {
5179 function domInsert(element, parentElement, afterElement) {
5180 // if for some reason the previous element was removed
5181 // from the dom sometime before this code runs then let's
5182 // just stick to using the parent element as the anchor
5183 if (afterElement) {
5184 var afterNode = extractElementNode(afterElement);
5185 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5186 afterElement = null;
5187 }
5188 }
5189 afterElement ? afterElement.after(element) : parentElement.prepend(element);
5190 }
5191
5192 /**
5193 * @ngdoc service
5194 * @name $animate
5195 * @description The $animate service exposes a series of DOM utility methods that provide support
5196 * for animation hooks. The default behavior is the application of DOM operations, however,
5197 * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5198 * to ensure that animation runs with the triggered DOM operation.
5199 *
5200 * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5201 * included and only when it is active then the animation hooks that `$animate` triggers will be
5202 * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5203 * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5204 * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5205 *
5206 * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5207 *
5208 * To learn more about enabling animation support, click here to visit the
5209 * {@link ngAnimate ngAnimate module page}.
5210 */
5211 return {
5212 // we don't call it directly since non-existant arguments may
5213 // be interpreted as null within the sub enabled function
5214
5215 /**
5216 *
5217 * @ngdoc method
5218 * @name $animate#on
5219 * @kind function
5220 * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5221 * has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5222 * is fired with the following params:
5223 *
5224 * ```js
5225 * $animate.on('enter', container,
5226 * function callback(element, phase) {
5227 * // cool we detected an enter animation within the container
5228 * }
5229 * );
5230 * ```
5231 *
5232 * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5233 * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5234 * as well as among its children
5235 * @param {Function} callback the callback function that will be fired when the listener is triggered
5236 *
5237 * The arguments present in the callback function are:
5238 * * `element` - The captured DOM element that the animation was fired on.
5239 * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5240 */
5241 on: $$animateQueue.on,
5242
5243 /**
5244 *
5245 * @ngdoc method
5246 * @name $animate#off
5247 * @kind function
5248 * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5249 * can be used in three different ways depending on the arguments:
5250 *
5251 * ```js
5252 * // remove all the animation event listeners listening for `enter`
5253 * $animate.off('enter');
5254 *
5255 * // remove all the animation event listeners listening for `enter` on the given element and its children
5256 * $animate.off('enter', container);
5257 *
5258 * // remove the event listener function provided by `callback` that is set
5259 * // to listen for `enter` on the given `container` as well as its children
5260 * $animate.off('enter', container, callback);
5261 * ```
5262 *
5263 * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5264 * @param {DOMElement=} container the container element the event listener was placed on
5265 * @param {Function=} callback the callback function that was registered as the listener
5266 */
5267 off: $$animateQueue.off,
5268
5269 /**
5270 * @ngdoc method
5271 * @name $animate#pin
5272 * @kind function
5273 * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5274 * outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5275 * element despite being outside the realm of the application or within another application. Say for example if the application
5276 * was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5277 * as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5278 * that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5279 *
5280 * Note that this feature is only active when the `ngAnimate` module is used.
5281 *
5282 * @param {DOMElement} element the external element that will be pinned
5283 * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5284 */
5285 pin: $$animateQueue.pin,
5286
5287 /**
5288 *
5289 * @ngdoc method
5290 * @name $animate#enabled
5291 * @kind function
5292 * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5293 * function can be called in four ways:
5294 *
5295 * ```js
5296 * // returns true or false
5297 * $animate.enabled();
5298 *
5299 * // changes the enabled state for all animations
5300 * $animate.enabled(false);
5301 * $animate.enabled(true);
5302 *
5303 * // returns true or false if animations are enabled for an element
5304 * $animate.enabled(element);
5305 *
5306 * // changes the enabled state for an element and its children
5307 * $animate.enabled(element, true);
5308 * $animate.enabled(element, false);
5309 * ```
5310 *
5311 * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5312 * @param {boolean=} enabled whether or not the animations will be enabled for the element
5313 *
5314 * @return {boolean} whether or not animations are enabled
5315 */
5316 enabled: $$animateQueue.enabled,
5317
5318 /**
5319 * @ngdoc method
5320 * @name $animate#cancel
5321 * @kind function
5322 * @description Cancels the provided animation.
5323 *
5324 * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5325 */
5326 cancel: function(runner) {
5327 runner.end && runner.end();
5328 },
5329
5330 /**
5331 *
5332 * @ngdoc method
5333 * @name $animate#enter
5334 * @kind function
5335 * @description Inserts the element into the DOM either after the `after` element (if provided) or
5336 * as the first child within the `parent` element and then triggers an animation.
5337 * A promise is returned that will be resolved during the next digest once the animation
5338 * has completed.
5339 *
5340 * @param {DOMElement} element the element which will be inserted into the DOM
5341 * @param {DOMElement} parent the parent element which will append the element as
5342 * a child (so long as the after element is not present)
5343 * @param {DOMElement=} after the sibling element after which the element will be appended
5344 * @param {object=} options an optional collection of options/styles that will be applied to the element
5345 *
5346 * @return {Promise} the animation callback promise
5347 */
5348 enter: function(element, parent, after, options) {
5349 parent = parent && jqLite(parent);
5350 after = after && jqLite(after);
5351 parent = parent || after.parent();
5352 domInsert(element, parent, after);
5353 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5354 },
5355
5356 /**
5357 *
5358 * @ngdoc method
5359 * @name $animate#move
5360 * @kind function
5361 * @description Inserts (moves) the element into its new position in the DOM either after
5362 * the `after` element (if provided) or as the first child within the `parent` element
5363 * and then triggers an animation. A promise is returned that will be resolved
5364 * during the next digest once the animation has completed.
5365 *
5366 * @param {DOMElement} element the element which will be moved into the new DOM position
5367 * @param {DOMElement} parent the parent element which will append the element as
5368 * a child (so long as the after element is not present)
5369 * @param {DOMElement=} after the sibling element after which the element will be appended
5370 * @param {object=} options an optional collection of options/styles that will be applied to the element
5371 *
5372 * @return {Promise} the animation callback promise
5373 */
5374 move: function(element, parent, after, options) {
5375 parent = parent && jqLite(parent);
5376 after = after && jqLite(after);
5377 parent = parent || after.parent();
5378 domInsert(element, parent, after);
5379 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5380 },
5381
5382 /**
5383 * @ngdoc method
5384 * @name $animate#leave
5385 * @kind function
5386 * @description Triggers an animation and then removes the element from the DOM.
5387 * When the function is called a promise is returned that will be resolved during the next
5388 * digest once the animation has completed.
5389 *
5390 * @param {DOMElement} element the element which will be removed from the DOM
5391 * @param {object=} options an optional collection of options/styles that will be applied to the element
5392 *
5393 * @return {Promise} the animation callback promise
5394 */
5395 leave: function(element, options) {
5396 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5397 element.remove();
5398 });
5399 },
5400
5401 /**
5402 * @ngdoc method
5403 * @name $animate#addClass
5404 * @kind function
5405 *
5406 * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5407 * execution, the addClass operation will only be handled after the next digest and it will not trigger an
5408 * animation if element already contains the CSS class or if the class is removed at a later step.
5409 * Note that class-based animations are treated differently compared to structural animations
5410 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5411 * depending if CSS or JavaScript animations are used.
5412 *
5413 * @param {DOMElement} element the element which the CSS classes will be applied to
5414 * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5415 * @param {object=} options an optional collection of options/styles that will be applied to the element
5416 *
5417 * @return {Promise} the animation callback promise
5418 */
5419 addClass: function(element, className, options) {
5420 options = prepareAnimateOptions(options);
5421 options.addClass = mergeClasses(options.addclass, className);
5422 return $$animateQueue.push(element, 'addClass', options);
5423 },
5424
5425 /**
5426 * @ngdoc method
5427 * @name $animate#removeClass
5428 * @kind function
5429 *
5430 * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5431 * execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5432 * animation if element does not contain the CSS class or if the class is added at a later step.
5433 * Note that class-based animations are treated differently compared to structural animations
5434 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5435 * depending if CSS or JavaScript animations are used.
5436 *
5437 * @param {DOMElement} element the element which the CSS classes will be applied to
5438 * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5439 * @param {object=} options an optional collection of options/styles that will be applied to the element
5440 *
5441 * @return {Promise} the animation callback promise
5442 */
5443 removeClass: function(element, className, options) {
5444 options = prepareAnimateOptions(options);
5445 options.removeClass = mergeClasses(options.removeClass, className);
5446 return $$animateQueue.push(element, 'removeClass', options);
5447 },
5448
5449 /**
5450 * @ngdoc method
5451 * @name $animate#setClass
5452 * @kind function
5453 *
5454 * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5455 * triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5456 * `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5457 * passed. Note that class-based animations are treated differently compared to structural animations
5458 * (like enter, move and leave) since the CSS classes may be added/removed at different points
5459 * depending if CSS or JavaScript animations are used.
5460 *
5461 * @param {DOMElement} element the element which the CSS classes will be applied to
5462 * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5463 * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5464 * @param {object=} options an optional collection of options/styles that will be applied to the element
5465 *
5466 * @return {Promise} the animation callback promise
5467 */
5468 setClass: function(element, add, remove, options) {
5469 options = prepareAnimateOptions(options);
5470 options.addClass = mergeClasses(options.addClass, add);
5471 options.removeClass = mergeClasses(options.removeClass, remove);
5472 return $$animateQueue.push(element, 'setClass', options);
5473 },
5474
5475 /**
5476 * @ngdoc method
5477 * @name $animate#animate
5478 * @kind function
5479 *
5480 * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5481 * If any detected CSS transition, keyframe or JavaScript matches the provided className value, then the animation will take
5482 * on the provided styles. For example, if a transition animation is set for the given classNamem, then the provided `from` and
5483 * `to` styles will be applied alongside the given transition. If the CSS style provided in `from` does not have a corresponding
5484 * style in `to`, the style in `from` is applied immediately, and no animation is run.
5485 * If a JavaScript animation is detected then the provided styles will be given in as function parameters into the `animate`
5486 * method (or as part of the `options` parameter):
5487 *
5488 * ```js
5489 * ngModule.animation('.my-inline-animation', function() {
5490 * return {
5491 * animate : function(element, from, to, done, options) {
5492 * //animation
5493 * done();
5494 * }
5495 * }
5496 * });
5497 * ```
5498 *
5499 * @param {DOMElement} element the element which the CSS styles will be applied to
5500 * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5501 * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5502 * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5503 * this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5504 * (Note that if no animation is detected then this value will not be applied to the element.)
5505 * @param {object=} options an optional collection of options/styles that will be applied to the element
5506 *
5507 * @return {Promise} the animation callback promise
5508 */
5509 animate: function(element, from, to, className, options) {
5510 options = prepareAnimateOptions(options);
5511 options.from = options.from ? extend(options.from, from) : from;
5512 options.to = options.to ? extend(options.to, to) : to;
5513
5514 className = className || 'ng-inline-animate';
5515 options.tempClasses = mergeClasses(options.tempClasses, className);
5516 return $$animateQueue.push(element, 'animate', options);
5517 }
5518 };
5519 }];
5520}];
5521
5522var $$AnimateAsyncRunFactoryProvider = function() {
5523 this.$get = ['$$rAF', function($$rAF) {
5524 var waitQueue = [];
5525
5526 function waitForTick(fn) {
5527 waitQueue.push(fn);
5528 if (waitQueue.length > 1) return;
5529 $$rAF(function() {
5530 for (var i = 0; i < waitQueue.length; i++) {
5531 waitQueue[i]();
5532 }
5533 waitQueue = [];
5534 });
5535 }
5536
5537 return function() {
5538 var passed = false;
5539 waitForTick(function() {
5540 passed = true;
5541 });
5542 return function(callback) {
5543 passed ? callback() : waitForTick(callback);
5544 };
5545 };
5546 }];
5547};
5548
5549var $$AnimateRunnerFactoryProvider = function() {
5550 this.$get = ['$q', '$sniffer', '$$animateAsyncRun', '$document', '$timeout',
5551 function($q, $sniffer, $$animateAsyncRun, $document, $timeout) {
5552
5553 var INITIAL_STATE = 0;
5554 var DONE_PENDING_STATE = 1;
5555 var DONE_COMPLETE_STATE = 2;
5556
5557 AnimateRunner.chain = function(chain, callback) {
5558 var index = 0;
5559
5560 next();
5561 function next() {
5562 if (index === chain.length) {
5563 callback(true);
5564 return;
5565 }
5566
5567 chain[index](function(response) {
5568 if (response === false) {
5569 callback(false);
5570 return;
5571 }
5572 index++;
5573 next();
5574 });
5575 }
5576 };
5577
5578 AnimateRunner.all = function(runners, callback) {
5579 var count = 0;
5580 var status = true;
5581 forEach(runners, function(runner) {
5582 runner.done(onProgress);
5583 });
5584
5585 function onProgress(response) {
5586 status = status && response;
5587 if (++count === runners.length) {
5588 callback(status);
5589 }
5590 }
5591 };
5592
5593 function AnimateRunner(host) {
5594 this.setHost(host);
5595
5596 var rafTick = $$animateAsyncRun();
5597 var timeoutTick = function(fn) {
5598 $timeout(fn, 0, false);
5599 };
5600
5601 this._doneCallbacks = [];
5602 this._tick = function(fn) {
5603 var doc = $document[0];
5604
5605 // the document may not be ready or attached
5606 // to the module for some internal tests
5607 if (doc && doc.hidden) {
5608 timeoutTick(fn);
5609 } else {
5610 rafTick(fn);
5611 }
5612 };
5613 this._state = 0;
5614 }
5615
5616 AnimateRunner.prototype = {
5617 setHost: function(host) {
5618 this.host = host || {};
5619 },
5620
5621 done: function(fn) {
5622 if (this._state === DONE_COMPLETE_STATE) {
5623 fn();
5624 } else {
5625 this._doneCallbacks.push(fn);
5626 }
5627 },
5628
5629 progress: noop,
5630
5631 getPromise: function() {
5632 if (!this.promise) {
5633 var self = this;
5634 this.promise = $q(function(resolve, reject) {
5635 self.done(function(status) {
5636 status === false ? reject() : resolve();
5637 });
5638 });
5639 }
5640 return this.promise;
5641 },
5642
5643 then: function(resolveHandler, rejectHandler) {
5644 return this.getPromise().then(resolveHandler, rejectHandler);
5645 },
5646
5647 'catch': function(handler) {
5648 return this.getPromise()['catch'](handler);
5649 },
5650
5651 'finally': function(handler) {
5652 return this.getPromise()['finally'](handler);
5653 },
5654
5655 pause: function() {
5656 if (this.host.pause) {
5657 this.host.pause();
5658 }
5659 },
5660
5661 resume: function() {
5662 if (this.host.resume) {
5663 this.host.resume();
5664 }
5665 },
5666
5667 end: function() {
5668 if (this.host.end) {
5669 this.host.end();
5670 }
5671 this._resolve(true);
5672 },
5673
5674 cancel: function() {
5675 if (this.host.cancel) {
5676 this.host.cancel();
5677 }
5678 this._resolve(false);
5679 },
5680
5681 complete: function(response) {
5682 var self = this;
5683 if (self._state === INITIAL_STATE) {
5684 self._state = DONE_PENDING_STATE;
5685 self._tick(function() {
5686 self._resolve(response);
5687 });
5688 }
5689 },
5690
5691 _resolve: function(response) {
5692 if (this._state !== DONE_COMPLETE_STATE) {
5693 forEach(this._doneCallbacks, function(fn) {
5694 fn(response);
5695 });
5696 this._doneCallbacks.length = 0;
5697 this._state = DONE_COMPLETE_STATE;
5698 }
5699 }
5700 };
5701
5702 return AnimateRunner;
5703 }];
5704};
5705
5706/**
5707 * @ngdoc service
5708 * @name $animateCss
5709 * @kind object
5710 *
5711 * @description
5712 * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5713 * then the `$animateCss` service will actually perform animations.
5714 *
5715 * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5716 */
5717var $CoreAnimateCssProvider = function() {
5718 this.$get = ['$$rAF', '$q', '$$AnimateRunner', function($$rAF, $q, $$AnimateRunner) {
5719
5720 return function(element, initialOptions) {
5721 // all of the animation functions should create
5722 // a copy of the options data, however, if a
5723 // parent service has already created a copy then
5724 // we should stick to using that
5725 var options = initialOptions || {};
5726 if (!options.$$prepared) {
5727 options = copy(options);
5728 }
5729
5730 // there is no point in applying the styles since
5731 // there is no animation that goes on at all in
5732 // this version of $animateCss.
5733 if (options.cleanupStyles) {
5734 options.from = options.to = null;
5735 }
5736
5737 if (options.from) {
5738 element.css(options.from);
5739 options.from = null;
5740 }
5741
5742 /* jshint newcap: false */
5743 var closed, runner = new $$AnimateRunner();
5744 return {
5745 start: run,
5746 end: run
5747 };
5748
5749 function run() {
5750 $$rAF(function() {
5751 applyAnimationContents();
5752 if (!closed) {
5753 runner.complete();
5754 }
5755 closed = true;
5756 });
5757 return runner;
5758 }
5759
5760 function applyAnimationContents() {
5761 if (options.addClass) {
5762 element.addClass(options.addClass);
5763 options.addClass = null;
5764 }
5765 if (options.removeClass) {
5766 element.removeClass(options.removeClass);
5767 options.removeClass = null;
5768 }
5769 if (options.to) {
5770 element.css(options.to);
5771 options.to = null;
5772 }
5773 }
5774 };
5775 }];
5776};
5777
5778/* global stripHash: true */
5779
5780/**
5781 * ! This is a private undocumented service !
5782 *
5783 * @name $browser
5784 * @requires $log
5785 * @description
5786 * This object has two goals:
5787 *
5788 * - hide all the global state in the browser caused by the window object
5789 * - abstract away all the browser specific features and inconsistencies
5790 *
5791 * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5792 * service, which can be used for convenient testing of the application without the interaction with
5793 * the real browser apis.
5794 */
5795/**
5796 * @param {object} window The global window object.
5797 * @param {object} document jQuery wrapped document.
5798 * @param {object} $log window.console or an object with the same interface.
5799 * @param {object} $sniffer $sniffer service
5800 */
5801function Browser(window, document, $log, $sniffer) {
5802 var self = this,
5803 location = window.location,
5804 history = window.history,
5805 setTimeout = window.setTimeout,
5806 clearTimeout = window.clearTimeout,
5807 pendingDeferIds = {};
5808
5809 self.isMock = false;
5810
5811 var outstandingRequestCount = 0;
5812 var outstandingRequestCallbacks = [];
5813
5814 // TODO(vojta): remove this temporary api
5815 self.$$completeOutstandingRequest = completeOutstandingRequest;
5816 self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5817
5818 /**
5819 * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5820 * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5821 */
5822 function completeOutstandingRequest(fn) {
5823 try {
5824 fn.apply(null, sliceArgs(arguments, 1));
5825 } finally {
5826 outstandingRequestCount--;
5827 if (outstandingRequestCount === 0) {
5828 while (outstandingRequestCallbacks.length) {
5829 try {
5830 outstandingRequestCallbacks.pop()();
5831 } catch (e) {
5832 $log.error(e);
5833 }
5834 }
5835 }
5836 }
5837 }
5838
5839 function getHash(url) {
5840 var index = url.indexOf('#');
5841 return index === -1 ? '' : url.substr(index);
5842 }
5843
5844 /**
5845 * @private
5846 * Note: this method is used only by scenario runner
5847 * TODO(vojta): prefix this method with $$ ?
5848 * @param {function()} callback Function that will be called when no outstanding request
5849 */
5850 self.notifyWhenNoOutstandingRequests = function(callback) {
5851 if (outstandingRequestCount === 0) {
5852 callback();
5853 } else {
5854 outstandingRequestCallbacks.push(callback);
5855 }
5856 };
5857
5858 //////////////////////////////////////////////////////////////
5859 // URL API
5860 //////////////////////////////////////////////////////////////
5861
5862 var cachedState, lastHistoryState,
5863 lastBrowserUrl = location.href,
5864 baseElement = document.find('base'),
5865 pendingLocation = null,
5866 getCurrentState = !$sniffer.history ? noop : function getCurrentState() {
5867 try {
5868 return history.state;
5869 } catch (e) {
5870 // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5871 }
5872 };
5873
5874 cacheState();
5875 lastHistoryState = cachedState;
5876
5877 /**
5878 * @name $browser#url
5879 *
5880 * @description
5881 * GETTER:
5882 * Without any argument, this method just returns current value of location.href.
5883 *
5884 * SETTER:
5885 * With at least one argument, this method sets url to new value.
5886 * If html5 history api supported, pushState/replaceState is used, otherwise
5887 * location.href/location.replace is used.
5888 * Returns its own instance to allow chaining
5889 *
5890 * NOTE: this api is intended for use only by the $location service. Please use the
5891 * {@link ng.$location $location service} to change url.
5892 *
5893 * @param {string} url New url (when used as setter)
5894 * @param {boolean=} replace Should new url replace current history record?
5895 * @param {object=} state object to use with pushState/replaceState
5896 */
5897 self.url = function(url, replace, state) {
5898 // In modern browsers `history.state` is `null` by default; treating it separately
5899 // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5900 // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5901 if (isUndefined(state)) {
5902 state = null;
5903 }
5904
5905 // Android Browser BFCache causes location, history reference to become stale.
5906 if (location !== window.location) location = window.location;
5907 if (history !== window.history) history = window.history;
5908
5909 // setter
5910 if (url) {
5911 var sameState = lastHistoryState === state;
5912
5913 // Don't change anything if previous and current URLs and states match. This also prevents
5914 // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5915 // See https://github.com/angular/angular.js/commit/ffb2701
5916 if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5917 return self;
5918 }
5919 var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5920 lastBrowserUrl = url;
5921 lastHistoryState = state;
5922 // Don't use history API if only the hash changed
5923 // due to a bug in IE10/IE11 which leads
5924 // to not firing a `hashchange` nor `popstate` event
5925 // in some cases (see #9143).
5926 if ($sniffer.history && (!sameBase || !sameState)) {
5927 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5928 cacheState();
5929 // Do the assignment again so that those two variables are referentially identical.
5930 lastHistoryState = cachedState;
5931 } else {
5932 if (!sameBase || pendingLocation) {
5933 pendingLocation = url;
5934 }
5935 if (replace) {
5936 location.replace(url);
5937 } else if (!sameBase) {
5938 location.href = url;
5939 } else {
5940 location.hash = getHash(url);
5941 }
5942 if (location.href !== url) {
5943 pendingLocation = url;
5944 }
5945 }
5946 return self;
5947 // getter
5948 } else {
5949 // - pendingLocation is needed as browsers don't allow to read out
5950 // the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5951 // https://openradar.appspot.com/22186109).
5952 // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5953 return pendingLocation || location.href.replace(/%27/g,"'");
5954 }
5955 };
5956
5957 /**
5958 * @name $browser#state
5959 *
5960 * @description
5961 * This method is a getter.
5962 *
5963 * Return history.state or null if history.state is undefined.
5964 *
5965 * @returns {object} state
5966 */
5967 self.state = function() {
5968 return cachedState;
5969 };
5970
5971 var urlChangeListeners = [],
5972 urlChangeInit = false;
5973
5974 function cacheStateAndFireUrlChange() {
5975 pendingLocation = null;
5976 cacheState();
5977 fireUrlChange();
5978 }
5979
5980 // This variable should be used *only* inside the cacheState function.
5981 var lastCachedState = null;
5982 function cacheState() {
5983 // This should be the only place in $browser where `history.state` is read.
5984 cachedState = getCurrentState();
5985 cachedState = isUndefined(cachedState) ? null : cachedState;
5986
5987 // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5988 if (equals(cachedState, lastCachedState)) {
5989 cachedState = lastCachedState;
5990 }
5991 lastCachedState = cachedState;
5992 }
5993
5994 function fireUrlChange() {
5995 if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5996 return;
5997 }
5998
5999 lastBrowserUrl = self.url();
6000 lastHistoryState = cachedState;
6001 forEach(urlChangeListeners, function(listener) {
6002 listener(self.url(), cachedState);
6003 });
6004 }
6005
6006 /**
6007 * @name $browser#onUrlChange
6008 *
6009 * @description
6010 * Register callback function that will be called, when url changes.
6011 *
6012 * It's only called when the url is changed from outside of angular:
6013 * - user types different url into address bar
6014 * - user clicks on history (forward/back) button
6015 * - user clicks on a link
6016 *
6017 * It's not called when url is changed by $browser.url() method
6018 *
6019 * The listener gets called with new url as parameter.
6020 *
6021 * NOTE: this api is intended for use only by the $location service. Please use the
6022 * {@link ng.$location $location service} to monitor url changes in angular apps.
6023 *
6024 * @param {function(string)} listener Listener function to be called when url changes.
6025 * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
6026 */
6027 self.onUrlChange = function(callback) {
6028 // TODO(vojta): refactor to use node's syntax for events
6029 if (!urlChangeInit) {
6030 // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
6031 // don't fire popstate when user change the address bar and don't fire hashchange when url
6032 // changed by push/replaceState
6033
6034 // html5 history api - popstate event
6035 if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
6036 // hashchange event
6037 jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
6038
6039 urlChangeInit = true;
6040 }
6041
6042 urlChangeListeners.push(callback);
6043 return callback;
6044 };
6045
6046 /**
6047 * @private
6048 * Remove popstate and hashchange handler from window.
6049 *
6050 * NOTE: this api is intended for use only by $rootScope.
6051 */
6052 self.$$applicationDestroyed = function() {
6053 jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
6054 };
6055
6056 /**
6057 * Checks whether the url has changed outside of Angular.
6058 * Needs to be exported to be able to check for changes that have been done in sync,
6059 * as hashchange/popstate events fire in async.
6060 */
6061 self.$$checkUrlChange = fireUrlChange;
6062
6063 //////////////////////////////////////////////////////////////
6064 // Misc API
6065 //////////////////////////////////////////////////////////////
6066
6067 /**
6068 * @name $browser#baseHref
6069 *
6070 * @description
6071 * Returns current <base href>
6072 * (always relative - without domain)
6073 *
6074 * @returns {string} The current base href
6075 */
6076 self.baseHref = function() {
6077 var href = baseElement.attr('href');
6078 return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
6079 };
6080
6081 /**
6082 * @name $browser#defer
6083 * @param {function()} fn A function, who's execution should be deferred.
6084 * @param {number=} [delay=0] of milliseconds to defer the function execution.
6085 * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
6086 *
6087 * @description
6088 * Executes a fn asynchronously via `setTimeout(fn, delay)`.
6089 *
6090 * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
6091 * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
6092 * via `$browser.defer.flush()`.
6093 *
6094 */
6095 self.defer = function(fn, delay) {
6096 var timeoutId;
6097 outstandingRequestCount++;
6098 timeoutId = setTimeout(function() {
6099 delete pendingDeferIds[timeoutId];
6100 completeOutstandingRequest(fn);
6101 }, delay || 0);
6102 pendingDeferIds[timeoutId] = true;
6103 return timeoutId;
6104 };
6105
6106
6107 /**
6108 * @name $browser#defer.cancel
6109 *
6110 * @description
6111 * Cancels a deferred task identified with `deferId`.
6112 *
6113 * @param {*} deferId Token returned by the `$browser.defer` function.
6114 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
6115 * canceled.
6116 */
6117 self.defer.cancel = function(deferId) {
6118 if (pendingDeferIds[deferId]) {
6119 delete pendingDeferIds[deferId];
6120 clearTimeout(deferId);
6121 completeOutstandingRequest(noop);
6122 return true;
6123 }
6124 return false;
6125 };
6126
6127}
6128
6129function $BrowserProvider() {
6130 this.$get = ['$window', '$log', '$sniffer', '$document',
6131 function($window, $log, $sniffer, $document) {
6132 return new Browser($window, $document, $log, $sniffer);
6133 }];
6134}
6135
6136/**
6137 * @ngdoc service
6138 * @name $cacheFactory
6139 *
6140 * @description
6141 * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
6142 * them.
6143 *
6144 * ```js
6145 *
6146 * var cache = $cacheFactory('cacheId');
6147 * expect($cacheFactory.get('cacheId')).toBe(cache);
6148 * expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
6149 *
6150 * cache.put("key", "value");
6151 * cache.put("another key", "another value");
6152 *
6153 * // We've specified no options on creation
6154 * expect(cache.info()).toEqual({id: 'cacheId', size: 2});
6155 *
6156 * ```
6157 *
6158 *
6159 * @param {string} cacheId Name or id of the newly created cache.
6160 * @param {object=} options Options object that specifies the cache behavior. Properties:
6161 *
6162 * - `{number=}` `capacity` — turns the cache into LRU cache.
6163 *
6164 * @returns {object} Newly created cache object with the following set of methods:
6165 *
6166 * - `{object}` `info()` — Returns id, size, and options of cache.
6167 * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
6168 * it.
6169 * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
6170 * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
6171 * - `{void}` `removeAll()` — Removes all cached values.
6172 * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
6173 *
6174 * @example
6175 <example module="cacheExampleApp">
6176 <file name="index.html">
6177 <div ng-controller="CacheController">
6178 <input ng-model="newCacheKey" placeholder="Key">
6179 <input ng-model="newCacheValue" placeholder="Value">
6180 <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
6181
6182 <p ng-if="keys.length">Cached Values</p>
6183 <div ng-repeat="key in keys">
6184 <span ng-bind="key"></span>
6185 <span>: </span>
6186 <b ng-bind="cache.get(key)"></b>
6187 </div>
6188
6189 <p>Cache Info</p>
6190 <div ng-repeat="(key, value) in cache.info()">
6191 <span ng-bind="key"></span>
6192 <span>: </span>
6193 <b ng-bind="value"></b>
6194 </div>
6195 </div>
6196 </file>
6197 <file name="script.js">
6198 angular.module('cacheExampleApp', []).
6199 controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6200 $scope.keys = [];
6201 $scope.cache = $cacheFactory('cacheId');
6202 $scope.put = function(key, value) {
6203 if (angular.isUndefined($scope.cache.get(key))) {
6204 $scope.keys.push(key);
6205 }
6206 $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6207 };
6208 }]);
6209 </file>
6210 <file name="style.css">
6211 p {
6212 margin: 10px 0 3px;
6213 }
6214 </file>
6215 </example>
6216 */
6217function $CacheFactoryProvider() {
6218
6219 this.$get = function() {
6220 var caches = {};
6221
6222 function cacheFactory(cacheId, options) {
6223 if (cacheId in caches) {
6224 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6225 }
6226
6227 var size = 0,
6228 stats = extend({}, options, {id: cacheId}),
6229 data = createMap(),
6230 capacity = (options && options.capacity) || Number.MAX_VALUE,
6231 lruHash = createMap(),
6232 freshEnd = null,
6233 staleEnd = null;
6234
6235 /**
6236 * @ngdoc type
6237 * @name $cacheFactory.Cache
6238 *
6239 * @description
6240 * A cache object used to store and retrieve data, primarily used by
6241 * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6242 * templates and other data.
6243 *
6244 * ```js
6245 * angular.module('superCache')
6246 * .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6247 * return $cacheFactory('super-cache');
6248 * }]);
6249 * ```
6250 *
6251 * Example test:
6252 *
6253 * ```js
6254 * it('should behave like a cache', inject(function(superCache) {
6255 * superCache.put('key', 'value');
6256 * superCache.put('another key', 'another value');
6257 *
6258 * expect(superCache.info()).toEqual({
6259 * id: 'super-cache',
6260 * size: 2
6261 * });
6262 *
6263 * superCache.remove('another key');
6264 * expect(superCache.get('another key')).toBeUndefined();
6265 *
6266 * superCache.removeAll();
6267 * expect(superCache.info()).toEqual({
6268 * id: 'super-cache',
6269 * size: 0
6270 * });
6271 * }));
6272 * ```
6273 */
6274 return caches[cacheId] = {
6275
6276 /**
6277 * @ngdoc method
6278 * @name $cacheFactory.Cache#put
6279 * @kind function
6280 *
6281 * @description
6282 * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6283 * retrieved later, and incrementing the size of the cache if the key was not already
6284 * present in the cache. If behaving like an LRU cache, it will also remove stale
6285 * entries from the set.
6286 *
6287 * It will not insert undefined values into the cache.
6288 *
6289 * @param {string} key the key under which the cached data is stored.
6290 * @param {*} value the value to store alongside the key. If it is undefined, the key
6291 * will not be stored.
6292 * @returns {*} the value stored.
6293 */
6294 put: function(key, value) {
6295 if (isUndefined(value)) return;
6296 if (capacity < Number.MAX_VALUE) {
6297 var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6298
6299 refresh(lruEntry);
6300 }
6301
6302 if (!(key in data)) size++;
6303 data[key] = value;
6304
6305 if (size > capacity) {
6306 this.remove(staleEnd.key);
6307 }
6308
6309 return value;
6310 },
6311
6312 /**
6313 * @ngdoc method
6314 * @name $cacheFactory.Cache#get
6315 * @kind function
6316 *
6317 * @description
6318 * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6319 *
6320 * @param {string} key the key of the data to be retrieved
6321 * @returns {*} the value stored.
6322 */
6323 get: function(key) {
6324 if (capacity < Number.MAX_VALUE) {
6325 var lruEntry = lruHash[key];
6326
6327 if (!lruEntry) return;
6328
6329 refresh(lruEntry);
6330 }
6331
6332 return data[key];
6333 },
6334
6335
6336 /**
6337 * @ngdoc method
6338 * @name $cacheFactory.Cache#remove
6339 * @kind function
6340 *
6341 * @description
6342 * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6343 *
6344 * @param {string} key the key of the entry to be removed
6345 */
6346 remove: function(key) {
6347 if (capacity < Number.MAX_VALUE) {
6348 var lruEntry = lruHash[key];
6349
6350 if (!lruEntry) return;
6351
6352 if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6353 if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6354 link(lruEntry.n,lruEntry.p);
6355
6356 delete lruHash[key];
6357 }
6358
6359 if (!(key in data)) return;
6360
6361 delete data[key];
6362 size--;
6363 },
6364
6365
6366 /**
6367 * @ngdoc method
6368 * @name $cacheFactory.Cache#removeAll
6369 * @kind function
6370 *
6371 * @description
6372 * Clears the cache object of any entries.
6373 */
6374 removeAll: function() {
6375 data = createMap();
6376 size = 0;
6377 lruHash = createMap();
6378 freshEnd = staleEnd = null;
6379 },
6380
6381
6382 /**
6383 * @ngdoc method
6384 * @name $cacheFactory.Cache#destroy
6385 * @kind function
6386 *
6387 * @description
6388 * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6389 * removing it from the {@link $cacheFactory $cacheFactory} set.
6390 */
6391 destroy: function() {
6392 data = null;
6393 stats = null;
6394 lruHash = null;
6395 delete caches[cacheId];
6396 },
6397
6398
6399 /**
6400 * @ngdoc method
6401 * @name $cacheFactory.Cache#info
6402 * @kind function
6403 *
6404 * @description
6405 * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6406 *
6407 * @returns {object} an object with the following properties:
6408 * <ul>
6409 * <li>**id**: the id of the cache instance</li>
6410 * <li>**size**: the number of entries kept in the cache instance</li>
6411 * <li>**...**: any additional properties from the options object when creating the
6412 * cache.</li>
6413 * </ul>
6414 */
6415 info: function() {
6416 return extend({}, stats, {size: size});
6417 }
6418 };
6419
6420
6421 /**
6422 * makes the `entry` the freshEnd of the LRU linked list
6423 */
6424 function refresh(entry) {
6425 if (entry != freshEnd) {
6426 if (!staleEnd) {
6427 staleEnd = entry;
6428 } else if (staleEnd == entry) {
6429 staleEnd = entry.n;
6430 }
6431
6432 link(entry.n, entry.p);
6433 link(entry, freshEnd);
6434 freshEnd = entry;
6435 freshEnd.n = null;
6436 }
6437 }
6438
6439
6440 /**
6441 * bidirectionally links two entries of the LRU linked list
6442 */
6443 function link(nextEntry, prevEntry) {
6444 if (nextEntry != prevEntry) {
6445 if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6446 if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6447 }
6448 }
6449 }
6450
6451
6452 /**
6453 * @ngdoc method
6454 * @name $cacheFactory#info
6455 *
6456 * @description
6457 * Get information about all the caches that have been created
6458 *
6459 * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6460 */
6461 cacheFactory.info = function() {
6462 var info = {};
6463 forEach(caches, function(cache, cacheId) {
6464 info[cacheId] = cache.info();
6465 });
6466 return info;
6467 };
6468
6469
6470 /**
6471 * @ngdoc method
6472 * @name $cacheFactory#get
6473 *
6474 * @description
6475 * Get access to a cache object by the `cacheId` used when it was created.
6476 *
6477 * @param {string} cacheId Name or id of a cache to access.
6478 * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6479 */
6480 cacheFactory.get = function(cacheId) {
6481 return caches[cacheId];
6482 };
6483
6484
6485 return cacheFactory;
6486 };
6487}
6488
6489/**
6490 * @ngdoc service
6491 * @name $templateCache
6492 *
6493 * @description
6494 * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6495 * can load templates directly into the cache in a `script` tag, or by consuming the
6496 * `$templateCache` service directly.
6497 *
6498 * Adding via the `script` tag:
6499 *
6500 * ```html
6501 * <script type="text/ng-template" id="templateId.html">
6502 * <p>This is the content of the template</p>
6503 * </script>
6504 * ```
6505 *
6506 * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6507 * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6508 * element with ng-app attribute), otherwise the template will be ignored.
6509 *
6510 * Adding via the `$templateCache` service:
6511 *
6512 * ```js
6513 * var myApp = angular.module('myApp', []);
6514 * myApp.run(function($templateCache) {
6515 * $templateCache.put('templateId.html', 'This is the content of the template');
6516 * });
6517 * ```
6518 *
6519 * To retrieve the template later, simply use it in your HTML:
6520 * ```html
6521 * <div ng-include=" 'templateId.html' "></div>
6522 * ```
6523 *
6524 * or get it via Javascript:
6525 * ```js
6526 * $templateCache.get('templateId.html')
6527 * ```
6528 *
6529 * See {@link ng.$cacheFactory $cacheFactory}.
6530 *
6531 */
6532function $TemplateCacheProvider() {
6533 this.$get = ['$cacheFactory', function($cacheFactory) {
6534 return $cacheFactory('templates');
6535 }];
6536}
6537
6538/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6539 * Any commits to this file should be reviewed with security in mind. *
6540 * Changes to this file can potentially create security vulnerabilities. *
6541 * An approval from 2 Core members with history of modifying *
6542 * this file is required. *
6543 * *
6544 * Does the change somehow allow for arbitrary javascript to be executed? *
6545 * Or allows for someone to change the prototype of built-in objects? *
6546 * Or gives undesired access to variables likes document or window? *
6547 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6548
6549/* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6550 *
6551 * DOM-related variables:
6552 *
6553 * - "node" - DOM Node
6554 * - "element" - DOM Element or Node
6555 * - "$node" or "$element" - jqLite-wrapped node or element
6556 *
6557 *
6558 * Compiler related stuff:
6559 *
6560 * - "linkFn" - linking fn of a single directive
6561 * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6562 * - "childLinkFn" - function that aggregates all linking fns for child nodes of a particular node
6563 * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6564 */
6565
6566
6567/**
6568 * @ngdoc service
6569 * @name $compile
6570 * @kind function
6571 *
6572 * @description
6573 * Compiles an HTML string or DOM into a template and produces a template function, which
6574 * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6575 *
6576 * The compilation is a process of walking the DOM tree and matching DOM elements to
6577 * {@link ng.$compileProvider#directive directives}.
6578 *
6579 * <div class="alert alert-warning">
6580 * **Note:** This document is an in-depth reference of all directive options.
6581 * For a gentle introduction to directives with examples of common use cases,
6582 * see the {@link guide/directive directive guide}.
6583 * </div>
6584 *
6585 * ## Comprehensive Directive API
6586 *
6587 * There are many different options for a directive.
6588 *
6589 * The difference resides in the return value of the factory function.
6590 * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6591 * or just the `postLink` function (all other properties will have the default values).
6592 *
6593 * <div class="alert alert-success">
6594 * **Best Practice:** It's recommended to use the "directive definition object" form.
6595 * </div>
6596 *
6597 * Here's an example directive declared with a Directive Definition Object:
6598 *
6599 * ```js
6600 * var myModule = angular.module(...);
6601 *
6602 * myModule.directive('directiveName', function factory(injectables) {
6603 * var directiveDefinitionObject = {
6604 * priority: 0,
6605 * template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6606 * // or
6607 * // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6608 * transclude: false,
6609 * restrict: 'A',
6610 * templateNamespace: 'html',
6611 * scope: false,
6612 * controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6613 * controllerAs: 'stringIdentifier',
6614 * bindToController: false,
6615 * require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6616 * compile: function compile(tElement, tAttrs, transclude) {
6617 * return {
6618 * pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6619 * post: function postLink(scope, iElement, iAttrs, controller) { ... }
6620 * }
6621 * // or
6622 * // return function postLink( ... ) { ... }
6623 * },
6624 * // or
6625 * // link: {
6626 * // pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6627 * // post: function postLink(scope, iElement, iAttrs, controller) { ... }
6628 * // }
6629 * // or
6630 * // link: function postLink( ... ) { ... }
6631 * };
6632 * return directiveDefinitionObject;
6633 * });
6634 * ```
6635 *
6636 * <div class="alert alert-warning">
6637 * **Note:** Any unspecified options will use the default value. You can see the default values below.
6638 * </div>
6639 *
6640 * Therefore the above can be simplified as:
6641 *
6642 * ```js
6643 * var myModule = angular.module(...);
6644 *
6645 * myModule.directive('directiveName', function factory(injectables) {
6646 * var directiveDefinitionObject = {
6647 * link: function postLink(scope, iElement, iAttrs) { ... }
6648 * };
6649 * return directiveDefinitionObject;
6650 * // or
6651 * // return function postLink(scope, iElement, iAttrs) { ... }
6652 * });
6653 * ```
6654 *
6655 *
6656 *
6657 * ### Directive Definition Object
6658 *
6659 * The directive definition object provides instructions to the {@link ng.$compile
6660 * compiler}. The attributes are:
6661 *
6662 * #### `multiElement`
6663 * When this property is set to true, the HTML compiler will collect DOM nodes between
6664 * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6665 * together as the directive elements. It is recommended that this feature be used on directives
6666 * which are not strictly behavioral (such as {@link ngClick}), and which
6667 * do not manipulate or replace child nodes (such as {@link ngInclude}).
6668 *
6669 * #### `priority`
6670 * When there are multiple directives defined on a single DOM element, sometimes it
6671 * is necessary to specify the order in which the directives are applied. The `priority` is used
6672 * to sort the directives before their `compile` functions get called. Priority is defined as a
6673 * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6674 * are also run in priority order, but post-link functions are run in reverse order. The order
6675 * of directives with the same priority is undefined. The default priority is `0`.
6676 *
6677 * #### `terminal`
6678 * If set to true then the current `priority` will be the last set of directives
6679 * which will execute (any directives at the current priority will still execute
6680 * as the order of execution on same `priority` is undefined). Note that expressions
6681 * and other directives used in the directive's template will also be excluded from execution.
6682 *
6683 * #### `scope`
6684 * The scope property can be `true`, an object or a falsy value:
6685 *
6686 * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6687 *
6688 * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6689 * the directive's element. If multiple directives on the same element request a new scope,
6690 * only one new scope is created. The new scope rule does not apply for the root of the template
6691 * since the root of the template always gets a new scope.
6692 *
6693 * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6694 * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6695 * scope. This is useful when creating reusable components, which should not accidentally read or modify
6696 * data in the parent scope.
6697 *
6698 * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6699 * directive's element. These local properties are useful for aliasing values for templates. The keys in
6700 * the object hash map to the name of the property on the isolate scope; the values define how the property
6701 * is bound to the parent scope, via matching attributes on the directive's element:
6702 *
6703 * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6704 * always a string since DOM attributes are strings. If no `attr` name is specified then the
6705 * attribute name is assumed to be the same as the local name. Given `<my-component
6706 * my-attr="hello {{name}}">` and the isolate scope definition `scope: { localName:'@myAttr' }`,
6707 * the directive's scope property `localName` will reflect the interpolated value of `hello
6708 * {{name}}`. As the `name` attribute changes so will the `localName` property on the directive's
6709 * scope. The `name` is read from the parent scope (not the directive's scope).
6710 *
6711 * * `=` or `=attr` - set up a bidirectional binding between a local scope property and an expression
6712 * passed via the attribute `attr`. The expression is evaluated in the context of the parent scope.
6713 * If no `attr` name is specified then the attribute name is assumed to be the same as the local
6714 * name. Given `<my-component my-attr="parentModel">` and the isolate scope definition `scope: {
6715 * localModel: '=myAttr' }`, the property `localModel` on the directive's scope will reflect the
6716 * value of `parentModel` on the parent scope. Changes to `parentModel` will be reflected in
6717 * `localModel` and vice versa. Optional attributes should be marked as such with a question mark:
6718 * `=?` or `=?attr`. If the binding expression is non-assignable, or if the attribute isn't
6719 * optional and doesn't exist, an exception ({@link error/$compile/nonassign `$compile:nonassign`})
6720 * will be thrown upon discovering changes to the local value, since it will be impossible to sync
6721 * them back to the parent scope. By default, the {@link ng.$rootScope.Scope#$watch `$watch`}
6722 * method is used for tracking changes, and the equality check is based on object identity.
6723 * However, if an object literal or an array literal is passed as the binding expression, the
6724 * equality check is done by value (using the {@link angular.equals} function). It's also possible
6725 * to watch the evaluated value shallowly with {@link ng.$rootScope.Scope#$watchCollection
6726 * `$watchCollection`}: use `=*` or `=*attr` (`=*?` or `=*?attr` if the attribute is optional).
6727 *
6728 * * `<` or `<attr` - set up a one-way (one-directional) binding between a local scope property and an
6729 * expression passed via the attribute `attr`. The expression is evaluated in the context of the
6730 * parent scope. If no `attr` name is specified then the attribute name is assumed to be the same as the
6731 * local name. You can also make the binding optional by adding `?`: `<?` or `<?attr`.
6732 *
6733 * For example, given `<my-component my-attr="parentModel">` and directive definition of
6734 * `scope: { localModel:'<myAttr' }`, then the isolated scope property `localModel` will reflect the
6735 * value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6736 * in `localModel`, but changes in `localModel` will not reflect in `parentModel`. There are however
6737 * two caveats:
6738 * 1. one-way binding does not copy the value from the parent to the isolate scope, it simply
6739 * sets the same value. That means if your bound value is an object, changes to its properties
6740 * in the isolated scope will be reflected in the parent scope (because both reference the same object).
6741 * 2. one-way binding watches changes to the **identity** of the parent value. That means the
6742 * {@link ng.$rootScope.Scope#$watch `$watch`} on the parent value only fires if the reference
6743 * to the value has changed. In most cases, this should not be of concern, but can be important
6744 * to know if you one-way bind to an object, and then replace that object in the isolated scope.
6745 * If you now change a property of the object in your parent scope, the change will not be
6746 * propagated to the isolated scope, because the identity of the object on the parent scope
6747 * has not changed. Instead you must assign a new object.
6748 *
6749 * One-way binding is useful if you do not plan to propagate changes to your isolated scope bindings
6750 * back to the parent. However, it does not make this completely impossible.
6751 *
6752 * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope. If
6753 * no `attr` name is specified then the attribute name is assumed to be the same as the local name.
6754 * Given `<my-component my-attr="count = count + value">` and the isolate scope definition `scope: {
6755 * localFn:'&myAttr' }`, the isolate scope property `localFn` will point to a function wrapper for
6756 * the `count = count + value` expression. Often it's desirable to pass data from the isolated scope
6757 * via an expression to the parent scope. This can be done by passing a map of local variable names
6758 * and values into the expression wrapper fn. For example, if the expression is `increment(amount)`
6759 * then we can specify the amount value by calling the `localFn` as `localFn({amount: 22})`.
6760 *
6761 * In general it's possible to apply more than one directive to one element, but there might be limitations
6762 * depending on the type of scope required by the directives. The following points will help explain these limitations.
6763 * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6764 *
6765 * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6766 * * **child scope** + **no scope** => Both directives will share one single child scope
6767 * * **child scope** + **child scope** => Both directives will share one single child scope
6768 * * **isolated scope** + **no scope** => The isolated directive will use it's own created isolated scope. The other directive will use
6769 * its parent's scope
6770 * * **isolated scope** + **child scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6771 * be applied to the same element.
6772 * * **isolated scope** + **isolated scope** => **Won't work!** Only one scope can be related to one element. Therefore these directives
6773 * cannot be applied to the same element.
6774 *
6775 *
6776 * #### `bindToController`
6777 * This property is used to bind scope properties directly to the controller. It can be either
6778 * `true` or an object hash with the same format as the `scope` property. Additionally, a controller
6779 * alias must be set, either by using `controllerAs: 'myAlias'` or by specifying the alias in the controller
6780 * definition: `controller: 'myCtrl as myAlias'`.
6781 *
6782 * When an isolate scope is used for a directive (see above), `bindToController: true` will
6783 * allow a component to have its properties bound to the controller, rather than to scope.
6784 *
6785 * After the controller is instantiated, the initial values of the isolate scope bindings will be bound to the controller
6786 * properties. You can access these bindings once they have been initialized by providing a controller method called
6787 * `$onInit`, which is called after all the controllers on an element have been constructed and had their bindings
6788 * initialized.
6789 *
6790 * <div class="alert alert-warning">
6791 * **Deprecation warning:** although bindings for non-ES6 class controllers are currently
6792 * bound to `this` before the controller constructor is called, this use is now deprecated. Please place initialization
6793 * code that relies upon bindings inside a `$onInit` method on the controller, instead.
6794 * </div>
6795 *
6796 * It is also possible to set `bindToController` to an object hash with the same format as the `scope` property.
6797 * This will set up the scope bindings to the controller directly. Note that `scope` can still be used
6798 * to define which kind of scope is created. By default, no scope is created. Use `scope: {}` to create an isolate
6799 * scope (useful for component directives).
6800 *
6801 * If both `bindToController` and `scope` are defined and have object hashes, `bindToController` overrides `scope`.
6802 *
6803 *
6804 * #### `controller`
6805 * Controller constructor function. The controller is instantiated before the
6806 * pre-linking phase and can be accessed by other directives (see
6807 * `require` attribute). This allows the directives to communicate with each other and augment
6808 * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6809 *
6810 * * `$scope` - Current scope associated with the element
6811 * * `$element` - Current element
6812 * * `$attrs` - Current attributes object for the element
6813 * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6814 * `function([scope], cloneLinkingFn, futureParentElement, slotName)`:
6815 * * `scope`: (optional) override the scope.
6816 * * `cloneLinkingFn`: (optional) argument to create clones of the original transcluded content.
6817 * * `futureParentElement` (optional):
6818 * * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6819 * * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6820 * * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6821 * and when the `cloneLinkinFn` is passed,
6822 * as those elements need to created and cloned in a special way when they are defined outside their
6823 * usual containers (e.g. like `<svg>`).
6824 * * See also the `directive.templateNamespace` property.
6825 * * `slotName`: (optional) the name of the slot to transclude. If falsy (e.g. `null`, `undefined` or `''`)
6826 * then the default translusion is provided.
6827 * The `$transclude` function also has a method on it, `$transclude.isSlotFilled(slotName)`, which returns
6828 * `true` if the specified slot contains content (i.e. one or more DOM nodes).
6829 *
6830 * The controller can provide the following methods that act as life-cycle hooks:
6831 * * `$onInit()` - Called on each controller after all the controllers on an element have been constructed and
6832 * had their bindings initialized (and before the pre & post linking functions for the directives on
6833 * this element). This is a good place to put initialization code for your controller.
6834 * * `$onChanges(changesObj)` - Called whenever one-way (`<`) or interpolation (`@`) bindings are updated. The
6835 * `changesObj` is a hash whose keys are the names of the bound properties that have changed, and the values are an
6836 * object of the form `{ currentValue: ..., previousValue: ... }`. Use this hook to trigger updates within a component
6837 * such as cloning the bound value to prevent accidental mutation of the outer value.
6838 * * `$onDestroy()` - Called on a controller when its containing scope is destroyed. Use this hook for releasing
6839 * external resources, watches and event handlers. Note that components have their `$onDestroy()` hooks called in
6840 * the same order as the `$scope.$broadcast` events are triggered, which is top down. This means that parent
6841 * components will have their `$onDestroy()` hook called before child components.
6842 * * `$postLink()` - Called after this controller's element and its children have been linked. Similar to the post-link
6843 * function this hook can be used to set up DOM event handlers and do direct DOM manipulation.
6844 * Note that child elements that contain `templateUrl` directives will not have been compiled and linked since
6845 * they are waiting for their template to load asynchronously and their own compilation and linking has been
6846 * suspended until that occurs.
6847 *
6848 *
6849 * #### `require`
6850 * Require another directive and inject its controller as the fourth argument to the linking function. The
6851 * `require` property can be a string, an array or an object:
6852 * * a **string** containing the name of the directive to pass to the linking function
6853 * * an **array** containing the names of directives to pass to the linking function. The argument passed to the
6854 * linking function will be an array of controllers in the same order as the names in the `require` property
6855 * * an **object** whose property values are the names of the directives to pass to the linking function. The argument
6856 * passed to the linking function will also be an object with matching keys, whose values will hold the corresponding
6857 * controllers.
6858 *
6859 * If the `require` property is an object and `bindToController` is truthy, then the required controllers are
6860 * bound to the controller using the keys of the `require` property. This binding occurs after all the controllers
6861 * have been constructed but before `$onInit` is called.
6862 * See the {@link $compileProvider#component} helper for an example of how this can be used.
6863 *
6864 * If no such required directive(s) can be found, or if the directive does not have a controller, then an error is
6865 * raised (unless no link function is specified and the required controllers are not being bound to the directive
6866 * controller, in which case error checking is skipped). The name can be prefixed with:
6867 *
6868 * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6869 * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6870 * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6871 * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6872 * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6873 * `null` to the `link` fn if not found.
6874 * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6875 * `null` to the `link` fn if not found.
6876 *
6877 *
6878 * #### `controllerAs`
6879 * Identifier name for a reference to the controller in the directive's scope.
6880 * This allows the controller to be referenced from the directive template. This is especially
6881 * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6882 * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6883 * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6884 *
6885 *
6886 * #### `restrict`
6887 * String of subset of `EACM` which restricts the directive to a specific directive
6888 * declaration style. If omitted, the defaults (elements and attributes) are used.
6889 *
6890 * * `E` - Element name (default): `<my-directive></my-directive>`
6891 * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6892 * * `C` - Class: `<div class="my-directive: exp;"></div>`
6893 * * `M` - Comment: `<!-- directive: my-directive exp -->`
6894 *
6895 *
6896 * #### `templateNamespace`
6897 * String representing the document type used by the markup in the template.
6898 * AngularJS needs this information as those elements need to be created and cloned
6899 * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6900 *
6901 * * `html` - All root nodes in the template are HTML. Root nodes may also be
6902 * top-level elements such as `<svg>` or `<math>`.
6903 * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6904 * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6905 *
6906 * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6907 *
6908 * #### `template`
6909 * HTML markup that may:
6910 * * Replace the contents of the directive's element (default).
6911 * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6912 * * Wrap the contents of the directive's element (if `transclude` is true).
6913 *
6914 * Value may be:
6915 *
6916 * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6917 * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6918 * function api below) and returns a string value.
6919 *
6920 *
6921 * #### `templateUrl`
6922 * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6923 *
6924 * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6925 * for later when the template has been resolved. In the meantime it will continue to compile and link
6926 * sibling and parent elements as though this element had not contained any directives.
6927 *
6928 * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6929 * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6930 * case when only one deeply nested directive has `templateUrl`.
6931 *
6932 * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6933 *
6934 * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6935 * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6936 * a string value representing the url. In either case, the template URL is passed through {@link
6937 * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6938 *
6939 *
6940 * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6941 * specify what the template should replace. Defaults to `false`.
6942 *
6943 * * `true` - the template will replace the directive's element.
6944 * * `false` - the template will replace the contents of the directive's element.
6945 *
6946 * The replacement process migrates all of the attributes / classes from the old element to the new
6947 * one. See the {@link guide/directive#template-expanding-directive
6948 * Directives Guide} for an example.
6949 *
6950 * There are very few scenarios where element replacement is required for the application function,
6951 * the main one being reusable custom components that are used within SVG contexts
6952 * (because SVG doesn't work with custom elements in the DOM tree).
6953 *
6954 * #### `transclude`
6955 * Extract the contents of the element where the directive appears and make it available to the directive.
6956 * The contents are compiled and provided to the directive as a **transclusion function**. See the
6957 * {@link $compile#transclusion Transclusion} section below.
6958 *
6959 *
6960 * #### `compile`
6961 *
6962 * ```js
6963 * function compile(tElement, tAttrs, transclude) { ... }
6964 * ```
6965 *
6966 * The compile function deals with transforming the template DOM. Since most directives do not do
6967 * template transformation, it is not used often. The compile function takes the following arguments:
6968 *
6969 * * `tElement` - template element - The element where the directive has been declared. It is
6970 * safe to do template transformation on the element and child elements only.
6971 *
6972 * * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6973 * between all directive compile functions.
6974 *
6975 * * `transclude` - [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6976 *
6977 * <div class="alert alert-warning">
6978 * **Note:** The template instance and the link instance may be different objects if the template has
6979 * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6980 * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6981 * should be done in a linking function rather than in a compile function.
6982 * </div>
6983
6984 * <div class="alert alert-warning">
6985 * **Note:** The compile function cannot handle directives that recursively use themselves in their
6986 * own templates or compile functions. Compiling these directives results in an infinite loop and
6987 * stack overflow errors.
6988 *
6989 * This can be avoided by manually using $compile in the postLink function to imperatively compile
6990 * a directive's template instead of relying on automatic template compilation via `template` or
6991 * `templateUrl` declaration or manual compilation inside the compile function.
6992 * </div>
6993 *
6994 * <div class="alert alert-danger">
6995 * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6996 * e.g. does not know about the right outer scope. Please use the transclude function that is passed
6997 * to the link function instead.
6998 * </div>
6999
7000 * A compile function can have a return value which can be either a function or an object.
7001 *
7002 * * returning a (post-link) function - is equivalent to registering the linking function via the
7003 * `link` property of the config object when the compile function is empty.
7004 *
7005 * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
7006 * control when a linking function should be called during the linking phase. See info about
7007 * pre-linking and post-linking functions below.
7008 *
7009 *
7010 * #### `link`
7011 * This property is used only if the `compile` property is not defined.
7012 *
7013 * ```js
7014 * function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
7015 * ```
7016 *
7017 * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
7018 * executed after the template has been cloned. This is where most of the directive logic will be
7019 * put.
7020 *
7021 * * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
7022 * directive for registering {@link ng.$rootScope.Scope#$watch watches}.
7023 *
7024 * * `iElement` - instance element - The element where the directive is to be used. It is safe to
7025 * manipulate the children of the element only in `postLink` function since the children have
7026 * already been linked.
7027 *
7028 * * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
7029 * between all directive linking functions.
7030 *
7031 * * `controller` - the directive's required controller instance(s) - Instances are shared
7032 * among all directives, which allows the directives to use the controllers as a communication
7033 * channel. The exact value depends on the directive's `require` property:
7034 * * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
7035 * * `string`: the controller instance
7036 * * `array`: array of controller instances
7037 *
7038 * If a required controller cannot be found, and it is optional, the instance is `null`,
7039 * otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
7040 *
7041 * Note that you can also require the directive's own controller - it will be made available like
7042 * any other controller.
7043 *
7044 * * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
7045 * This is the same as the `$transclude`
7046 * parameter of directive controllers, see there for details.
7047 * `function([scope], cloneLinkingFn, futureParentElement)`.
7048 *
7049 * #### Pre-linking function
7050 *
7051 * Executed before the child elements are linked. Not safe to do DOM transformation since the
7052 * compiler linking function will fail to locate the correct elements for linking.
7053 *
7054 * #### Post-linking function
7055 *
7056 * Executed after the child elements are linked.
7057 *
7058 * Note that child elements that contain `templateUrl` directives will not have been compiled
7059 * and linked since they are waiting for their template to load asynchronously and their own
7060 * compilation and linking has been suspended until that occurs.
7061 *
7062 * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
7063 * for their async templates to be resolved.
7064 *
7065 *
7066 * ### Transclusion
7067 *
7068 * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
7069 * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
7070 * scope from where they were taken.
7071 *
7072 * Transclusion is used (often with {@link ngTransclude}) to insert the
7073 * original contents of a directive's element into a specified place in the template of the directive.
7074 * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
7075 * content has access to the properties on the scope from which it was taken, even if the directive
7076 * has isolated scope.
7077 * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
7078 *
7079 * This makes it possible for the widget to have private state for its template, while the transcluded
7080 * content has access to its originating scope.
7081 *
7082 * <div class="alert alert-warning">
7083 * **Note:** When testing an element transclude directive you must not place the directive at the root of the
7084 * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
7085 * Testing Transclusion Directives}.
7086 * </div>
7087 *
7088 * There are three kinds of transclusion depending upon whether you want to transclude just the contents of the
7089 * directive's element, the entire element or multiple parts of the element contents:
7090 *
7091 * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
7092 * * `'element'` - transclude the whole of the directive's element including any directives on this
7093 * element that defined at a lower priority than this directive. When used, the `template`
7094 * property is ignored.
7095 * * **`{...}` (an object hash):** - map elements of the content onto transclusion "slots" in the template.
7096 *
7097 * **Mult-slot transclusion** is declared by providing an object for the `transclude` property.
7098 *
7099 * This object is a map where the keys are the name of the slot to fill and the value is an element selector
7100 * used to match the HTML to the slot. The element selector should be in normalized form (e.g. `myElement`)
7101 * and will match the standard element variants (e.g. `my-element`, `my:element`, `data-my-element`, etc).
7102 *
7103 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7104 *
7105 * If the element selector is prefixed with a `?` then that slot is optional.
7106 *
7107 * For example, the transclude object `{ slotA: '?myCustomElement' }` maps `<my-custom-element>` elements to
7108 * the `slotA` slot, which can be accessed via the `$transclude` function or via the {@link ngTransclude} directive.
7109 *
7110 * Slots that are not marked as optional (`?`) will trigger a compile time error if there are no matching elements
7111 * in the transclude content. If you wish to know if an optional slot was filled with content, then you can call
7112 * `$transclude.isSlotFilled(slotName)` on the transclude function passed to the directive's link function and
7113 * injectable into the directive's controller.
7114 *
7115 *
7116 * #### Transclusion Functions
7117 *
7118 * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
7119 * function** to the directive's `link` function and `controller`. This transclusion function is a special
7120 * **linking function** that will return the compiled contents linked to a new transclusion scope.
7121 *
7122 * <div class="alert alert-info">
7123 * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
7124 * ngTransclude will deal with it for us.
7125 * </div>
7126 *
7127 * If you want to manually control the insertion and removal of the transcluded content in your directive
7128 * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
7129 * object that contains the compiled DOM, which is linked to the correct transclusion scope.
7130 *
7131 * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
7132 * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
7133 * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
7134 *
7135 * <div class="alert alert-info">
7136 * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a transclude function
7137 * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
7138 * </div>
7139 *
7140 * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
7141 * attach function**:
7142 *
7143 * ```js
7144 * var transcludedContent, transclusionScope;
7145 *
7146 * $transclude(function(clone, scope) {
7147 * element.append(clone);
7148 * transcludedContent = clone;
7149 * transclusionScope = scope;
7150 * });
7151 * ```
7152 *
7153 * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
7154 * associated transclusion scope:
7155 *
7156 * ```js
7157 * transcludedContent.remove();
7158 * transclusionScope.$destroy();
7159 * ```
7160 *
7161 * <div class="alert alert-info">
7162 * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
7163 * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
7164 * then you are also responsible for calling `$destroy` on the transclusion scope.
7165 * </div>
7166 *
7167 * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
7168 * automatically destroy their transcluded clones as necessary so you do not need to worry about this if
7169 * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
7170 *
7171 *
7172 * #### Transclusion Scopes
7173 *
7174 * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
7175 * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
7176 * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
7177 * was taken.
7178 *
7179 * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
7180 * like this:
7181 *
7182 * ```html
7183 * <div ng-app>
7184 * <div isolate>
7185 * <div transclusion>
7186 * </div>
7187 * </div>
7188 * </div>
7189 * ```
7190 *
7191 * The `$parent` scope hierarchy will look like this:
7192 *
7193 ```
7194 - $rootScope
7195 - isolate
7196 - transclusion
7197 ```
7198 *
7199 * but the scopes will inherit prototypically from different scopes to their `$parent`.
7200 *
7201 ```
7202 - $rootScope
7203 - transclusion
7204 - isolate
7205 ```
7206 *
7207 *
7208 * ### Attributes
7209 *
7210 * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
7211 * `link()` or `compile()` functions. It has a variety of uses.
7212 *
7213 * * *Accessing normalized attribute names:* Directives like 'ngBind' can be expressed in many ways:
7214 * 'ng:bind', `data-ng-bind`, or 'x-ng-bind'. The attributes object allows for normalized access
7215 * to the attributes.
7216 *
7217 * * *Directive inter-communication:* All directives share the same instance of the attributes
7218 * object which allows the directives to use the attributes object as inter directive
7219 * communication.
7220 *
7221 * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
7222 * allowing other directives to read the interpolated value.
7223 *
7224 * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
7225 * that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
7226 * the only way to easily get the actual value because during the linking phase the interpolation
7227 * hasn't been evaluated yet and so the value is at this time set to `undefined`.
7228 *
7229 * ```js
7230 * function linkingFn(scope, elm, attrs, ctrl) {
7231 * // get the attribute value
7232 * console.log(attrs.ngModel);
7233 *
7234 * // change the attribute
7235 * attrs.$set('ngModel', 'new value');
7236 *
7237 * // observe changes to interpolated attribute
7238 * attrs.$observe('ngModel', function(value) {
7239 * console.log('ngModel has changed value to ' + value);
7240 * });
7241 * }
7242 * ```
7243 *
7244 * ## Example
7245 *
7246 * <div class="alert alert-warning">
7247 * **Note**: Typically directives are registered with `module.directive`. The example below is
7248 * to illustrate how `$compile` works.
7249 * </div>
7250 *
7251 <example module="compileExample">
7252 <file name="index.html">
7253 <script>
7254 angular.module('compileExample', [], function($compileProvider) {
7255 // configure new 'compile' directive by passing a directive
7256 // factory function. The factory function injects the '$compile'
7257 $compileProvider.directive('compile', function($compile) {
7258 // directive factory creates a link function
7259 return function(scope, element, attrs) {
7260 scope.$watch(
7261 function(scope) {
7262 // watch the 'compile' expression for changes
7263 return scope.$eval(attrs.compile);
7264 },
7265 function(value) {
7266 // when the 'compile' expression changes
7267 // assign it into the current DOM
7268 element.html(value);
7269
7270 // compile the new DOM and link it to the current
7271 // scope.
7272 // NOTE: we only compile .childNodes so that
7273 // we don't get into infinite loop compiling ourselves
7274 $compile(element.contents())(scope);
7275 }
7276 );
7277 };
7278 });
7279 })
7280 .controller('GreeterController', ['$scope', function($scope) {
7281 $scope.name = 'Angular';
7282 $scope.html = 'Hello {{name}}';
7283 }]);
7284 </script>
7285 <div ng-controller="GreeterController">
7286 <input ng-model="name"> <br/>
7287 <textarea ng-model="html"></textarea> <br/>
7288 <div compile="html"></div>
7289 </div>
7290 </file>
7291 <file name="protractor.js" type="protractor">
7292 it('should auto compile', function() {
7293 var textarea = $('textarea');
7294 var output = $('div[compile]');
7295 // The initial state reads 'Hello Angular'.
7296 expect(output.getText()).toBe('Hello Angular');
7297 textarea.clear();
7298 textarea.sendKeys('{{name}}!');
7299 expect(output.getText()).toBe('Angular!');
7300 });
7301 </file>
7302 </example>
7303
7304 *
7305 *
7306 * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7307 * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7308 *
7309 * <div class="alert alert-danger">
7310 * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7311 * e.g. will not use the right outer scope. Please pass the transclude function as a
7312 * `parentBoundTranscludeFn` to the link function instead.
7313 * </div>
7314 *
7315 * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7316 * root element(s), not their children)
7317 * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7318 * (a DOM element/tree) to a scope. Where:
7319 *
7320 * * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7321 * * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7322 * `template` and call the `cloneAttachFn` function allowing the caller to attach the
7323 * cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7324 * called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7325 *
7326 * * `clonedElement` - is a clone of the original `element` passed into the compiler.
7327 * * `scope` - is the current scope with which the linking function is working with.
7328 *
7329 * * `options` - An optional object hash with linking options. If `options` is provided, then the following
7330 * keys may be used to control linking behavior:
7331 *
7332 * * `parentBoundTranscludeFn` - the transclude function made available to
7333 * directives; if given, it will be passed through to the link functions of
7334 * directives found in `element` during compilation.
7335 * * `transcludeControllers` - an object hash with keys that map controller names
7336 * to a hash with the key `instance`, which maps to the controller instance;
7337 * if given, it will make the controllers available to directives on the compileNode:
7338 * ```
7339 * {
7340 * parent: {
7341 * instance: parentControllerInstance
7342 * }
7343 * }
7344 * ```
7345 * * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7346 * the cloned elements; only needed for transcludes that are allowed to contain non html
7347 * elements (e.g. SVG elements). See also the directive.controller property.
7348 *
7349 * Calling the linking function returns the element of the template. It is either the original
7350 * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7351 *
7352 * After linking the view is not updated until after a call to $digest which typically is done by
7353 * Angular automatically.
7354 *
7355 * If you need access to the bound view, there are two ways to do it:
7356 *
7357 * - If you are not asking the linking function to clone the template, create the DOM element(s)
7358 * before you send them to the compiler and keep this reference around.
7359 * ```js
7360 * var element = $compile('<p>{{total}}</p>')(scope);
7361 * ```
7362 *
7363 * - if on the other hand, you need the element to be cloned, the view reference from the original
7364 * example would not point to the clone, but rather to the original template that was cloned. In
7365 * this case, you can access the clone via the cloneAttachFn:
7366 * ```js
7367 * var templateElement = angular.element('<p>{{total}}</p>'),
7368 * scope = ....;
7369 *
7370 * var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7371 * //attach the clone to DOM document at the right place
7372 * });
7373 *
7374 * //now we have reference to the cloned DOM via `clonedElement`
7375 * ```
7376 *
7377 *
7378 * For information on how the compiler works, see the
7379 * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7380 */
7381
7382var $compileMinErr = minErr('$compile');
7383
7384/**
7385 * @ngdoc provider
7386 * @name $compileProvider
7387 *
7388 * @description
7389 */
7390$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7391function $CompileProvider($provide, $$sanitizeUriProvider) {
7392 var hasDirectives = {},
7393 Suffix = 'Directive',
7394 COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7395 CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7396 ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7397 REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7398
7399 // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7400 // The assumption is that future DOM event attribute names will begin with
7401 // 'on' and be composed of only English letters.
7402 var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7403 var bindingCache = createMap();
7404
7405 function parseIsolateBindings(scope, directiveName, isController) {
7406 var LOCAL_REGEXP = /^\s*([@&<]|=(\*?))(\??)\s*(\w*)\s*$/;
7407
7408 var bindings = {};
7409
7410 forEach(scope, function(definition, scopeName) {
7411 if (definition in bindingCache) {
7412 bindings[scopeName] = bindingCache[definition];
7413 return;
7414 }
7415 var match = definition.match(LOCAL_REGEXP);
7416
7417 if (!match) {
7418 throw $compileMinErr('iscp',
7419 "Invalid {3} for directive '{0}'." +
7420 " Definition: {... {1}: '{2}' ...}",
7421 directiveName, scopeName, definition,
7422 (isController ? "controller bindings definition" :
7423 "isolate scope definition"));
7424 }
7425
7426 bindings[scopeName] = {
7427 mode: match[1][0],
7428 collection: match[2] === '*',
7429 optional: match[3] === '?',
7430 attrName: match[4] || scopeName
7431 };
7432 if (match[4]) {
7433 bindingCache[definition] = bindings[scopeName];
7434 }
7435 });
7436
7437 return bindings;
7438 }
7439
7440 function parseDirectiveBindings(directive, directiveName) {
7441 var bindings = {
7442 isolateScope: null,
7443 bindToController: null
7444 };
7445 if (isObject(directive.scope)) {
7446 if (directive.bindToController === true) {
7447 bindings.bindToController = parseIsolateBindings(directive.scope,
7448 directiveName, true);
7449 bindings.isolateScope = {};
7450 } else {
7451 bindings.isolateScope = parseIsolateBindings(directive.scope,
7452 directiveName, false);
7453 }
7454 }
7455 if (isObject(directive.bindToController)) {
7456 bindings.bindToController =
7457 parseIsolateBindings(directive.bindToController, directiveName, true);
7458 }
7459 if (isObject(bindings.bindToController)) {
7460 var controller = directive.controller;
7461 var controllerAs = directive.controllerAs;
7462 if (!controller) {
7463 // There is no controller, there may or may not be a controllerAs property
7464 throw $compileMinErr('noctrl',
7465 "Cannot bind to controller without directive '{0}'s controller.",
7466 directiveName);
7467 } else if (!identifierForController(controller, controllerAs)) {
7468 // There is a controller, but no identifier or controllerAs property
7469 throw $compileMinErr('noident',
7470 "Cannot bind to controller without identifier for directive '{0}'.",
7471 directiveName);
7472 }
7473 }
7474 return bindings;
7475 }
7476
7477 function assertValidDirectiveName(name) {
7478 var letter = name.charAt(0);
7479 if (!letter || letter !== lowercase(letter)) {
7480 throw $compileMinErr('baddir', "Directive/Component name '{0}' is invalid. The first character must be a lowercase letter", name);
7481 }
7482 if (name !== name.trim()) {
7483 throw $compileMinErr('baddir',
7484 "Directive/Component name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7485 name);
7486 }
7487 }
7488
7489 /**
7490 * @ngdoc method
7491 * @name $compileProvider#directive
7492 * @kind function
7493 *
7494 * @description
7495 * Register a new directive with the compiler.
7496 *
7497 * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7498 * will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7499 * names and the values are the factories.
7500 * @param {Function|Array} directiveFactory An injectable directive factory function. See the
7501 * {@link guide/directive directive guide} and the {@link $compile compile API} for more info.
7502 * @returns {ng.$compileProvider} Self for chaining.
7503 */
7504 this.directive = function registerDirective(name, directiveFactory) {
7505 assertNotHasOwnProperty(name, 'directive');
7506 if (isString(name)) {
7507 assertValidDirectiveName(name);
7508 assertArg(directiveFactory, 'directiveFactory');
7509 if (!hasDirectives.hasOwnProperty(name)) {
7510 hasDirectives[name] = [];
7511 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7512 function($injector, $exceptionHandler) {
7513 var directives = [];
7514 forEach(hasDirectives[name], function(directiveFactory, index) {
7515 try {
7516 var directive = $injector.invoke(directiveFactory);
7517 if (isFunction(directive)) {
7518 directive = { compile: valueFn(directive) };
7519 } else if (!directive.compile && directive.link) {
7520 directive.compile = valueFn(directive.link);
7521 }
7522 directive.priority = directive.priority || 0;
7523 directive.index = index;
7524 directive.name = directive.name || name;
7525 directive.require = directive.require || (directive.controller && directive.name);
7526 directive.restrict = directive.restrict || 'EA';
7527 directive.$$moduleName = directiveFactory.$$moduleName;
7528 directives.push(directive);
7529 } catch (e) {
7530 $exceptionHandler(e);
7531 }
7532 });
7533 return directives;
7534 }]);
7535 }
7536 hasDirectives[name].push(directiveFactory);
7537 } else {
7538 forEach(name, reverseParams(registerDirective));
7539 }
7540 return this;
7541 };
7542
7543 /**
7544 * @ngdoc method
7545 * @name $compileProvider#component
7546 * @module ng
7547 * @param {string} name Name of the component in camelCase (i.e. `myComp` which will match `<my-comp>`)
7548 * @param {Object} options Component definition object (a simplified
7549 * {@link ng.$compile#directive-definition-object directive definition object}),
7550 * with the following properties (all optional):
7551 *
7552 * - `controller` – `{(string|function()=}` – controller constructor function that should be
7553 * associated with newly created scope or the name of a {@link ng.$compile#-controller-
7554 * registered controller} if passed as a string. An empty `noop` function by default.
7555 * - `controllerAs` – `{string=}` – identifier name for to reference the controller in the component's scope.
7556 * If present, the controller will be published to scope under the `controllerAs` name.
7557 * If not present, this will default to be `$ctrl`.
7558 * - `template` – `{string=|function()=}` – html template as a string or a function that
7559 * returns an html template as a string which should be used as the contents of this component.
7560 * Empty string by default.
7561 *
7562 * If `template` is a function, then it is {@link auto.$injector#invoke injected} with
7563 * the following locals:
7564 *
7565 * - `$element` - Current element
7566 * - `$attrs` - Current attributes object for the element
7567 *
7568 * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
7569 * template that should be used as the contents of this component.
7570 *
7571 * If `templateUrl` is a function, then it is {@link auto.$injector#invoke injected} with
7572 * the following locals:
7573 *
7574 * - `$element` - Current element
7575 * - `$attrs` - Current attributes object for the element
7576 *
7577 * - `bindings` – `{object=}` – defines bindings between DOM attributes and component properties.
7578 * Component properties are always bound to the component controller and not to the scope.
7579 * See {@link ng.$compile#-bindtocontroller- `bindToController`}.
7580 * - `transclude` – `{boolean=}` – whether {@link $compile#transclusion content transclusion} is enabled.
7581 * Disabled by default.
7582 * - `$...` – additional properties to attach to the directive factory function and the controller
7583 * constructor function. (This is used by the component router to annotate)
7584 *
7585 * @returns {ng.$compileProvider} the compile provider itself, for chaining of function calls.
7586 * @description
7587 * Register a **component definition** with the compiler. This is a shorthand for registering a special
7588 * type of directive, which represents a self-contained UI component in your application. Such components
7589 * are always isolated (i.e. `scope: {}`) and are always restricted to elements (i.e. `restrict: 'E'`).
7590 *
7591 * Component definitions are very simple and do not require as much configuration as defining general
7592 * directives. Component definitions usually consist only of a template and a controller backing it.
7593 *
7594 * In order to make the definition easier, components enforce best practices like use of `controllerAs`,
7595 * `bindToController`. They always have **isolate scope** and are restricted to elements.
7596 *
7597 * Here are a few examples of how you would usually define components:
7598 *
7599 * ```js
7600 * var myMod = angular.module(...);
7601 * myMod.component('myComp', {
7602 * template: '<div>My name is {{$ctrl.name}}</div>',
7603 * controller: function() {
7604 * this.name = 'shahar';
7605 * }
7606 * });
7607 *
7608 * myMod.component('myComp', {
7609 * template: '<div>My name is {{$ctrl.name}}</div>',
7610 * bindings: {name: '@'}
7611 * });
7612 *
7613 * myMod.component('myComp', {
7614 * templateUrl: 'views/my-comp.html',
7615 * controller: 'MyCtrl',
7616 * controllerAs: 'ctrl',
7617 * bindings: {name: '@'}
7618 * });
7619 *
7620 * ```
7621 * For more examples, and an in-depth guide, see the {@link guide/component component guide}.
7622 *
7623 * <br />
7624 * See also {@link ng.$compileProvider#directive $compileProvider.directive()}.
7625 */
7626 this.component = function registerComponent(name, options) {
7627 var controller = options.controller || noop;
7628
7629 function factory($injector) {
7630 function makeInjectable(fn) {
7631 if (isFunction(fn) || isArray(fn)) {
7632 return function(tElement, tAttrs) {
7633 return $injector.invoke(fn, this, {$element: tElement, $attrs: tAttrs});
7634 };
7635 } else {
7636 return fn;
7637 }
7638 }
7639
7640 var template = (!options.template && !options.templateUrl ? '' : options.template);
7641 return {
7642 controller: controller,
7643 controllerAs: identifierForController(options.controller) || options.controllerAs || '$ctrl',
7644 template: makeInjectable(template),
7645 templateUrl: makeInjectable(options.templateUrl),
7646 transclude: options.transclude,
7647 scope: {},
7648 bindToController: options.bindings || {},
7649 restrict: 'E',
7650 require: options.require
7651 };
7652 }
7653
7654 // Copy any annotation properties (starting with $) over to the factory function
7655 // These could be used by libraries such as the new component router
7656 forEach(options, function(val, key) {
7657 if (key.charAt(0) === '$') {
7658 factory[key] = val;
7659 controller[key] = val;
7660 }
7661 });
7662
7663 factory.$inject = ['$injector'];
7664
7665 return this.directive(name, factory);
7666 };
7667
7668
7669 /**
7670 * @ngdoc method
7671 * @name $compileProvider#aHrefSanitizationWhitelist
7672 * @kind function
7673 *
7674 * @description
7675 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7676 * urls during a[href] sanitization.
7677 *
7678 * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7679 *
7680 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7681 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7682 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7683 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7684 *
7685 * @param {RegExp=} regexp New regexp to whitelist urls with.
7686 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7687 * chaining otherwise.
7688 */
7689 this.aHrefSanitizationWhitelist = function(regexp) {
7690 if (isDefined(regexp)) {
7691 $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7692 return this;
7693 } else {
7694 return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7695 }
7696 };
7697
7698
7699 /**
7700 * @ngdoc method
7701 * @name $compileProvider#imgSrcSanitizationWhitelist
7702 * @kind function
7703 *
7704 * @description
7705 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7706 * urls during img[src] sanitization.
7707 *
7708 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7709 *
7710 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7711 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7712 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7713 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7714 *
7715 * @param {RegExp=} regexp New regexp to whitelist urls with.
7716 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7717 * chaining otherwise.
7718 */
7719 this.imgSrcSanitizationWhitelist = function(regexp) {
7720 if (isDefined(regexp)) {
7721 $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7722 return this;
7723 } else {
7724 return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7725 }
7726 };
7727
7728 /**
7729 * @ngdoc method
7730 * @name $compileProvider#debugInfoEnabled
7731 *
7732 * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7733 * current debugInfoEnabled state
7734 * @returns {*} current value if used as getter or itself (chaining) if used as setter
7735 *
7736 * @kind function
7737 *
7738 * @description
7739 * Call this method to enable/disable various debug runtime information in the compiler such as adding
7740 * binding information and a reference to the current scope on to DOM elements.
7741 * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7742 * * `ng-binding` CSS class
7743 * * `$binding` data property containing an array of the binding expressions
7744 *
7745 * You may want to disable this in production for a significant performance boost. See
7746 * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7747 *
7748 * The default value is true.
7749 */
7750 var debugInfoEnabled = true;
7751 this.debugInfoEnabled = function(enabled) {
7752 if (isDefined(enabled)) {
7753 debugInfoEnabled = enabled;
7754 return this;
7755 }
7756 return debugInfoEnabled;
7757 };
7758
7759
7760 var TTL = 10;
7761 /**
7762 * @ngdoc method
7763 * @name $compileProvider#onChangesTtl
7764 * @description
7765 *
7766 * Sets the number of times `$onChanges` hooks can trigger new changes before giving up and
7767 * assuming that the model is unstable.
7768 *
7769 * The current default is 10 iterations.
7770 *
7771 * In complex applications it's possible that dependencies between `$onChanges` hooks and bindings will result
7772 * in several iterations of calls to these hooks. However if an application needs more than the default 10
7773 * iterations to stabilize then you should investigate what is causing the model to continuously change during
7774 * the `$onChanges` hook execution.
7775 *
7776 * Increasing the TTL could have performance implications, so you should not change it without proper justification.
7777 *
7778 * @param {number} limit The number of `$onChanges` hook iterations.
7779 * @returns {number|object} the current limit (or `this` if called as a setter for chaining)
7780 */
7781 this.onChangesTtl = function(value) {
7782 if (arguments.length) {
7783 TTL = value;
7784 return this;
7785 }
7786 return TTL;
7787 };
7788
7789 this.$get = [
7790 '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7791 '$controller', '$rootScope', '$sce', '$animate', '$$sanitizeUri',
7792 function($injector, $interpolate, $exceptionHandler, $templateRequest, $parse,
7793 $controller, $rootScope, $sce, $animate, $$sanitizeUri) {
7794
7795 var SIMPLE_ATTR_NAME = /^\w/;
7796 var specialAttrHolder = document.createElement('div');
7797
7798
7799
7800 var onChangesTtl = TTL;
7801 // The onChanges hooks should all be run together in a single digest
7802 // When changes occur, the call to trigger their hooks will be added to this queue
7803 var onChangesQueue;
7804
7805 // This function is called in a $$postDigest to trigger all the onChanges hooks in a single digest
7806 function flushOnChangesQueue() {
7807 try {
7808 if (!(--onChangesTtl)) {
7809 // We have hit the TTL limit so reset everything
7810 onChangesQueue = undefined;
7811 throw $compileMinErr('infchng', '{0} $onChanges() iterations reached. Aborting!\n', TTL);
7812 }
7813 // We must run this hook in an apply since the $$postDigest runs outside apply
7814 $rootScope.$apply(function() {
7815 for (var i = 0, ii = onChangesQueue.length; i < ii; ++i) {
7816 onChangesQueue[i]();
7817 }
7818 // Reset the queue to trigger a new schedule next time there is a change
7819 onChangesQueue = undefined;
7820 });
7821 } finally {
7822 onChangesTtl++;
7823 }
7824 }
7825
7826
7827 function Attributes(element, attributesToCopy) {
7828 if (attributesToCopy) {
7829 var keys = Object.keys(attributesToCopy);
7830 var i, l, key;
7831
7832 for (i = 0, l = keys.length; i < l; i++) {
7833 key = keys[i];
7834 this[key] = attributesToCopy[key];
7835 }
7836 } else {
7837 this.$attr = {};
7838 }
7839
7840 this.$$element = element;
7841 }
7842
7843 Attributes.prototype = {
7844 /**
7845 * @ngdoc method
7846 * @name $compile.directive.Attributes#$normalize
7847 * @kind function
7848 *
7849 * @description
7850 * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7851 * `data-`) to its normalized, camelCase form.
7852 *
7853 * Also there is special case for Moz prefix starting with upper case letter.
7854 *
7855 * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7856 *
7857 * @param {string} name Name to normalize
7858 */
7859 $normalize: directiveNormalize,
7860
7861
7862 /**
7863 * @ngdoc method
7864 * @name $compile.directive.Attributes#$addClass
7865 * @kind function
7866 *
7867 * @description
7868 * Adds the CSS class value specified by the classVal parameter to the element. If animations
7869 * are enabled then an animation will be triggered for the class addition.
7870 *
7871 * @param {string} classVal The className value that will be added to the element
7872 */
7873 $addClass: function(classVal) {
7874 if (classVal && classVal.length > 0) {
7875 $animate.addClass(this.$$element, classVal);
7876 }
7877 },
7878
7879 /**
7880 * @ngdoc method
7881 * @name $compile.directive.Attributes#$removeClass
7882 * @kind function
7883 *
7884 * @description
7885 * Removes the CSS class value specified by the classVal parameter from the element. If
7886 * animations are enabled then an animation will be triggered for the class removal.
7887 *
7888 * @param {string} classVal The className value that will be removed from the element
7889 */
7890 $removeClass: function(classVal) {
7891 if (classVal && classVal.length > 0) {
7892 $animate.removeClass(this.$$element, classVal);
7893 }
7894 },
7895
7896 /**
7897 * @ngdoc method
7898 * @name $compile.directive.Attributes#$updateClass
7899 * @kind function
7900 *
7901 * @description
7902 * Adds and removes the appropriate CSS class values to the element based on the difference
7903 * between the new and old CSS class values (specified as newClasses and oldClasses).
7904 *
7905 * @param {string} newClasses The current CSS className value
7906 * @param {string} oldClasses The former CSS className value
7907 */
7908 $updateClass: function(newClasses, oldClasses) {
7909 var toAdd = tokenDifference(newClasses, oldClasses);
7910 if (toAdd && toAdd.length) {
7911 $animate.addClass(this.$$element, toAdd);
7912 }
7913
7914 var toRemove = tokenDifference(oldClasses, newClasses);
7915 if (toRemove && toRemove.length) {
7916 $animate.removeClass(this.$$element, toRemove);
7917 }
7918 },
7919
7920 /**
7921 * Set a normalized attribute on the element in a way such that all directives
7922 * can share the attribute. This function properly handles boolean attributes.
7923 * @param {string} key Normalized key. (ie ngAttribute)
7924 * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7925 * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7926 * Defaults to true.
7927 * @param {string=} attrName Optional none normalized name. Defaults to key.
7928 */
7929 $set: function(key, value, writeAttr, attrName) {
7930 // TODO: decide whether or not to throw an error if "class"
7931 //is set through this function since it may cause $updateClass to
7932 //become unstable.
7933
7934 var node = this.$$element[0],
7935 booleanKey = getBooleanAttrName(node, key),
7936 aliasedKey = getAliasedAttrName(key),
7937 observer = key,
7938 nodeName;
7939
7940 if (booleanKey) {
7941 this.$$element.prop(key, value);
7942 attrName = booleanKey;
7943 } else if (aliasedKey) {
7944 this[aliasedKey] = value;
7945 observer = aliasedKey;
7946 }
7947
7948 this[key] = value;
7949
7950 // translate normalized key to actual key
7951 if (attrName) {
7952 this.$attr[key] = attrName;
7953 } else {
7954 attrName = this.$attr[key];
7955 if (!attrName) {
7956 this.$attr[key] = attrName = snake_case(key, '-');
7957 }
7958 }
7959
7960 nodeName = nodeName_(this.$$element);
7961
7962 if ((nodeName === 'a' && (key === 'href' || key === 'xlinkHref')) ||
7963 (nodeName === 'img' && key === 'src')) {
7964 // sanitize a[href] and img[src] values
7965 this[key] = value = $$sanitizeUri(value, key === 'src');
7966 } else if (nodeName === 'img' && key === 'srcset') {
7967 // sanitize img[srcset] values
7968 var result = "";
7969
7970 // first check if there are spaces because it's not the same pattern
7971 var trimmedSrcset = trim(value);
7972 // ( 999x ,| 999w ,| ,|, )
7973 var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7974 var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7975
7976 // split srcset into tuple of uri and descriptor except for the last item
7977 var rawUris = trimmedSrcset.split(pattern);
7978
7979 // for each tuples
7980 var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7981 for (var i = 0; i < nbrUrisWith2parts; i++) {
7982 var innerIdx = i * 2;
7983 // sanitize the uri
7984 result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7985 // add the descriptor
7986 result += (" " + trim(rawUris[innerIdx + 1]));
7987 }
7988
7989 // split the last item into uri and descriptor
7990 var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7991
7992 // sanitize the last uri
7993 result += $$sanitizeUri(trim(lastTuple[0]), true);
7994
7995 // and add the last descriptor if any
7996 if (lastTuple.length === 2) {
7997 result += (" " + trim(lastTuple[1]));
7998 }
7999 this[key] = value = result;
8000 }
8001
8002 if (writeAttr !== false) {
8003 if (value === null || isUndefined(value)) {
8004 this.$$element.removeAttr(attrName);
8005 } else {
8006 if (SIMPLE_ATTR_NAME.test(attrName)) {
8007 this.$$element.attr(attrName, value);
8008 } else {
8009 setSpecialAttr(this.$$element[0], attrName, value);
8010 }
8011 }
8012 }
8013
8014 // fire observers
8015 var $$observers = this.$$observers;
8016 $$observers && forEach($$observers[observer], function(fn) {
8017 try {
8018 fn(value);
8019 } catch (e) {
8020 $exceptionHandler(e);
8021 }
8022 });
8023 },
8024
8025
8026 /**
8027 * @ngdoc method
8028 * @name $compile.directive.Attributes#$observe
8029 * @kind function
8030 *
8031 * @description
8032 * Observes an interpolated attribute.
8033 *
8034 * The observer function will be invoked once during the next `$digest` following
8035 * compilation. The observer is then invoked whenever the interpolated value
8036 * changes.
8037 *
8038 * @param {string} key Normalized key. (ie ngAttribute) .
8039 * @param {function(interpolatedValue)} fn Function that will be called whenever
8040 the interpolated value of the attribute changes.
8041 * See the {@link guide/interpolation#how-text-and-attribute-bindings-work Interpolation
8042 * guide} for more info.
8043 * @returns {function()} Returns a deregistration function for this observer.
8044 */
8045 $observe: function(key, fn) {
8046 var attrs = this,
8047 $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
8048 listeners = ($$observers[key] || ($$observers[key] = []));
8049
8050 listeners.push(fn);
8051 $rootScope.$evalAsync(function() {
8052 if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
8053 // no one registered attribute interpolation function, so lets call it manually
8054 fn(attrs[key]);
8055 }
8056 });
8057
8058 return function() {
8059 arrayRemove(listeners, fn);
8060 };
8061 }
8062 };
8063
8064 function setSpecialAttr(element, attrName, value) {
8065 // Attributes names that do not start with letters (such as `(click)`) cannot be set using `setAttribute`
8066 // so we have to jump through some hoops to get such an attribute
8067 // https://github.com/angular/angular.js/pull/13318
8068 specialAttrHolder.innerHTML = "<span " + attrName + ">";
8069 var attributes = specialAttrHolder.firstChild.attributes;
8070 var attribute = attributes[0];
8071 // We have to remove the attribute from its container element before we can add it to the destination element
8072 attributes.removeNamedItem(attribute.name);
8073 attribute.value = value;
8074 element.attributes.setNamedItem(attribute);
8075 }
8076
8077 function safeAddClass($element, className) {
8078 try {
8079 $element.addClass(className);
8080 } catch (e) {
8081 // ignore, since it means that we are trying to set class on
8082 // SVG element, where class name is read-only.
8083 }
8084 }
8085
8086
8087 var startSymbol = $interpolate.startSymbol(),
8088 endSymbol = $interpolate.endSymbol(),
8089 denormalizeTemplate = (startSymbol == '{{' && endSymbol == '}}')
8090 ? identity
8091 : function denormalizeTemplate(template) {
8092 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
8093 },
8094 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
8095 var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
8096
8097 compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
8098 var bindings = $element.data('$binding') || [];
8099
8100 if (isArray(binding)) {
8101 bindings = bindings.concat(binding);
8102 } else {
8103 bindings.push(binding);
8104 }
8105
8106 $element.data('$binding', bindings);
8107 } : noop;
8108
8109 compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
8110 safeAddClass($element, 'ng-binding');
8111 } : noop;
8112
8113 compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
8114 var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
8115 $element.data(dataName, scope);
8116 } : noop;
8117
8118 compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
8119 safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
8120 } : noop;
8121
8122 compile.$$createComment = function(directiveName, comment) {
8123 var content = '';
8124 if (debugInfoEnabled) {
8125 content = ' ' + (directiveName || '') + ': ' + (comment || '') + ' ';
8126 }
8127 return document.createComment(content);
8128 };
8129
8130 return compile;
8131
8132 //================================
8133
8134 function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
8135 previousCompileContext) {
8136 if (!($compileNodes instanceof jqLite)) {
8137 // jquery always rewraps, whereas we need to preserve the original selector so that we can
8138 // modify it.
8139 $compileNodes = jqLite($compileNodes);
8140 }
8141
8142 var NOT_EMPTY = /\S+/;
8143
8144 // We can not compile top level text elements since text nodes can be merged and we will
8145 // not be able to attach scope data to them, so we will wrap them in <span>
8146 for (var i = 0, len = $compileNodes.length; i < len; i++) {
8147 var domNode = $compileNodes[i];
8148
8149 if (domNode.nodeType === NODE_TYPE_TEXT && domNode.nodeValue.match(NOT_EMPTY) /* non-empty */) {
8150 jqLiteWrapNode(domNode, $compileNodes[i] = document.createElement('span'));
8151 }
8152 }
8153
8154 var compositeLinkFn =
8155 compileNodes($compileNodes, transcludeFn, $compileNodes,
8156 maxPriority, ignoreDirective, previousCompileContext);
8157 compile.$$addScopeClass($compileNodes);
8158 var namespace = null;
8159 return function publicLinkFn(scope, cloneConnectFn, options) {
8160 assertArg(scope, 'scope');
8161
8162 if (previousCompileContext && previousCompileContext.needsNewScope) {
8163 // A parent directive did a replace and a directive on this element asked
8164 // for transclusion, which caused us to lose a layer of element on which
8165 // we could hold the new transclusion scope, so we will create it manually
8166 // here.
8167 scope = scope.$parent.$new();
8168 }
8169
8170 options = options || {};
8171 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
8172 transcludeControllers = options.transcludeControllers,
8173 futureParentElement = options.futureParentElement;
8174
8175 // When `parentBoundTranscludeFn` is passed, it is a
8176 // `controllersBoundTransclude` function (it was previously passed
8177 // as `transclude` to directive.link) so we must unwrap it to get
8178 // its `boundTranscludeFn`
8179 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
8180 parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
8181 }
8182
8183 if (!namespace) {
8184 namespace = detectNamespaceForChildElements(futureParentElement);
8185 }
8186 var $linkNode;
8187 if (namespace !== 'html') {
8188 // When using a directive with replace:true and templateUrl the $compileNodes
8189 // (or a child element inside of them)
8190 // might change, so we need to recreate the namespace adapted compileNodes
8191 // for call to the link function.
8192 // Note: This will already clone the nodes...
8193 $linkNode = jqLite(
8194 wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
8195 );
8196 } else if (cloneConnectFn) {
8197 // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
8198 // and sometimes changes the structure of the DOM.
8199 $linkNode = JQLitePrototype.clone.call($compileNodes);
8200 } else {
8201 $linkNode = $compileNodes;
8202 }
8203
8204 if (transcludeControllers) {
8205 for (var controllerName in transcludeControllers) {
8206 $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
8207 }
8208 }
8209
8210 compile.$$addScopeInfo($linkNode, scope);
8211
8212 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
8213 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
8214 return $linkNode;
8215 };
8216 }
8217
8218 function detectNamespaceForChildElements(parentElement) {
8219 // TODO: Make this detect MathML as well...
8220 var node = parentElement && parentElement[0];
8221 if (!node) {
8222 return 'html';
8223 } else {
8224 return nodeName_(node) !== 'foreignobject' && toString.call(node).match(/SVG/) ? 'svg' : 'html';
8225 }
8226 }
8227
8228 /**
8229 * Compile function matches each node in nodeList against the directives. Once all directives
8230 * for a particular node are collected their compile functions are executed. The compile
8231 * functions return values - the linking functions - are combined into a composite linking
8232 * function, which is the a linking function for the node.
8233 *
8234 * @param {NodeList} nodeList an array of nodes or NodeList to compile
8235 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8236 * scope argument is auto-generated to the new child of the transcluded parent scope.
8237 * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
8238 * the rootElement must be set the jqLite collection of the compile root. This is
8239 * needed so that the jqLite collection items can be replaced with widgets.
8240 * @param {number=} maxPriority Max directive priority.
8241 * @returns {Function} A composite linking function of all of the matched directives or null.
8242 */
8243 function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
8244 previousCompileContext) {
8245 var linkFns = [],
8246 attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
8247
8248 for (var i = 0; i < nodeList.length; i++) {
8249 attrs = new Attributes();
8250
8251 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
8252 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
8253 ignoreDirective);
8254
8255 nodeLinkFn = (directives.length)
8256 ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
8257 null, [], [], previousCompileContext)
8258 : null;
8259
8260 if (nodeLinkFn && nodeLinkFn.scope) {
8261 compile.$$addScopeClass(attrs.$$element);
8262 }
8263
8264 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
8265 !(childNodes = nodeList[i].childNodes) ||
8266 !childNodes.length)
8267 ? null
8268 : compileNodes(childNodes,
8269 nodeLinkFn ? (
8270 (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
8271 && nodeLinkFn.transclude) : transcludeFn);
8272
8273 if (nodeLinkFn || childLinkFn) {
8274 linkFns.push(i, nodeLinkFn, childLinkFn);
8275 linkFnFound = true;
8276 nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
8277 }
8278
8279 //use the previous context only for the first element in the virtual group
8280 previousCompileContext = null;
8281 }
8282
8283 // return a linking function if we have found anything, null otherwise
8284 return linkFnFound ? compositeLinkFn : null;
8285
8286 function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
8287 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
8288 var stableNodeList;
8289
8290
8291 if (nodeLinkFnFound) {
8292 // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
8293 // offsets don't get screwed up
8294 var nodeListLength = nodeList.length;
8295 stableNodeList = new Array(nodeListLength);
8296
8297 // create a sparse array by only copying the elements which have a linkFn
8298 for (i = 0; i < linkFns.length; i+=3) {
8299 idx = linkFns[i];
8300 stableNodeList[idx] = nodeList[idx];
8301 }
8302 } else {
8303 stableNodeList = nodeList;
8304 }
8305
8306 for (i = 0, ii = linkFns.length; i < ii;) {
8307 node = stableNodeList[linkFns[i++]];
8308 nodeLinkFn = linkFns[i++];
8309 childLinkFn = linkFns[i++];
8310
8311 if (nodeLinkFn) {
8312 if (nodeLinkFn.scope) {
8313 childScope = scope.$new();
8314 compile.$$addScopeInfo(jqLite(node), childScope);
8315 } else {
8316 childScope = scope;
8317 }
8318
8319 if (nodeLinkFn.transcludeOnThisElement) {
8320 childBoundTranscludeFn = createBoundTranscludeFn(
8321 scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
8322
8323 } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
8324 childBoundTranscludeFn = parentBoundTranscludeFn;
8325
8326 } else if (!parentBoundTranscludeFn && transcludeFn) {
8327 childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
8328
8329 } else {
8330 childBoundTranscludeFn = null;
8331 }
8332
8333 nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
8334
8335 } else if (childLinkFn) {
8336 childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
8337 }
8338 }
8339 }
8340 }
8341
8342 function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
8343 function boundTranscludeFn(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
8344
8345 if (!transcludedScope) {
8346 transcludedScope = scope.$new(false, containingScope);
8347 transcludedScope.$$transcluded = true;
8348 }
8349
8350 return transcludeFn(transcludedScope, cloneFn, {
8351 parentBoundTranscludeFn: previousBoundTranscludeFn,
8352 transcludeControllers: controllers,
8353 futureParentElement: futureParentElement
8354 });
8355 }
8356
8357 // We need to attach the transclusion slots onto the `boundTranscludeFn`
8358 // so that they are available inside the `controllersBoundTransclude` function
8359 var boundSlots = boundTranscludeFn.$$slots = createMap();
8360 for (var slotName in transcludeFn.$$slots) {
8361 if (transcludeFn.$$slots[slotName]) {
8362 boundSlots[slotName] = createBoundTranscludeFn(scope, transcludeFn.$$slots[slotName], previousBoundTranscludeFn);
8363 } else {
8364 boundSlots[slotName] = null;
8365 }
8366 }
8367
8368 return boundTranscludeFn;
8369 }
8370
8371 /**
8372 * Looks for directives on the given node and adds them to the directive collection which is
8373 * sorted.
8374 *
8375 * @param node Node to search.
8376 * @param directives An array to which the directives are added to. This array is sorted before
8377 * the function returns.
8378 * @param attrs The shared attrs object which is used to populate the normalized attributes.
8379 * @param {number=} maxPriority Max directive priority.
8380 */
8381 function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
8382 var nodeType = node.nodeType,
8383 attrsMap = attrs.$attr,
8384 match,
8385 className;
8386
8387 switch (nodeType) {
8388 case NODE_TYPE_ELEMENT: /* Element */
8389 // use the node name: <directive>
8390 addDirective(directives,
8391 directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
8392
8393 // iterate over the attributes
8394 for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
8395 j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
8396 var attrStartName = false;
8397 var attrEndName = false;
8398
8399 attr = nAttrs[j];
8400 name = attr.name;
8401 value = trim(attr.value);
8402
8403 // support ngAttr attribute binding
8404 ngAttrName = directiveNormalize(name);
8405 if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
8406 name = name.replace(PREFIX_REGEXP, '')
8407 .substr(8).replace(/_(.)/g, function(match, letter) {
8408 return letter.toUpperCase();
8409 });
8410 }
8411
8412 var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
8413 if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
8414 attrStartName = name;
8415 attrEndName = name.substr(0, name.length - 5) + 'end';
8416 name = name.substr(0, name.length - 6);
8417 }
8418
8419 nName = directiveNormalize(name.toLowerCase());
8420 attrsMap[nName] = name;
8421 if (isNgAttr || !attrs.hasOwnProperty(nName)) {
8422 attrs[nName] = value;
8423 if (getBooleanAttrName(node, nName)) {
8424 attrs[nName] = true; // presence means true
8425 }
8426 }
8427 addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
8428 addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
8429 attrEndName);
8430 }
8431
8432 // use class as directive
8433 className = node.className;
8434 if (isObject(className)) {
8435 // Maybe SVGAnimatedString
8436 className = className.animVal;
8437 }
8438 if (isString(className) && className !== '') {
8439 while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
8440 nName = directiveNormalize(match[2]);
8441 if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
8442 attrs[nName] = trim(match[3]);
8443 }
8444 className = className.substr(match.index + match[0].length);
8445 }
8446 }
8447 break;
8448 case NODE_TYPE_TEXT: /* Text Node */
8449 if (msie === 11) {
8450 // Workaround for #11781
8451 while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
8452 node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
8453 node.parentNode.removeChild(node.nextSibling);
8454 }
8455 }
8456 addTextInterpolateDirective(directives, node.nodeValue);
8457 break;
8458 case NODE_TYPE_COMMENT: /* Comment */
8459 try {
8460 match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
8461 if (match) {
8462 nName = directiveNormalize(match[1]);
8463 if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
8464 attrs[nName] = trim(match[2]);
8465 }
8466 }
8467 } catch (e) {
8468 // turns out that under some circumstances IE9 throws errors when one attempts to read
8469 // comment's node value.
8470 // Just ignore it and continue. (Can't seem to reproduce in test case.)
8471 }
8472 break;
8473 }
8474
8475 directives.sort(byPriority);
8476 return directives;
8477 }
8478
8479 /**
8480 * Given a node with an directive-start it collects all of the siblings until it finds
8481 * directive-end.
8482 * @param node
8483 * @param attrStart
8484 * @param attrEnd
8485 * @returns {*}
8486 */
8487 function groupScan(node, attrStart, attrEnd) {
8488 var nodes = [];
8489 var depth = 0;
8490 if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
8491 do {
8492 if (!node) {
8493 throw $compileMinErr('uterdir',
8494 "Unterminated attribute, found '{0}' but no matching '{1}' found.",
8495 attrStart, attrEnd);
8496 }
8497 if (node.nodeType == NODE_TYPE_ELEMENT) {
8498 if (node.hasAttribute(attrStart)) depth++;
8499 if (node.hasAttribute(attrEnd)) depth--;
8500 }
8501 nodes.push(node);
8502 node = node.nextSibling;
8503 } while (depth > 0);
8504 } else {
8505 nodes.push(node);
8506 }
8507
8508 return jqLite(nodes);
8509 }
8510
8511 /**
8512 * Wrapper for linking function which converts normal linking function into a grouped
8513 * linking function.
8514 * @param linkFn
8515 * @param attrStart
8516 * @param attrEnd
8517 * @returns {Function}
8518 */
8519 function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
8520 return function groupedElementsLink(scope, element, attrs, controllers, transcludeFn) {
8521 element = groupScan(element[0], attrStart, attrEnd);
8522 return linkFn(scope, element, attrs, controllers, transcludeFn);
8523 };
8524 }
8525
8526 /**
8527 * A function generator that is used to support both eager and lazy compilation
8528 * linking function.
8529 * @param eager
8530 * @param $compileNodes
8531 * @param transcludeFn
8532 * @param maxPriority
8533 * @param ignoreDirective
8534 * @param previousCompileContext
8535 * @returns {Function}
8536 */
8537 function compilationGenerator(eager, $compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext) {
8538 var compiled;
8539
8540 if (eager) {
8541 return compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8542 }
8543 return function lazyCompilation() {
8544 if (!compiled) {
8545 compiled = compile($compileNodes, transcludeFn, maxPriority, ignoreDirective, previousCompileContext);
8546
8547 // Null out all of these references in order to make them eligible for garbage collection
8548 // since this is a potentially long lived closure
8549 $compileNodes = transcludeFn = previousCompileContext = null;
8550 }
8551 return compiled.apply(this, arguments);
8552 };
8553 }
8554
8555 /**
8556 * Once the directives have been collected, their compile functions are executed. This method
8557 * is responsible for inlining directive templates as well as terminating the application
8558 * of the directives if the terminal directive has been reached.
8559 *
8560 * @param {Array} directives Array of collected directives to execute their compile function.
8561 * this needs to be pre-sorted by priority order.
8562 * @param {Node} compileNode The raw DOM node to apply the compile functions to
8563 * @param {Object} templateAttrs The shared attribute function
8564 * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
8565 * scope argument is auto-generated to the new
8566 * child of the transcluded parent scope.
8567 * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8568 * argument has the root jqLite array so that we can replace nodes
8569 * on it.
8570 * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8571 * compiling the transclusion.
8572 * @param {Array.<Function>} preLinkFns
8573 * @param {Array.<Function>} postLinkFns
8574 * @param {Object} previousCompileContext Context used for previous compilation of the current
8575 * node
8576 * @returns {Function} linkFn
8577 */
8578 function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8579 jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8580 previousCompileContext) {
8581 previousCompileContext = previousCompileContext || {};
8582
8583 var terminalPriority = -Number.MAX_VALUE,
8584 newScopeDirective = previousCompileContext.newScopeDirective,
8585 controllerDirectives = previousCompileContext.controllerDirectives,
8586 newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8587 templateDirective = previousCompileContext.templateDirective,
8588 nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8589 hasTranscludeDirective = false,
8590 hasTemplate = false,
8591 hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8592 $compileNode = templateAttrs.$$element = jqLite(compileNode),
8593 directive,
8594 directiveName,
8595 $template,
8596 replaceDirective = originalReplaceDirective,
8597 childTranscludeFn = transcludeFn,
8598 linkFn,
8599 didScanForMultipleTransclusion = false,
8600 mightHaveMultipleTransclusionError = false,
8601 directiveValue;
8602
8603 // executes all directives on the current element
8604 for (var i = 0, ii = directives.length; i < ii; i++) {
8605 directive = directives[i];
8606 var attrStart = directive.$$start;
8607 var attrEnd = directive.$$end;
8608
8609 // collect multiblock sections
8610 if (attrStart) {
8611 $compileNode = groupScan(compileNode, attrStart, attrEnd);
8612 }
8613 $template = undefined;
8614
8615 if (terminalPriority > directive.priority) {
8616 break; // prevent further processing of directives
8617 }
8618
8619 if (directiveValue = directive.scope) {
8620
8621 // skip the check for directives with async templates, we'll check the derived sync
8622 // directive when the template arrives
8623 if (!directive.templateUrl) {
8624 if (isObject(directiveValue)) {
8625 // This directive is trying to add an isolated scope.
8626 // Check that there is no scope of any kind already
8627 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8628 directive, $compileNode);
8629 newIsolateScopeDirective = directive;
8630 } else {
8631 // This directive is trying to add a child scope.
8632 // Check that there is no isolated scope already
8633 assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8634 $compileNode);
8635 }
8636 }
8637
8638 newScopeDirective = newScopeDirective || directive;
8639 }
8640
8641 directiveName = directive.name;
8642
8643 // If we encounter a condition that can result in transclusion on the directive,
8644 // then scan ahead in the remaining directives for others that may cause a multiple
8645 // transclusion error to be thrown during the compilation process. If a matching directive
8646 // is found, then we know that when we encounter a transcluded directive, we need to eagerly
8647 // compile the `transclude` function rather than doing it lazily in order to throw
8648 // exceptions at the correct time
8649 if (!didScanForMultipleTransclusion && ((directive.replace && (directive.templateUrl || directive.template))
8650 || (directive.transclude && !directive.$$tlb))) {
8651 var candidateDirective;
8652
8653 for (var scanningIndex = i + 1; candidateDirective = directives[scanningIndex++];) {
8654 if ((candidateDirective.transclude && !candidateDirective.$$tlb)
8655 || (candidateDirective.replace && (candidateDirective.templateUrl || candidateDirective.template))) {
8656 mightHaveMultipleTransclusionError = true;
8657 break;
8658 }
8659 }
8660
8661 didScanForMultipleTransclusion = true;
8662 }
8663
8664 if (!directive.templateUrl && directive.controller) {
8665 directiveValue = directive.controller;
8666 controllerDirectives = controllerDirectives || createMap();
8667 assertNoDuplicate("'" + directiveName + "' controller",
8668 controllerDirectives[directiveName], directive, $compileNode);
8669 controllerDirectives[directiveName] = directive;
8670 }
8671
8672 if (directiveValue = directive.transclude) {
8673 hasTranscludeDirective = true;
8674
8675 // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8676 // This option should only be used by directives that know how to safely handle element transclusion,
8677 // where the transcluded nodes are added or replaced after linking.
8678 if (!directive.$$tlb) {
8679 assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8680 nonTlbTranscludeDirective = directive;
8681 }
8682
8683 if (directiveValue == 'element') {
8684 hasElementTranscludeDirective = true;
8685 terminalPriority = directive.priority;
8686 $template = $compileNode;
8687 $compileNode = templateAttrs.$$element =
8688 jqLite(compile.$$createComment(directiveName, templateAttrs[directiveName]));
8689 compileNode = $compileNode[0];
8690 replaceWith(jqCollection, sliceArgs($template), compileNode);
8691
8692 // Support: Chrome < 50
8693 // https://github.com/angular/angular.js/issues/14041
8694
8695 // In the versions of V8 prior to Chrome 50, the document fragment that is created
8696 // in the `replaceWith` function is improperly garbage collected despite still
8697 // being referenced by the `parentNode` property of all of the child nodes. By adding
8698 // a reference to the fragment via a different property, we can avoid that incorrect
8699 // behavior.
8700 // TODO: remove this line after Chrome 50 has been released
8701 $template[0].$$parentNode = $template[0].parentNode;
8702
8703 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, terminalPriority,
8704 replaceDirective && replaceDirective.name, {
8705 // Don't pass in:
8706 // - controllerDirectives - otherwise we'll create duplicates controllers
8707 // - newIsolateScopeDirective or templateDirective - combining templates with
8708 // element transclusion doesn't make sense.
8709 //
8710 // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8711 // on the same element more than once.
8712 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8713 });
8714 } else {
8715
8716 var slots = createMap();
8717
8718 $template = jqLite(jqLiteClone(compileNode)).contents();
8719
8720 if (isObject(directiveValue)) {
8721
8722 // We have transclusion slots,
8723 // collect them up, compile them and store their transclusion functions
8724 $template = [];
8725
8726 var slotMap = createMap();
8727 var filledSlots = createMap();
8728
8729 // Parse the element selectors
8730 forEach(directiveValue, function(elementSelector, slotName) {
8731 // If an element selector starts with a ? then it is optional
8732 var optional = (elementSelector.charAt(0) === '?');
8733 elementSelector = optional ? elementSelector.substring(1) : elementSelector;
8734
8735 slotMap[elementSelector] = slotName;
8736
8737 // We explicitly assign `null` since this implies that a slot was defined but not filled.
8738 // Later when calling boundTransclusion functions with a slot name we only error if the
8739 // slot is `undefined`
8740 slots[slotName] = null;
8741
8742 // filledSlots contains `true` for all slots that are either optional or have been
8743 // filled. This is used to check that we have not missed any required slots
8744 filledSlots[slotName] = optional;
8745 });
8746
8747 // Add the matching elements into their slot
8748 forEach($compileNode.contents(), function(node) {
8749 var slotName = slotMap[directiveNormalize(nodeName_(node))];
8750 if (slotName) {
8751 filledSlots[slotName] = true;
8752 slots[slotName] = slots[slotName] || [];
8753 slots[slotName].push(node);
8754 } else {
8755 $template.push(node);
8756 }
8757 });
8758
8759 // Check for required slots that were not filled
8760 forEach(filledSlots, function(filled, slotName) {
8761 if (!filled) {
8762 throw $compileMinErr('reqslot', 'Required transclusion slot `{0}` was not filled.', slotName);
8763 }
8764 });
8765
8766 for (var slotName in slots) {
8767 if (slots[slotName]) {
8768 // Only define a transclusion function if the slot was filled
8769 slots[slotName] = compilationGenerator(mightHaveMultipleTransclusionError, slots[slotName], transcludeFn);
8770 }
8771 }
8772 }
8773
8774 $compileNode.empty(); // clear contents
8775 childTranscludeFn = compilationGenerator(mightHaveMultipleTransclusionError, $template, transcludeFn, undefined,
8776 undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8777 childTranscludeFn.$$slots = slots;
8778 }
8779 }
8780
8781 if (directive.template) {
8782 hasTemplate = true;
8783 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8784 templateDirective = directive;
8785
8786 directiveValue = (isFunction(directive.template))
8787 ? directive.template($compileNode, templateAttrs)
8788 : directive.template;
8789
8790 directiveValue = denormalizeTemplate(directiveValue);
8791
8792 if (directive.replace) {
8793 replaceDirective = directive;
8794 if (jqLiteIsTextNode(directiveValue)) {
8795 $template = [];
8796 } else {
8797 $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8798 }
8799 compileNode = $template[0];
8800
8801 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8802 throw $compileMinErr('tplrt',
8803 "Template for directive '{0}' must have exactly one root element. {1}",
8804 directiveName, '');
8805 }
8806
8807 replaceWith(jqCollection, $compileNode, compileNode);
8808
8809 var newTemplateAttrs = {$attr: {}};
8810
8811 // combine directives from the original node and from the template:
8812 // - take the array of directives for this element
8813 // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8814 // - collect directives from the template and sort them by priority
8815 // - combine directives as: processed + template + unprocessed
8816 var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8817 var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8818
8819 if (newIsolateScopeDirective || newScopeDirective) {
8820 // The original directive caused the current element to be replaced but this element
8821 // also needs to have a new scope, so we need to tell the template directives
8822 // that they would need to get their scope from further up, if they require transclusion
8823 markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8824 }
8825 directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8826 mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8827
8828 ii = directives.length;
8829 } else {
8830 $compileNode.html(directiveValue);
8831 }
8832 }
8833
8834 if (directive.templateUrl) {
8835 hasTemplate = true;
8836 assertNoDuplicate('template', templateDirective, directive, $compileNode);
8837 templateDirective = directive;
8838
8839 if (directive.replace) {
8840 replaceDirective = directive;
8841 }
8842
8843 nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8844 templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8845 controllerDirectives: controllerDirectives,
8846 newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8847 newIsolateScopeDirective: newIsolateScopeDirective,
8848 templateDirective: templateDirective,
8849 nonTlbTranscludeDirective: nonTlbTranscludeDirective
8850 });
8851 ii = directives.length;
8852 } else if (directive.compile) {
8853 try {
8854 linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8855 if (isFunction(linkFn)) {
8856 addLinkFns(null, linkFn, attrStart, attrEnd);
8857 } else if (linkFn) {
8858 addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8859 }
8860 } catch (e) {
8861 $exceptionHandler(e, startingTag($compileNode));
8862 }
8863 }
8864
8865 if (directive.terminal) {
8866 nodeLinkFn.terminal = true;
8867 terminalPriority = Math.max(terminalPriority, directive.priority);
8868 }
8869
8870 }
8871
8872 nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8873 nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8874 nodeLinkFn.templateOnThisElement = hasTemplate;
8875 nodeLinkFn.transclude = childTranscludeFn;
8876
8877 previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8878
8879 // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8880 return nodeLinkFn;
8881
8882 ////////////////////
8883
8884 function addLinkFns(pre, post, attrStart, attrEnd) {
8885 if (pre) {
8886 if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8887 pre.require = directive.require;
8888 pre.directiveName = directiveName;
8889 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8890 pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8891 }
8892 preLinkFns.push(pre);
8893 }
8894 if (post) {
8895 if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8896 post.require = directive.require;
8897 post.directiveName = directiveName;
8898 if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8899 post = cloneAndAnnotateFn(post, {isolateScope: true});
8900 }
8901 postLinkFns.push(post);
8902 }
8903 }
8904
8905 function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8906 var i, ii, linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8907 attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8908
8909 if (compileNode === linkNode) {
8910 attrs = templateAttrs;
8911 $element = templateAttrs.$$element;
8912 } else {
8913 $element = jqLite(linkNode);
8914 attrs = new Attributes($element, templateAttrs);
8915 }
8916
8917 controllerScope = scope;
8918 if (newIsolateScopeDirective) {
8919 isolateScope = scope.$new(true);
8920 } else if (newScopeDirective) {
8921 controllerScope = scope.$parent;
8922 }
8923
8924 if (boundTranscludeFn) {
8925 // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8926 // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8927 transcludeFn = controllersBoundTransclude;
8928 transcludeFn.$$boundTransclude = boundTranscludeFn;
8929 // expose the slots on the `$transclude` function
8930 transcludeFn.isSlotFilled = function(slotName) {
8931 return !!boundTranscludeFn.$$slots[slotName];
8932 };
8933 }
8934
8935 if (controllerDirectives) {
8936 elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective);
8937 }
8938
8939 if (newIsolateScopeDirective) {
8940 // Initialize isolate scope bindings for new isolate scope directive.
8941 compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8942 templateDirective === newIsolateScopeDirective.$$originalDirective)));
8943 compile.$$addScopeClass($element, true);
8944 isolateScope.$$isolateBindings =
8945 newIsolateScopeDirective.$$isolateBindings;
8946 removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8947 isolateScope.$$isolateBindings,
8948 newIsolateScopeDirective);
8949 if (removeScopeBindingWatches) {
8950 isolateScope.$on('$destroy', removeScopeBindingWatches);
8951 }
8952 }
8953
8954 // Initialize bindToController bindings
8955 for (var name in elementControllers) {
8956 var controllerDirective = controllerDirectives[name];
8957 var controller = elementControllers[name];
8958 var bindings = controllerDirective.$$bindings.bindToController;
8959
8960 if (controller.identifier && bindings) {
8961 removeControllerBindingWatches =
8962 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8963 }
8964
8965 var controllerResult = controller();
8966 if (controllerResult !== controller.instance) {
8967 // If the controller constructor has a return value, overwrite the instance
8968 // from setupControllers
8969 controller.instance = controllerResult;
8970 $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8971 removeControllerBindingWatches && removeControllerBindingWatches();
8972 removeControllerBindingWatches =
8973 initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8974 }
8975 }
8976
8977 // Bind the required controllers to the controller, if `require` is an object and `bindToController` is truthy
8978 forEach(controllerDirectives, function(controllerDirective, name) {
8979 var require = controllerDirective.require;
8980 if (controllerDirective.bindToController && !isArray(require) && isObject(require)) {
8981 extend(elementControllers[name].instance, getControllers(name, require, $element, elementControllers));
8982 }
8983 });
8984
8985 // Handle the init and destroy lifecycle hooks on all controllers that have them
8986 forEach(elementControllers, function(controller) {
8987 var controllerInstance = controller.instance;
8988 if (isFunction(controllerInstance.$onInit)) {
8989 controllerInstance.$onInit();
8990 }
8991 if (isFunction(controllerInstance.$onDestroy)) {
8992 controllerScope.$on('$destroy', function callOnDestroyHook() {
8993 controllerInstance.$onDestroy();
8994 });
8995 }
8996 });
8997
8998 // PRELINKING
8999 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
9000 linkFn = preLinkFns[i];
9001 invokeLinkFn(linkFn,
9002 linkFn.isolateScope ? isolateScope : scope,
9003 $element,
9004 attrs,
9005 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9006 transcludeFn
9007 );
9008 }
9009
9010 // RECURSION
9011 // We only pass the isolate scope, if the isolate directive has a template,
9012 // otherwise the child elements do not belong to the isolate directive.
9013 var scopeToChild = scope;
9014 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
9015 scopeToChild = isolateScope;
9016 }
9017 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
9018
9019 // POSTLINKING
9020 for (i = postLinkFns.length - 1; i >= 0; i--) {
9021 linkFn = postLinkFns[i];
9022 invokeLinkFn(linkFn,
9023 linkFn.isolateScope ? isolateScope : scope,
9024 $element,
9025 attrs,
9026 linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
9027 transcludeFn
9028 );
9029 }
9030
9031 // Trigger $postLink lifecycle hooks
9032 forEach(elementControllers, function(controller) {
9033 var controllerInstance = controller.instance;
9034 if (isFunction(controllerInstance.$postLink)) {
9035 controllerInstance.$postLink();
9036 }
9037 });
9038
9039 // This is the function that is injected as `$transclude`.
9040 // Note: all arguments are optional!
9041 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement, slotName) {
9042 var transcludeControllers;
9043 // No scope passed in:
9044 if (!isScope(scope)) {
9045 slotName = futureParentElement;
9046 futureParentElement = cloneAttachFn;
9047 cloneAttachFn = scope;
9048 scope = undefined;
9049 }
9050
9051 if (hasElementTranscludeDirective) {
9052 transcludeControllers = elementControllers;
9053 }
9054 if (!futureParentElement) {
9055 futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
9056 }
9057 if (slotName) {
9058 // slotTranscludeFn can be one of three things:
9059 // * a transclude function - a filled slot
9060 // * `null` - an optional slot that was not filled
9061 // * `undefined` - a slot that was not declared (i.e. invalid)
9062 var slotTranscludeFn = boundTranscludeFn.$$slots[slotName];
9063 if (slotTranscludeFn) {
9064 return slotTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9065 } else if (isUndefined(slotTranscludeFn)) {
9066 throw $compileMinErr('noslot',
9067 'No parent directive that requires a transclusion with slot name "{0}". ' +
9068 'Element: {1}',
9069 slotName, startingTag($element));
9070 }
9071 } else {
9072 return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
9073 }
9074 }
9075 }
9076 }
9077
9078 function getControllers(directiveName, require, $element, elementControllers) {
9079 var value;
9080
9081 if (isString(require)) {
9082 var match = require.match(REQUIRE_PREFIX_REGEXP);
9083 var name = require.substring(match[0].length);
9084 var inheritType = match[1] || match[3];
9085 var optional = match[2] === '?';
9086
9087 //If only parents then start at the parent element
9088 if (inheritType === '^^') {
9089 $element = $element.parent();
9090 //Otherwise attempt getting the controller from elementControllers in case
9091 //the element is transcluded (and has no data) and to avoid .data if possible
9092 } else {
9093 value = elementControllers && elementControllers[name];
9094 value = value && value.instance;
9095 }
9096
9097 if (!value) {
9098 var dataName = '$' + name + 'Controller';
9099 value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
9100 }
9101
9102 if (!value && !optional) {
9103 throw $compileMinErr('ctreq',
9104 "Controller '{0}', required by directive '{1}', can't be found!",
9105 name, directiveName);
9106 }
9107 } else if (isArray(require)) {
9108 value = [];
9109 for (var i = 0, ii = require.length; i < ii; i++) {
9110 value[i] = getControllers(directiveName, require[i], $element, elementControllers);
9111 }
9112 } else if (isObject(require)) {
9113 value = {};
9114 forEach(require, function(controller, property) {
9115 value[property] = getControllers(directiveName, controller, $element, elementControllers);
9116 });
9117 }
9118
9119 return value || null;
9120 }
9121
9122 function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope, newIsolateScopeDirective) {
9123 var elementControllers = createMap();
9124 for (var controllerKey in controllerDirectives) {
9125 var directive = controllerDirectives[controllerKey];
9126 var locals = {
9127 $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
9128 $element: $element,
9129 $attrs: attrs,
9130 $transclude: transcludeFn
9131 };
9132
9133 var controller = directive.controller;
9134 if (controller == '@') {
9135 controller = attrs[directive.name];
9136 }
9137
9138 var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
9139
9140 // For directives with element transclusion the element is a comment.
9141 // In this case .data will not attach any data.
9142 // Instead, we save the controllers for the element in a local hash and attach to .data
9143 // later, once we have the actual element.
9144 elementControllers[directive.name] = controllerInstance;
9145 $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
9146 }
9147 return elementControllers;
9148 }
9149
9150 // Depending upon the context in which a directive finds itself it might need to have a new isolated
9151 // or child scope created. For instance:
9152 // * if the directive has been pulled into a template because another directive with a higher priority
9153 // asked for element transclusion
9154 // * if the directive itself asks for transclusion but it is at the root of a template and the original
9155 // element was replaced. See https://github.com/angular/angular.js/issues/12936
9156 function markDirectiveScope(directives, isolateScope, newScope) {
9157 for (var j = 0, jj = directives.length; j < jj; j++) {
9158 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
9159 }
9160 }
9161
9162 /**
9163 * looks up the directive and decorates it with exception handling and proper parameters. We
9164 * call this the boundDirective.
9165 *
9166 * @param {string} name name of the directive to look up.
9167 * @param {string} location The directive must be found in specific format.
9168 * String containing any of theses characters:
9169 *
9170 * * `E`: element name
9171 * * `A': attribute
9172 * * `C`: class
9173 * * `M`: comment
9174 * @returns {boolean} true if directive was added.
9175 */
9176 function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
9177 endAttrName) {
9178 if (name === ignoreDirective) return null;
9179 var match = null;
9180 if (hasDirectives.hasOwnProperty(name)) {
9181 for (var directive, directives = $injector.get(name + Suffix),
9182 i = 0, ii = directives.length; i < ii; i++) {
9183 try {
9184 directive = directives[i];
9185 if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
9186 directive.restrict.indexOf(location) != -1) {
9187 if (startAttrName) {
9188 directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
9189 }
9190 if (!directive.$$bindings) {
9191 var bindings = directive.$$bindings =
9192 parseDirectiveBindings(directive, directive.name);
9193 if (isObject(bindings.isolateScope)) {
9194 directive.$$isolateBindings = bindings.isolateScope;
9195 }
9196 }
9197 tDirectives.push(directive);
9198 match = directive;
9199 }
9200 } catch (e) { $exceptionHandler(e); }
9201 }
9202 }
9203 return match;
9204 }
9205
9206
9207 /**
9208 * looks up the directive and returns true if it is a multi-element directive,
9209 * and therefore requires DOM nodes between -start and -end markers to be grouped
9210 * together.
9211 *
9212 * @param {string} name name of the directive to look up.
9213 * @returns true if directive was registered as multi-element.
9214 */
9215 function directiveIsMultiElement(name) {
9216 if (hasDirectives.hasOwnProperty(name)) {
9217 for (var directive, directives = $injector.get(name + Suffix),
9218 i = 0, ii = directives.length; i < ii; i++) {
9219 directive = directives[i];
9220 if (directive.multiElement) {
9221 return true;
9222 }
9223 }
9224 }
9225 return false;
9226 }
9227
9228 /**
9229 * When the element is replaced with HTML template then the new attributes
9230 * on the template need to be merged with the existing attributes in the DOM.
9231 * The desired effect is to have both of the attributes present.
9232 *
9233 * @param {object} dst destination attributes (original DOM)
9234 * @param {object} src source attributes (from the directive template)
9235 */
9236 function mergeTemplateAttributes(dst, src) {
9237 var srcAttr = src.$attr,
9238 dstAttr = dst.$attr,
9239 $element = dst.$$element;
9240
9241 // reapply the old attributes to the new element
9242 forEach(dst, function(value, key) {
9243 if (key.charAt(0) != '$') {
9244 if (src[key] && src[key] !== value) {
9245 value += (key === 'style' ? ';' : ' ') + src[key];
9246 }
9247 dst.$set(key, value, true, srcAttr[key]);
9248 }
9249 });
9250
9251 // copy the new attributes on the old attrs object
9252 forEach(src, function(value, key) {
9253 if (key == 'class') {
9254 safeAddClass($element, value);
9255 dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
9256 } else if (key == 'style') {
9257 $element.attr('style', $element.attr('style') + ';' + value);
9258 dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
9259 // `dst` will never contain hasOwnProperty as DOM parser won't let it.
9260 // You will get an "InvalidCharacterError: DOM Exception 5" error if you
9261 // have an attribute like "has-own-property" or "data-has-own-property", etc.
9262 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
9263 dst[key] = value;
9264 dstAttr[key] = srcAttr[key];
9265 }
9266 });
9267 }
9268
9269
9270 function compileTemplateUrl(directives, $compileNode, tAttrs,
9271 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
9272 var linkQueue = [],
9273 afterTemplateNodeLinkFn,
9274 afterTemplateChildLinkFn,
9275 beforeTemplateCompileNode = $compileNode[0],
9276 origAsyncDirective = directives.shift(),
9277 derivedSyncDirective = inherit(origAsyncDirective, {
9278 templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
9279 }),
9280 templateUrl = (isFunction(origAsyncDirective.templateUrl))
9281 ? origAsyncDirective.templateUrl($compileNode, tAttrs)
9282 : origAsyncDirective.templateUrl,
9283 templateNamespace = origAsyncDirective.templateNamespace;
9284
9285 $compileNode.empty();
9286
9287 $templateRequest(templateUrl)
9288 .then(function(content) {
9289 var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
9290
9291 content = denormalizeTemplate(content);
9292
9293 if (origAsyncDirective.replace) {
9294 if (jqLiteIsTextNode(content)) {
9295 $template = [];
9296 } else {
9297 $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
9298 }
9299 compileNode = $template[0];
9300
9301 if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
9302 throw $compileMinErr('tplrt',
9303 "Template for directive '{0}' must have exactly one root element. {1}",
9304 origAsyncDirective.name, templateUrl);
9305 }
9306
9307 tempTemplateAttrs = {$attr: {}};
9308 replaceWith($rootElement, $compileNode, compileNode);
9309 var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
9310
9311 if (isObject(origAsyncDirective.scope)) {
9312 // the original directive that caused the template to be loaded async required
9313 // an isolate scope
9314 markDirectiveScope(templateDirectives, true);
9315 }
9316 directives = templateDirectives.concat(directives);
9317 mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
9318 } else {
9319 compileNode = beforeTemplateCompileNode;
9320 $compileNode.html(content);
9321 }
9322
9323 directives.unshift(derivedSyncDirective);
9324
9325 afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
9326 childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
9327 previousCompileContext);
9328 forEach($rootElement, function(node, i) {
9329 if (node == compileNode) {
9330 $rootElement[i] = $compileNode[0];
9331 }
9332 });
9333 afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
9334
9335 while (linkQueue.length) {
9336 var scope = linkQueue.shift(),
9337 beforeTemplateLinkNode = linkQueue.shift(),
9338 linkRootElement = linkQueue.shift(),
9339 boundTranscludeFn = linkQueue.shift(),
9340 linkNode = $compileNode[0];
9341
9342 if (scope.$$destroyed) continue;
9343
9344 if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
9345 var oldClasses = beforeTemplateLinkNode.className;
9346
9347 if (!(previousCompileContext.hasElementTranscludeDirective &&
9348 origAsyncDirective.replace)) {
9349 // it was cloned therefore we have to clone as well.
9350 linkNode = jqLiteClone(compileNode);
9351 }
9352 replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
9353
9354 // Copy in CSS classes from original node
9355 safeAddClass(jqLite(linkNode), oldClasses);
9356 }
9357 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9358 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9359 } else {
9360 childBoundTranscludeFn = boundTranscludeFn;
9361 }
9362 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
9363 childBoundTranscludeFn);
9364 }
9365 linkQueue = null;
9366 });
9367
9368 return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
9369 var childBoundTranscludeFn = boundTranscludeFn;
9370 if (scope.$$destroyed) return;
9371 if (linkQueue) {
9372 linkQueue.push(scope,
9373 node,
9374 rootElement,
9375 childBoundTranscludeFn);
9376 } else {
9377 if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
9378 childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
9379 }
9380 afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
9381 }
9382 };
9383 }
9384
9385
9386 /**
9387 * Sorting function for bound directives.
9388 */
9389 function byPriority(a, b) {
9390 var diff = b.priority - a.priority;
9391 if (diff !== 0) return diff;
9392 if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
9393 return a.index - b.index;
9394 }
9395
9396 function assertNoDuplicate(what, previousDirective, directive, element) {
9397
9398 function wrapModuleNameIfDefined(moduleName) {
9399 return moduleName ?
9400 (' (module: ' + moduleName + ')') :
9401 '';
9402 }
9403
9404 if (previousDirective) {
9405 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
9406 previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
9407 directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
9408 }
9409 }
9410
9411
9412 function addTextInterpolateDirective(directives, text) {
9413 var interpolateFn = $interpolate(text, true);
9414 if (interpolateFn) {
9415 directives.push({
9416 priority: 0,
9417 compile: function textInterpolateCompileFn(templateNode) {
9418 var templateNodeParent = templateNode.parent(),
9419 hasCompileParent = !!templateNodeParent.length;
9420
9421 // When transcluding a template that has bindings in the root
9422 // we don't have a parent and thus need to add the class during linking fn.
9423 if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
9424
9425 return function textInterpolateLinkFn(scope, node) {
9426 var parent = node.parent();
9427 if (!hasCompileParent) compile.$$addBindingClass(parent);
9428 compile.$$addBindingInfo(parent, interpolateFn.expressions);
9429 scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
9430 node[0].nodeValue = value;
9431 });
9432 };
9433 }
9434 });
9435 }
9436 }
9437
9438
9439 function wrapTemplate(type, template) {
9440 type = lowercase(type || 'html');
9441 switch (type) {
9442 case 'svg':
9443 case 'math':
9444 var wrapper = document.createElement('div');
9445 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
9446 return wrapper.childNodes[0].childNodes;
9447 default:
9448 return template;
9449 }
9450 }
9451
9452
9453 function getTrustedContext(node, attrNormalizedName) {
9454 if (attrNormalizedName == "srcdoc") {
9455 return $sce.HTML;
9456 }
9457 var tag = nodeName_(node);
9458 // maction[xlink:href] can source SVG. It's not limited to <maction>.
9459 if (attrNormalizedName == "xlinkHref" ||
9460 (tag == "form" && attrNormalizedName == "action") ||
9461 (tag != "img" && (attrNormalizedName == "src" ||
9462 attrNormalizedName == "ngSrc"))) {
9463 return $sce.RESOURCE_URL;
9464 }
9465 }
9466
9467
9468 function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
9469 var trustedContext = getTrustedContext(node, name);
9470 allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
9471
9472 var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
9473
9474 // no interpolation found -> ignore
9475 if (!interpolateFn) return;
9476
9477
9478 if (name === "multiple" && nodeName_(node) === "select") {
9479 throw $compileMinErr("selmulti",
9480 "Binding to the 'multiple' attribute is not supported. Element: {0}",
9481 startingTag(node));
9482 }
9483
9484 directives.push({
9485 priority: 100,
9486 compile: function() {
9487 return {
9488 pre: function attrInterpolatePreLinkFn(scope, element, attr) {
9489 var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
9490
9491 if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
9492 throw $compileMinErr('nodomevents',
9493 "Interpolations for HTML DOM event attributes are disallowed. Please use the " +
9494 "ng- versions (such as ng-click instead of onclick) instead.");
9495 }
9496
9497 // If the attribute has changed since last $interpolate()ed
9498 var newValue = attr[name];
9499 if (newValue !== value) {
9500 // we need to interpolate again since the attribute value has been updated
9501 // (e.g. by another directive's compile function)
9502 // ensure unset/empty values make interpolateFn falsy
9503 interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
9504 value = newValue;
9505 }
9506
9507 // if attribute was updated so that there is no interpolation going on we don't want to
9508 // register any observers
9509 if (!interpolateFn) return;
9510
9511 // initialize attr object so that it's ready in case we need the value for isolate
9512 // scope initialization, otherwise the value would not be available from isolate
9513 // directive's linking fn during linking phase
9514 attr[name] = interpolateFn(scope);
9515
9516 ($$observers[name] || ($$observers[name] = [])).$$inter = true;
9517 (attr.$$observers && attr.$$observers[name].$$scope || scope).
9518 $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
9519 //special case for class attribute addition + removal
9520 //so that class changes can tap into the animation
9521 //hooks provided by the $animate service. Be sure to
9522 //skip animations when the first digest occurs (when
9523 //both the new and the old values are the same) since
9524 //the CSS classes are the non-interpolated values
9525 if (name === 'class' && newValue != oldValue) {
9526 attr.$updateClass(newValue, oldValue);
9527 } else {
9528 attr.$set(name, newValue);
9529 }
9530 });
9531 }
9532 };
9533 }
9534 });
9535 }
9536
9537
9538 /**
9539 * This is a special jqLite.replaceWith, which can replace items which
9540 * have no parents, provided that the containing jqLite collection is provided.
9541 *
9542 * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
9543 * in the root of the tree.
9544 * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
9545 * the shell, but replace its DOM node reference.
9546 * @param {Node} newNode The new DOM node.
9547 */
9548 function replaceWith($rootElement, elementsToRemove, newNode) {
9549 var firstElementToRemove = elementsToRemove[0],
9550 removeCount = elementsToRemove.length,
9551 parent = firstElementToRemove.parentNode,
9552 i, ii;
9553
9554 if ($rootElement) {
9555 for (i = 0, ii = $rootElement.length; i < ii; i++) {
9556 if ($rootElement[i] == firstElementToRemove) {
9557 $rootElement[i++] = newNode;
9558 for (var j = i, j2 = j + removeCount - 1,
9559 jj = $rootElement.length;
9560 j < jj; j++, j2++) {
9561 if (j2 < jj) {
9562 $rootElement[j] = $rootElement[j2];
9563 } else {
9564 delete $rootElement[j];
9565 }
9566 }
9567 $rootElement.length -= removeCount - 1;
9568
9569 // If the replaced element is also the jQuery .context then replace it
9570 // .context is a deprecated jQuery api, so we should set it only when jQuery set it
9571 // http://api.jquery.com/context/
9572 if ($rootElement.context === firstElementToRemove) {
9573 $rootElement.context = newNode;
9574 }
9575 break;
9576 }
9577 }
9578 }
9579
9580 if (parent) {
9581 parent.replaceChild(newNode, firstElementToRemove);
9582 }
9583
9584 // Append all the `elementsToRemove` to a fragment. This will...
9585 // - remove them from the DOM
9586 // - allow them to still be traversed with .nextSibling
9587 // - allow a single fragment.qSA to fetch all elements being removed
9588 var fragment = document.createDocumentFragment();
9589 for (i = 0; i < removeCount; i++) {
9590 fragment.appendChild(elementsToRemove[i]);
9591 }
9592
9593 if (jqLite.hasData(firstElementToRemove)) {
9594 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
9595 // data here because there's no public interface in jQuery to do that and copying over
9596 // event listeners (which is the main use of private data) wouldn't work anyway.
9597 jqLite.data(newNode, jqLite.data(firstElementToRemove));
9598
9599 // Remove $destroy event listeners from `firstElementToRemove`
9600 jqLite(firstElementToRemove).off('$destroy');
9601 }
9602
9603 // Cleanup any data/listeners on the elements and children.
9604 // This includes invoking the $destroy event on any elements with listeners.
9605 jqLite.cleanData(fragment.querySelectorAll('*'));
9606
9607 // Update the jqLite collection to only contain the `newNode`
9608 for (i = 1; i < removeCount; i++) {
9609 delete elementsToRemove[i];
9610 }
9611 elementsToRemove[0] = newNode;
9612 elementsToRemove.length = 1;
9613 }
9614
9615
9616 function cloneAndAnnotateFn(fn, annotation) {
9617 return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
9618 }
9619
9620
9621 function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
9622 try {
9623 linkFn(scope, $element, attrs, controllers, transcludeFn);
9624 } catch (e) {
9625 $exceptionHandler(e, startingTag($element));
9626 }
9627 }
9628
9629
9630 // Set up $watches for isolate scope and controller bindings. This process
9631 // only occurs for isolate scopes and new scopes with controllerAs.
9632 function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
9633 var removeWatchCollection = [];
9634 var changes;
9635 forEach(bindings, function initializeBinding(definition, scopeName) {
9636 var attrName = definition.attrName,
9637 optional = definition.optional,
9638 mode = definition.mode, // @, =, or &
9639 lastValue,
9640 parentGet, parentSet, compare, removeWatch;
9641
9642 switch (mode) {
9643
9644 case '@':
9645 if (!optional && !hasOwnProperty.call(attrs, attrName)) {
9646 destination[scopeName] = attrs[attrName] = void 0;
9647 }
9648 attrs.$observe(attrName, function(value) {
9649 if (isString(value)) {
9650 var oldValue = destination[scopeName];
9651 recordChanges(scopeName, value, oldValue);
9652 destination[scopeName] = value;
9653 }
9654 });
9655 attrs.$$observers[attrName].$$scope = scope;
9656 lastValue = attrs[attrName];
9657 if (isString(lastValue)) {
9658 // If the attribute has been provided then we trigger an interpolation to ensure
9659 // the value is there for use in the link fn
9660 destination[scopeName] = $interpolate(lastValue)(scope);
9661 } else if (isBoolean(lastValue)) {
9662 // If the attributes is one of the BOOLEAN_ATTR then Angular will have converted
9663 // the value to boolean rather than a string, so we special case this situation
9664 destination[scopeName] = lastValue;
9665 }
9666 break;
9667
9668 case '=':
9669 if (!hasOwnProperty.call(attrs, attrName)) {
9670 if (optional) break;
9671 attrs[attrName] = void 0;
9672 }
9673 if (optional && !attrs[attrName]) break;
9674
9675 parentGet = $parse(attrs[attrName]);
9676 if (parentGet.literal) {
9677 compare = equals;
9678 } else {
9679 compare = function simpleCompare(a, b) { return a === b || (a !== a && b !== b); };
9680 }
9681 parentSet = parentGet.assign || function() {
9682 // reset the change, or we will throw this exception on every $digest
9683 lastValue = destination[scopeName] = parentGet(scope);
9684 throw $compileMinErr('nonassign',
9685 "Expression '{0}' in attribute '{1}' used with directive '{2}' is non-assignable!",
9686 attrs[attrName], attrName, directive.name);
9687 };
9688 lastValue = destination[scopeName] = parentGet(scope);
9689 var parentValueWatch = function parentValueWatch(parentValue) {
9690 if (!compare(parentValue, destination[scopeName])) {
9691 // we are out of sync and need to copy
9692 if (!compare(parentValue, lastValue)) {
9693 // parent changed and it has precedence
9694 destination[scopeName] = parentValue;
9695 } else {
9696 // if the parent can be assigned then do so
9697 parentSet(scope, parentValue = destination[scopeName]);
9698 }
9699 }
9700 return lastValue = parentValue;
9701 };
9702 parentValueWatch.$stateful = true;
9703 if (definition.collection) {
9704 removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
9705 } else {
9706 removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
9707 }
9708 removeWatchCollection.push(removeWatch);
9709 break;
9710
9711 case '<':
9712 if (!hasOwnProperty.call(attrs, attrName)) {
9713 if (optional) break;
9714 attrs[attrName] = void 0;
9715 }
9716 if (optional && !attrs[attrName]) break;
9717
9718 parentGet = $parse(attrs[attrName]);
9719
9720 destination[scopeName] = parentGet(scope);
9721
9722 removeWatch = scope.$watch(parentGet, function parentValueWatchAction(newParentValue) {
9723 var oldValue = destination[scopeName];
9724 recordChanges(scopeName, newParentValue, oldValue);
9725 destination[scopeName] = newParentValue;
9726 }, parentGet.literal);
9727
9728 removeWatchCollection.push(removeWatch);
9729 break;
9730
9731 case '&':
9732 // Don't assign Object.prototype method to scope
9733 parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
9734
9735 // Don't assign noop to destination if expression is not valid
9736 if (parentGet === noop && optional) break;
9737
9738 destination[scopeName] = function(locals) {
9739 return parentGet(scope, locals);
9740 };
9741 break;
9742 }
9743 });
9744
9745 function recordChanges(key, currentValue, previousValue) {
9746 if (isFunction(destination.$onChanges) && currentValue !== previousValue) {
9747 // If we have not already scheduled the top level onChangesQueue handler then do so now
9748 if (!onChangesQueue) {
9749 scope.$$postDigest(flushOnChangesQueue);
9750 onChangesQueue = [];
9751 }
9752 // If we have not already queued a trigger of onChanges for this controller then do so now
9753 if (!changes) {
9754 changes = {};
9755 onChangesQueue.push(triggerOnChangesHook);
9756 }
9757 // If the has been a change on this property already then we need to reuse the previous value
9758 if (changes[key]) {
9759 previousValue = changes[key].previousValue;
9760 }
9761 // Store this change
9762 changes[key] = {previousValue: previousValue, currentValue: currentValue};
9763 }
9764 }
9765
9766 function triggerOnChangesHook() {
9767 destination.$onChanges(changes);
9768 // Now clear the changes so that we schedule onChanges when more changes arrive
9769 changes = undefined;
9770 }
9771
9772 return removeWatchCollection.length && function removeWatches() {
9773 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9774 removeWatchCollection[i]();
9775 }
9776 };
9777 }
9778 }];
9779}
9780
9781var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9782/**
9783 * Converts all accepted directives format into proper directive name.
9784 * @param name Name to normalize
9785 */
9786function directiveNormalize(name) {
9787 return camelCase(name.replace(PREFIX_REGEXP, ''));
9788}
9789
9790/**
9791 * @ngdoc type
9792 * @name $compile.directive.Attributes
9793 *
9794 * @description
9795 * A shared object between directive compile / linking functions which contains normalized DOM
9796 * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9797 * needed since all of these are treated as equivalent in Angular:
9798 *
9799 * ```
9800 * <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9801 * ```
9802 */
9803
9804/**
9805 * @ngdoc property
9806 * @name $compile.directive.Attributes#$attr
9807 *
9808 * @description
9809 * A map of DOM element attribute names to the normalized name. This is
9810 * needed to do reverse lookup from normalized name back to actual name.
9811 */
9812
9813
9814/**
9815 * @ngdoc method
9816 * @name $compile.directive.Attributes#$set
9817 * @kind function
9818 *
9819 * @description
9820 * Set DOM element attribute value.
9821 *
9822 *
9823 * @param {string} name Normalized element attribute name of the property to modify. The name is
9824 * reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9825 * property to the original name.
9826 * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9827 */
9828
9829
9830
9831/**
9832 * Closure compiler type information
9833 */
9834
9835function nodesetLinkingFn(
9836 /* angular.Scope */ scope,
9837 /* NodeList */ nodeList,
9838 /* Element */ rootElement,
9839 /* function(Function) */ boundTranscludeFn
9840) {}
9841
9842function directiveLinkingFn(
9843 /* nodesetLinkingFn */ nodesetLinkingFn,
9844 /* angular.Scope */ scope,
9845 /* Node */ node,
9846 /* Element */ rootElement,
9847 /* function(Function) */ boundTranscludeFn
9848) {}
9849
9850function tokenDifference(str1, str2) {
9851 var values = '',
9852 tokens1 = str1.split(/\s+/),
9853 tokens2 = str2.split(/\s+/);
9854
9855 outer:
9856 for (var i = 0; i < tokens1.length; i++) {
9857 var token = tokens1[i];
9858 for (var j = 0; j < tokens2.length; j++) {
9859 if (token == tokens2[j]) continue outer;
9860 }
9861 values += (values.length > 0 ? ' ' : '') + token;
9862 }
9863 return values;
9864}
9865
9866function removeComments(jqNodes) {
9867 jqNodes = jqLite(jqNodes);
9868 var i = jqNodes.length;
9869
9870 if (i <= 1) {
9871 return jqNodes;
9872 }
9873
9874 while (i--) {
9875 var node = jqNodes[i];
9876 if (node.nodeType === NODE_TYPE_COMMENT) {
9877 splice.call(jqNodes, i, 1);
9878 }
9879 }
9880 return jqNodes;
9881}
9882
9883var $controllerMinErr = minErr('$controller');
9884
9885
9886var CNTRL_REG = /^(\S+)(\s+as\s+([\w$]+))?$/;
9887function identifierForController(controller, ident) {
9888 if (ident && isString(ident)) return ident;
9889 if (isString(controller)) {
9890 var match = CNTRL_REG.exec(controller);
9891 if (match) return match[3];
9892 }
9893}
9894
9895
9896/**
9897 * @ngdoc provider
9898 * @name $controllerProvider
9899 * @description
9900 * The {@link ng.$controller $controller service} is used by Angular to create new
9901 * controllers.
9902 *
9903 * This provider allows controller registration via the
9904 * {@link ng.$controllerProvider#register register} method.
9905 */
9906function $ControllerProvider() {
9907 var controllers = {},
9908 globals = false;
9909
9910 /**
9911 * @ngdoc method
9912 * @name $controllerProvider#has
9913 * @param {string} name Controller name to check.
9914 */
9915 this.has = function(name) {
9916 return controllers.hasOwnProperty(name);
9917 };
9918
9919 /**
9920 * @ngdoc method
9921 * @name $controllerProvider#register
9922 * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9923 * the names and the values are the constructors.
9924 * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9925 * annotations in the array notation).
9926 */
9927 this.register = function(name, constructor) {
9928 assertNotHasOwnProperty(name, 'controller');
9929 if (isObject(name)) {
9930 extend(controllers, name);
9931 } else {
9932 controllers[name] = constructor;
9933 }
9934 };
9935
9936 /**
9937 * @ngdoc method
9938 * @name $controllerProvider#allowGlobals
9939 * @description If called, allows `$controller` to find controller constructors on `window`
9940 */
9941 this.allowGlobals = function() {
9942 globals = true;
9943 };
9944
9945
9946 this.$get = ['$injector', '$window', function($injector, $window) {
9947
9948 /**
9949 * @ngdoc service
9950 * @name $controller
9951 * @requires $injector
9952 *
9953 * @param {Function|string} constructor If called with a function then it's considered to be the
9954 * controller constructor function. Otherwise it's considered to be a string which is used
9955 * to retrieve the controller constructor using the following steps:
9956 *
9957 * * check if a controller with given name is registered via `$controllerProvider`
9958 * * check if evaluating the string on the current scope returns a constructor
9959 * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9960 * `window` object (not recommended)
9961 *
9962 * The string can use the `controller as property` syntax, where the controller instance is published
9963 * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9964 * to work correctly.
9965 *
9966 * @param {Object} locals Injection locals for Controller.
9967 * @return {Object} Instance of given controller.
9968 *
9969 * @description
9970 * `$controller` service is responsible for instantiating controllers.
9971 *
9972 * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9973 * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9974 */
9975 return function $controller(expression, locals, later, ident) {
9976 // PRIVATE API:
9977 // param `later` --- indicates that the controller's constructor is invoked at a later time.
9978 // If true, $controller will allocate the object with the correct
9979 // prototype chain, but will not invoke the controller until a returned
9980 // callback is invoked.
9981 // param `ident` --- An optional label which overrides the label parsed from the controller
9982 // expression, if any.
9983 var instance, match, constructor, identifier;
9984 later = later === true;
9985 if (ident && isString(ident)) {
9986 identifier = ident;
9987 }
9988
9989 if (isString(expression)) {
9990 match = expression.match(CNTRL_REG);
9991 if (!match) {
9992 throw $controllerMinErr('ctrlfmt',
9993 "Badly formed controller string '{0}'. " +
9994 "Must match `__name__ as __id__` or `__name__`.", expression);
9995 }
9996 constructor = match[1],
9997 identifier = identifier || match[3];
9998 expression = controllers.hasOwnProperty(constructor)
9999 ? controllers[constructor]
10000 : getter(locals.$scope, constructor, true) ||
10001 (globals ? getter($window, constructor, true) : undefined);
10002
10003 assertArgFn(expression, constructor, true);
10004 }
10005
10006 if (later) {
10007 // Instantiate controller later:
10008 // This machinery is used to create an instance of the object before calling the
10009 // controller's constructor itself.
10010 //
10011 // This allows properties to be added to the controller before the constructor is
10012 // invoked. Primarily, this is used for isolate scope bindings in $compile.
10013 //
10014 // This feature is not intended for use by applications, and is thus not documented
10015 // publicly.
10016 // Object creation: http://jsperf.com/create-constructor/2
10017 var controllerPrototype = (isArray(expression) ?
10018 expression[expression.length - 1] : expression).prototype;
10019 instance = Object.create(controllerPrototype || null);
10020
10021 if (identifier) {
10022 addIdentifier(locals, identifier, instance, constructor || expression.name);
10023 }
10024
10025 var instantiate;
10026 return instantiate = extend(function $controllerInit() {
10027 var result = $injector.invoke(expression, instance, locals, constructor);
10028 if (result !== instance && (isObject(result) || isFunction(result))) {
10029 instance = result;
10030 if (identifier) {
10031 // If result changed, re-assign controllerAs value to scope.
10032 addIdentifier(locals, identifier, instance, constructor || expression.name);
10033 }
10034 }
10035 return instance;
10036 }, {
10037 instance: instance,
10038 identifier: identifier
10039 });
10040 }
10041
10042 instance = $injector.instantiate(expression, locals, constructor);
10043
10044 if (identifier) {
10045 addIdentifier(locals, identifier, instance, constructor || expression.name);
10046 }
10047
10048 return instance;
10049 };
10050
10051 function addIdentifier(locals, identifier, instance, name) {
10052 if (!(locals && isObject(locals.$scope))) {
10053 throw minErr('$controller')('noscp',
10054 "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
10055 name, identifier);
10056 }
10057
10058 locals.$scope[identifier] = instance;
10059 }
10060 }];
10061}
10062
10063/**
10064 * @ngdoc service
10065 * @name $document
10066 * @requires $window
10067 *
10068 * @description
10069 * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
10070 *
10071 * @example
10072 <example module="documentExample">
10073 <file name="index.html">
10074 <div ng-controller="ExampleController">
10075 <p>$document title: <b ng-bind="title"></b></p>
10076 <p>window.document title: <b ng-bind="windowTitle"></b></p>
10077 </div>
10078 </file>
10079 <file name="script.js">
10080 angular.module('documentExample', [])
10081 .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
10082 $scope.title = $document[0].title;
10083 $scope.windowTitle = angular.element(window.document)[0].title;
10084 }]);
10085 </file>
10086 </example>
10087 */
10088function $DocumentProvider() {
10089 this.$get = ['$window', function(window) {
10090 return jqLite(window.document);
10091 }];
10092}
10093
10094/**
10095 * @ngdoc service
10096 * @name $exceptionHandler
10097 * @requires ng.$log
10098 *
10099 * @description
10100 * Any uncaught exception in angular expressions is delegated to this service.
10101 * The default implementation simply delegates to `$log.error` which logs it into
10102 * the browser console.
10103 *
10104 * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
10105 * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
10106 *
10107 * ## Example:
10108 *
10109 * ```js
10110 * angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
10111 * return function(exception, cause) {
10112 * exception.message += ' (caused by "' + cause + '")';
10113 * throw exception;
10114 * };
10115 * });
10116 * ```
10117 *
10118 * This example will override the normal action of `$exceptionHandler`, to make angular
10119 * exceptions fail hard when they happen, instead of just logging to the console.
10120 *
10121 * <hr />
10122 * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
10123 * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
10124 * (unless executed during a digest).
10125 *
10126 * If you wish, you can manually delegate exceptions, e.g.
10127 * `try { ... } catch(e) { $exceptionHandler(e); }`
10128 *
10129 * @param {Error} exception Exception associated with the error.
10130 * @param {string=} cause optional information about the context in which
10131 * the error was thrown.
10132 *
10133 */
10134function $ExceptionHandlerProvider() {
10135 this.$get = ['$log', function($log) {
10136 return function(exception, cause) {
10137 $log.error.apply($log, arguments);
10138 };
10139 }];
10140}
10141
10142var $$ForceReflowProvider = function() {
10143 this.$get = ['$document', function($document) {
10144 return function(domNode) {
10145 //the line below will force the browser to perform a repaint so
10146 //that all the animated elements within the animation frame will
10147 //be properly updated and drawn on screen. This is required to
10148 //ensure that the preparation animation is properly flushed so that
10149 //the active state picks up from there. DO NOT REMOVE THIS LINE.
10150 //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
10151 //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
10152 //WILL TAKE YEARS AWAY FROM YOUR LIFE.
10153 if (domNode) {
10154 if (!domNode.nodeType && domNode instanceof jqLite) {
10155 domNode = domNode[0];
10156 }
10157 } else {
10158 domNode = $document[0].body;
10159 }
10160 return domNode.offsetWidth + 1;
10161 };
10162 }];
10163};
10164
10165var APPLICATION_JSON = 'application/json';
10166var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
10167var JSON_START = /^\[|^\{(?!\{)/;
10168var JSON_ENDS = {
10169 '[': /]$/,
10170 '{': /}$/
10171};
10172var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
10173var $httpMinErr = minErr('$http');
10174var $httpMinErrLegacyFn = function(method) {
10175 return function() {
10176 throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
10177 };
10178};
10179
10180function serializeValue(v) {
10181 if (isObject(v)) {
10182 return isDate(v) ? v.toISOString() : toJson(v);
10183 }
10184 return v;
10185}
10186
10187
10188function $HttpParamSerializerProvider() {
10189 /**
10190 * @ngdoc service
10191 * @name $httpParamSerializer
10192 * @description
10193 *
10194 * Default {@link $http `$http`} params serializer that converts objects to strings
10195 * according to the following rules:
10196 *
10197 * * `{'foo': 'bar'}` results in `foo=bar`
10198 * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
10199 * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
10200 * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
10201 *
10202 * Note that serializer will sort the request parameters alphabetically.
10203 * */
10204
10205 this.$get = function() {
10206 return function ngParamSerializer(params) {
10207 if (!params) return '';
10208 var parts = [];
10209 forEachSorted(params, function(value, key) {
10210 if (value === null || isUndefined(value)) return;
10211 if (isArray(value)) {
10212 forEach(value, function(v) {
10213 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(v)));
10214 });
10215 } else {
10216 parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
10217 }
10218 });
10219
10220 return parts.join('&');
10221 };
10222 };
10223}
10224
10225function $HttpParamSerializerJQLikeProvider() {
10226 /**
10227 * @ngdoc service
10228 * @name $httpParamSerializerJQLike
10229 * @description
10230 *
10231 * Alternative {@link $http `$http`} params serializer that follows
10232 * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
10233 * The serializer will also sort the params alphabetically.
10234 *
10235 * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
10236 *
10237 * ```js
10238 * $http({
10239 * url: myUrl,
10240 * method: 'GET',
10241 * params: myParams,
10242 * paramSerializer: '$httpParamSerializerJQLike'
10243 * });
10244 * ```
10245 *
10246 * It is also possible to set it as the default `paramSerializer` in the
10247 * {@link $httpProvider#defaults `$httpProvider`}.
10248 *
10249 * Additionally, you can inject the serializer and use it explicitly, for example to serialize
10250 * form data for submission:
10251 *
10252 * ```js
10253 * .controller(function($http, $httpParamSerializerJQLike) {
10254 * //...
10255 *
10256 * $http({
10257 * url: myUrl,
10258 * method: 'POST',
10259 * data: $httpParamSerializerJQLike(myData),
10260 * headers: {
10261 * 'Content-Type': 'application/x-www-form-urlencoded'
10262 * }
10263 * });
10264 *
10265 * });
10266 * ```
10267 *
10268 * */
10269 this.$get = function() {
10270 return function jQueryLikeParamSerializer(params) {
10271 if (!params) return '';
10272 var parts = [];
10273 serialize(params, '', true);
10274 return parts.join('&');
10275
10276 function serialize(toSerialize, prefix, topLevel) {
10277 if (toSerialize === null || isUndefined(toSerialize)) return;
10278 if (isArray(toSerialize)) {
10279 forEach(toSerialize, function(value, index) {
10280 serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
10281 });
10282 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
10283 forEachSorted(toSerialize, function(value, key) {
10284 serialize(value, prefix +
10285 (topLevel ? '' : '[') +
10286 key +
10287 (topLevel ? '' : ']'));
10288 });
10289 } else {
10290 parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
10291 }
10292 }
10293 };
10294 };
10295}
10296
10297function defaultHttpResponseTransform(data, headers) {
10298 if (isString(data)) {
10299 // Strip json vulnerability protection prefix and trim whitespace
10300 var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
10301
10302 if (tempData) {
10303 var contentType = headers('Content-Type');
10304 if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
10305 data = fromJson(tempData);
10306 }
10307 }
10308 }
10309
10310 return data;
10311}
10312
10313function isJsonLike(str) {
10314 var jsonStart = str.match(JSON_START);
10315 return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
10316}
10317
10318/**
10319 * Parse headers into key value object
10320 *
10321 * @param {string} headers Raw headers as a string
10322 * @returns {Object} Parsed headers as key value object
10323 */
10324function parseHeaders(headers) {
10325 var parsed = createMap(), i;
10326
10327 function fillInParsed(key, val) {
10328 if (key) {
10329 parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
10330 }
10331 }
10332
10333 if (isString(headers)) {
10334 forEach(headers.split('\n'), function(line) {
10335 i = line.indexOf(':');
10336 fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
10337 });
10338 } else if (isObject(headers)) {
10339 forEach(headers, function(headerVal, headerKey) {
10340 fillInParsed(lowercase(headerKey), trim(headerVal));
10341 });
10342 }
10343
10344 return parsed;
10345}
10346
10347
10348/**
10349 * Returns a function that provides access to parsed headers.
10350 *
10351 * Headers are lazy parsed when first requested.
10352 * @see parseHeaders
10353 *
10354 * @param {(string|Object)} headers Headers to provide access to.
10355 * @returns {function(string=)} Returns a getter function which if called with:
10356 *
10357 * - if called with single an argument returns a single header value or null
10358 * - if called with no arguments returns an object containing all headers.
10359 */
10360function headersGetter(headers) {
10361 var headersObj;
10362
10363 return function(name) {
10364 if (!headersObj) headersObj = parseHeaders(headers);
10365
10366 if (name) {
10367 var value = headersObj[lowercase(name)];
10368 if (value === void 0) {
10369 value = null;
10370 }
10371 return value;
10372 }
10373
10374 return headersObj;
10375 };
10376}
10377
10378
10379/**
10380 * Chain all given functions
10381 *
10382 * This function is used for both request and response transforming
10383 *
10384 * @param {*} data Data to transform.
10385 * @param {function(string=)} headers HTTP headers getter fn.
10386 * @param {number} status HTTP status code of the response.
10387 * @param {(Function|Array.<Function>)} fns Function or an array of functions.
10388 * @returns {*} Transformed data.
10389 */
10390function transformData(data, headers, status, fns) {
10391 if (isFunction(fns)) {
10392 return fns(data, headers, status);
10393 }
10394
10395 forEach(fns, function(fn) {
10396 data = fn(data, headers, status);
10397 });
10398
10399 return data;
10400}
10401
10402
10403function isSuccess(status) {
10404 return 200 <= status && status < 300;
10405}
10406
10407
10408/**
10409 * @ngdoc provider
10410 * @name $httpProvider
10411 * @description
10412 * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
10413 * */
10414function $HttpProvider() {
10415 /**
10416 * @ngdoc property
10417 * @name $httpProvider#defaults
10418 * @description
10419 *
10420 * Object containing default values for all {@link ng.$http $http} requests.
10421 *
10422 * - **`defaults.cache`** - {boolean|Object} - A boolean value or object created with
10423 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of HTTP responses
10424 * by default. See {@link $http#caching $http Caching} for more information.
10425 *
10426 * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
10427 * Defaults value is `'XSRF-TOKEN'`.
10428 *
10429 * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
10430 * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
10431 *
10432 * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
10433 * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
10434 * setting default headers.
10435 * - **`defaults.headers.common`**
10436 * - **`defaults.headers.post`**
10437 * - **`defaults.headers.put`**
10438 * - **`defaults.headers.patch`**
10439 *
10440 *
10441 * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
10442 * used to the prepare string representation of request parameters (specified as an object).
10443 * If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
10444 * Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
10445 *
10446 **/
10447 var defaults = this.defaults = {
10448 // transform incoming response data
10449 transformResponse: [defaultHttpResponseTransform],
10450
10451 // transform outgoing request data
10452 transformRequest: [function(d) {
10453 return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
10454 }],
10455
10456 // default headers
10457 headers: {
10458 common: {
10459 'Accept': 'application/json, text/plain, */*'
10460 },
10461 post: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10462 put: shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
10463 patch: shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
10464 },
10465
10466 xsrfCookieName: 'XSRF-TOKEN',
10467 xsrfHeaderName: 'X-XSRF-TOKEN',
10468
10469 paramSerializer: '$httpParamSerializer'
10470 };
10471
10472 var useApplyAsync = false;
10473 /**
10474 * @ngdoc method
10475 * @name $httpProvider#useApplyAsync
10476 * @description
10477 *
10478 * Configure $http service to combine processing of multiple http responses received at around
10479 * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
10480 * significant performance improvement for bigger applications that make many HTTP requests
10481 * concurrently (common during application bootstrap).
10482 *
10483 * Defaults to false. If no value is specified, returns the current configured value.
10484 *
10485 * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
10486 * "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
10487 * to load and share the same digest cycle.
10488 *
10489 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10490 * otherwise, returns the current configured value.
10491 **/
10492 this.useApplyAsync = function(value) {
10493 if (isDefined(value)) {
10494 useApplyAsync = !!value;
10495 return this;
10496 }
10497 return useApplyAsync;
10498 };
10499
10500 var useLegacyPromise = true;
10501 /**
10502 * @ngdoc method
10503 * @name $httpProvider#useLegacyPromiseExtensions
10504 * @description
10505 *
10506 * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
10507 * This should be used to make sure that applications work without these methods.
10508 *
10509 * Defaults to true. If no value is specified, returns the current configured value.
10510 *
10511 * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
10512 *
10513 * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
10514 * otherwise, returns the current configured value.
10515 **/
10516 this.useLegacyPromiseExtensions = function(value) {
10517 if (isDefined(value)) {
10518 useLegacyPromise = !!value;
10519 return this;
10520 }
10521 return useLegacyPromise;
10522 };
10523
10524 /**
10525 * @ngdoc property
10526 * @name $httpProvider#interceptors
10527 * @description
10528 *
10529 * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
10530 * pre-processing of request or postprocessing of responses.
10531 *
10532 * These service factories are ordered by request, i.e. they are applied in the same order as the
10533 * array, on request, but reverse order, on response.
10534 *
10535 * {@link ng.$http#interceptors Interceptors detailed info}
10536 **/
10537 var interceptorFactories = this.interceptors = [];
10538
10539 this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
10540 function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
10541
10542 var defaultCache = $cacheFactory('$http');
10543
10544 /**
10545 * Make sure that default param serializer is exposed as a function
10546 */
10547 defaults.paramSerializer = isString(defaults.paramSerializer) ?
10548 $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
10549
10550 /**
10551 * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
10552 * The reversal is needed so that we can build up the interception chain around the
10553 * server request.
10554 */
10555 var reversedInterceptors = [];
10556
10557 forEach(interceptorFactories, function(interceptorFactory) {
10558 reversedInterceptors.unshift(isString(interceptorFactory)
10559 ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
10560 });
10561
10562 /**
10563 * @ngdoc service
10564 * @kind function
10565 * @name $http
10566 * @requires ng.$httpBackend
10567 * @requires $cacheFactory
10568 * @requires $rootScope
10569 * @requires $q
10570 * @requires $injector
10571 *
10572 * @description
10573 * The `$http` service is a core Angular service that facilitates communication with the remote
10574 * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
10575 * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
10576 *
10577 * For unit testing applications that use `$http` service, see
10578 * {@link ngMock.$httpBackend $httpBackend mock}.
10579 *
10580 * For a higher level of abstraction, please check out the {@link ngResource.$resource
10581 * $resource} service.
10582 *
10583 * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
10584 * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
10585 * it is important to familiarize yourself with these APIs and the guarantees they provide.
10586 *
10587 *
10588 * ## General usage
10589 * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
10590 * that is used to generate an HTTP request and returns a {@link ng.$q promise}.
10591 *
10592 * ```js
10593 * // Simple GET request example:
10594 * $http({
10595 * method: 'GET',
10596 * url: '/someUrl'
10597 * }).then(function successCallback(response) {
10598 * // this callback will be called asynchronously
10599 * // when the response is available
10600 * }, function errorCallback(response) {
10601 * // called asynchronously if an error occurs
10602 * // or server returns response with an error status.
10603 * });
10604 * ```
10605 *
10606 * The response object has these properties:
10607 *
10608 * - **data** – `{string|Object}` – The response body transformed with the transform
10609 * functions.
10610 * - **status** – `{number}` – HTTP status code of the response.
10611 * - **headers** – `{function([headerName])}` – Header getter function.
10612 * - **config** – `{Object}` – The configuration object that was used to generate the request.
10613 * - **statusText** – `{string}` – HTTP status text of the response.
10614 *
10615 * A response status code between 200 and 299 is considered a success status and
10616 * will result in the success callback being called. Note that if the response is a redirect,
10617 * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
10618 * called for such responses.
10619 *
10620 *
10621 * ## Shortcut methods
10622 *
10623 * Shortcut methods are also available. All shortcut methods require passing in the URL, and
10624 * request data must be passed in for POST/PUT requests. An optional config can be passed as the
10625 * last argument.
10626 *
10627 * ```js
10628 * $http.get('/someUrl', config).then(successCallback, errorCallback);
10629 * $http.post('/someUrl', data, config).then(successCallback, errorCallback);
10630 * ```
10631 *
10632 * Complete list of shortcut methods:
10633 *
10634 * - {@link ng.$http#get $http.get}
10635 * - {@link ng.$http#head $http.head}
10636 * - {@link ng.$http#post $http.post}
10637 * - {@link ng.$http#put $http.put}
10638 * - {@link ng.$http#delete $http.delete}
10639 * - {@link ng.$http#jsonp $http.jsonp}
10640 * - {@link ng.$http#patch $http.patch}
10641 *
10642 *
10643 * ## Writing Unit Tests that use $http
10644 * When unit testing (using {@link ngMock ngMock}), it is necessary to call
10645 * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
10646 * request using trained responses.
10647 *
10648 * ```
10649 * $httpBackend.expectGET(...);
10650 * $http.get(...);
10651 * $httpBackend.flush();
10652 * ```
10653 *
10654 * ## Deprecation Notice
10655 * <div class="alert alert-danger">
10656 * The `$http` legacy promise methods `success` and `error` have been deprecated.
10657 * Use the standard `then` method instead.
10658 * If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
10659 * `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
10660 * </div>
10661 *
10662 * ## Setting HTTP Headers
10663 *
10664 * The $http service will automatically add certain HTTP headers to all requests. These defaults
10665 * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
10666 * object, which currently contains this default configuration:
10667 *
10668 * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
10669 * - `Accept: application/json, text/plain, * / *`
10670 * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
10671 * - `Content-Type: application/json`
10672 * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
10673 * - `Content-Type: application/json`
10674 *
10675 * To add or overwrite these defaults, simply add or remove a property from these configuration
10676 * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
10677 * with the lowercased HTTP method name as the key, e.g.
10678 * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
10679 *
10680 * The defaults can also be set at runtime via the `$http.defaults` object in the same
10681 * fashion. For example:
10682 *
10683 * ```
10684 * module.run(function($http) {
10685 * $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
10686 * });
10687 * ```
10688 *
10689 * In addition, you can supply a `headers` property in the config object passed when
10690 * calling `$http(config)`, which overrides the defaults without changing them globally.
10691 *
10692 * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
10693 * Use the `headers` property, setting the desired header to `undefined`. For example:
10694 *
10695 * ```js
10696 * var req = {
10697 * method: 'POST',
10698 * url: 'http://example.com',
10699 * headers: {
10700 * 'Content-Type': undefined
10701 * },
10702 * data: { test: 'test' }
10703 * }
10704 *
10705 * $http(req).then(function(){...}, function(){...});
10706 * ```
10707 *
10708 * ## Transforming Requests and Responses
10709 *
10710 * Both requests and responses can be transformed using transformation functions: `transformRequest`
10711 * and `transformResponse`. These properties can be a single function that returns
10712 * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
10713 * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
10714 *
10715 * <div class="alert alert-warning">
10716 * **Note:** Angular does not make a copy of the `data` parameter before it is passed into the `transformRequest` pipeline.
10717 * That means changes to the properties of `data` are not local to the transform function (since Javascript passes objects by reference).
10718 * For example, when calling `$http.get(url, $scope.myObject)`, modifications to the object's properties in a transformRequest
10719 * function will be reflected on the scope and in any templates where the object is data-bound.
10720 * To prevent his, transform functions should have no side-effects.
10721 * If you need to modify properties, it is recommended to make a copy of the data, or create new object to return.
10722 * </div>
10723 *
10724 * ### Default Transformations
10725 *
10726 * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
10727 * `defaults.transformResponse` properties. If a request does not provide its own transformations
10728 * then these will be applied.
10729 *
10730 * You can augment or replace the default transformations by modifying these properties by adding to or
10731 * replacing the array.
10732 *
10733 * Angular provides the following default transformations:
10734 *
10735 * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
10736 *
10737 * - If the `data` property of the request configuration object contains an object, serialize it
10738 * into JSON format.
10739 *
10740 * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
10741 *
10742 * - If XSRF prefix is detected, strip it (see Security Considerations section below).
10743 * - If JSON response is detected, deserialize it using a JSON parser.
10744 *
10745 *
10746 * ### Overriding the Default Transformations Per Request
10747 *
10748 * If you wish override the request/response transformations only for a single request then provide
10749 * `transformRequest` and/or `transformResponse` properties on the configuration object passed
10750 * into `$http`.
10751 *
10752 * Note that if you provide these properties on the config object the default transformations will be
10753 * overwritten. If you wish to augment the default transformations then you must include them in your
10754 * local transformation array.
10755 *
10756 * The following code demonstrates adding a new response transformation to be run after the default response
10757 * transformations have been run.
10758 *
10759 * ```js
10760 * function appendTransform(defaults, transform) {
10761 *
10762 * // We can't guarantee that the default transformation is an array
10763 * defaults = angular.isArray(defaults) ? defaults : [defaults];
10764 *
10765 * // Append the new transformation to the defaults
10766 * return defaults.concat(transform);
10767 * }
10768 *
10769 * $http({
10770 * url: '...',
10771 * method: 'GET',
10772 * transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
10773 * return doTransform(value);
10774 * })
10775 * });
10776 * ```
10777 *
10778 *
10779 * ## Caching
10780 *
10781 * {@link ng.$http `$http`} responses are not cached by default. To enable caching, you must
10782 * set the config.cache value or the default cache value to TRUE or to a cache object (created
10783 * with {@link ng.$cacheFactory `$cacheFactory`}). If defined, the value of config.cache takes
10784 * precedence over the default cache value.
10785 *
10786 * In order to:
10787 * * cache all responses - set the default cache value to TRUE or to a cache object
10788 * * cache a specific response - set config.cache value to TRUE or to a cache object
10789 *
10790 * If caching is enabled, but neither the default cache nor config.cache are set to a cache object,
10791 * then the default `$cacheFactory($http)` object is used.
10792 *
10793 * The default cache value can be set by updating the
10794 * {@link ng.$http#defaults `$http.defaults.cache`} property or the
10795 * {@link $httpProvider#defaults `$httpProvider.defaults.cache`} property.
10796 *
10797 * When caching is enabled, {@link ng.$http `$http`} stores the response from the server using
10798 * the relevant cache object. The next time the same request is made, the response is returned
10799 * from the cache without sending a request to the server.
10800 *
10801 * Take note that:
10802 *
10803 * * Only GET and JSONP requests are cached.
10804 * * The cache key is the request URL including search parameters; headers are not considered.
10805 * * Cached responses are returned asynchronously, in the same way as responses from the server.
10806 * * If multiple identical requests are made using the same cache, which is not yet populated,
10807 * one request will be made to the server and remaining requests will return the same response.
10808 * * A cache-control header on the response does not affect if or how responses are cached.
10809 *
10810 *
10811 * ## Interceptors
10812 *
10813 * Before you start creating interceptors, be sure to understand the
10814 * {@link ng.$q $q and deferred/promise APIs}.
10815 *
10816 * For purposes of global error handling, authentication, or any kind of synchronous or
10817 * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10818 * able to intercept requests before they are handed to the server and
10819 * responses before they are handed over to the application code that
10820 * initiated these requests. The interceptors leverage the {@link ng.$q
10821 * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10822 *
10823 * The interceptors are service factories that are registered with the `$httpProvider` by
10824 * adding them to the `$httpProvider.interceptors` array. The factory is called and
10825 * injected with dependencies (if specified) and returns the interceptor.
10826 *
10827 * There are two kinds of interceptors (and two kinds of rejection interceptors):
10828 *
10829 * * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10830 * modify the `config` object or create a new one. The function needs to return the `config`
10831 * object directly, or a promise containing the `config` or a new `config` object.
10832 * * `requestError`: interceptor gets called when a previous interceptor threw an error or
10833 * resolved with a rejection.
10834 * * `response`: interceptors get called with http `response` object. The function is free to
10835 * modify the `response` object or create a new one. The function needs to return the `response`
10836 * object directly, or as a promise containing the `response` or a new `response` object.
10837 * * `responseError`: interceptor gets called when a previous interceptor threw an error or
10838 * resolved with a rejection.
10839 *
10840 *
10841 * ```js
10842 * // register the interceptor as a service
10843 * $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10844 * return {
10845 * // optional method
10846 * 'request': function(config) {
10847 * // do something on success
10848 * return config;
10849 * },
10850 *
10851 * // optional method
10852 * 'requestError': function(rejection) {
10853 * // do something on error
10854 * if (canRecover(rejection)) {
10855 * return responseOrNewPromise
10856 * }
10857 * return $q.reject(rejection);
10858 * },
10859 *
10860 *
10861 *
10862 * // optional method
10863 * 'response': function(response) {
10864 * // do something on success
10865 * return response;
10866 * },
10867 *
10868 * // optional method
10869 * 'responseError': function(rejection) {
10870 * // do something on error
10871 * if (canRecover(rejection)) {
10872 * return responseOrNewPromise
10873 * }
10874 * return $q.reject(rejection);
10875 * }
10876 * };
10877 * });
10878 *
10879 * $httpProvider.interceptors.push('myHttpInterceptor');
10880 *
10881 *
10882 * // alternatively, register the interceptor via an anonymous factory
10883 * $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10884 * return {
10885 * 'request': function(config) {
10886 * // same as above
10887 * },
10888 *
10889 * 'response': function(response) {
10890 * // same as above
10891 * }
10892 * };
10893 * });
10894 * ```
10895 *
10896 * ## Security Considerations
10897 *
10898 * When designing web applications, consider security threats from:
10899 *
10900 * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10901 * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10902 *
10903 * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10904 * pre-configured with strategies that address these issues, but for this to work backend server
10905 * cooperation is required.
10906 *
10907 * ### JSON Vulnerability Protection
10908 *
10909 * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10910 * allows third party website to turn your JSON resource URL into
10911 * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10912 * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10913 * Angular will automatically strip the prefix before processing it as JSON.
10914 *
10915 * For example if your server needs to return:
10916 * ```js
10917 * ['one','two']
10918 * ```
10919 *
10920 * which is vulnerable to attack, your server can return:
10921 * ```js
10922 * )]}',
10923 * ['one','two']
10924 * ```
10925 *
10926 * Angular will strip the prefix, before processing the JSON.
10927 *
10928 *
10929 * ### Cross Site Request Forgery (XSRF) Protection
10930 *
10931 * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is an attack technique by
10932 * which the attacker can trick an authenticated user into unknowingly executing actions on your
10933 * website. Angular provides a mechanism to counter XSRF. When performing XHR requests, the
10934 * $http service reads a token from a cookie (by default, `XSRF-TOKEN`) and sets it as an HTTP
10935 * header (`X-XSRF-TOKEN`). Since only JavaScript that runs on your domain could read the
10936 * cookie, your server can be assured that the XHR came from JavaScript running on your domain.
10937 * The header will not be set for cross-domain requests.
10938 *
10939 * To take advantage of this, your server needs to set a token in a JavaScript readable session
10940 * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10941 * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10942 * that only JavaScript running on your domain could have sent the request. The token must be
10943 * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10944 * making up its own tokens). We recommend that the token is a digest of your site's
10945 * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography))
10946 * for added security.
10947 *
10948 * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10949 * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10950 * or the per-request config object.
10951 *
10952 * In order to prevent collisions in environments where multiple Angular apps share the
10953 * same domain or subdomain, we recommend that each application uses unique cookie name.
10954 *
10955 * @param {object} config Object describing the request to be made and how it should be
10956 * processed. The object has following properties:
10957 *
10958 * - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10959 * - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10960 * - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10961 * with the `paramSerializer` and appended as GET parameters.
10962 * - **data** – `{string|Object}` – Data to be sent as the request message data.
10963 * - **headers** – `{Object}` – Map of strings or functions which return strings representing
10964 * HTTP headers to send to the server. If the return value of a function is null, the
10965 * header will not be sent. Functions accept a config object as an argument.
10966 * - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10967 * - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10968 * - **transformRequest** –
10969 * `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10970 * transform function or an array of such functions. The transform function takes the http
10971 * request body and headers and returns its transformed (typically serialized) version.
10972 * See {@link ng.$http#overriding-the-default-transformations-per-request
10973 * Overriding the Default Transformations}
10974 * - **transformResponse** –
10975 * `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10976 * transform function or an array of such functions. The transform function takes the http
10977 * response body, headers and status and returns its transformed (typically deserialized) version.
10978 * See {@link ng.$http#overriding-the-default-transformations-per-request
10979 * Overriding the Default Transformations}
10980 * - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10981 * prepare the string representation of request parameters (specified as an object).
10982 * If specified as string, it is interpreted as function registered with the
10983 * {@link $injector $injector}, which means you can create your own serializer
10984 * by registering it as a {@link auto.$provide#service service}.
10985 * The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10986 * alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10987 * - **cache** – `{boolean|Object}` – A boolean value or object created with
10988 * {@link ng.$cacheFactory `$cacheFactory`} to enable or disable caching of the HTTP response.
10989 * See {@link $http#caching $http Caching} for more information.
10990 * - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10991 * that should abort the request when resolved.
10992 * - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10993 * XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10994 * for more information.
10995 * - **responseType** - `{string}` - see
10996 * [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10997 *
10998 * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10999 * when the request succeeds or fails.
11000 *
11001 *
11002 * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
11003 * requests. This is primarily meant to be used for debugging purposes.
11004 *
11005 *
11006 * @example
11007<example module="httpExample">
11008<file name="index.html">
11009 <div ng-controller="FetchController">
11010 <select ng-model="method" aria-label="Request method">
11011 <option>GET</option>
11012 <option>JSONP</option>
11013 </select>
11014 <input type="text" ng-model="url" size="80" aria-label="URL" />
11015 <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
11016 <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
11017 <button id="samplejsonpbtn"
11018 ng-click="updateModel('JSONP',
11019 'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
11020 Sample JSONP
11021 </button>
11022 <button id="invalidjsonpbtn"
11023 ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
11024 Invalid JSONP
11025 </button>
11026 <pre>http status code: {{status}}</pre>
11027 <pre>http response data: {{data}}</pre>
11028 </div>
11029</file>
11030<file name="script.js">
11031 angular.module('httpExample', [])
11032 .controller('FetchController', ['$scope', '$http', '$templateCache',
11033 function($scope, $http, $templateCache) {
11034 $scope.method = 'GET';
11035 $scope.url = 'http-hello.html';
11036
11037 $scope.fetch = function() {
11038 $scope.code = null;
11039 $scope.response = null;
11040
11041 $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
11042 then(function(response) {
11043 $scope.status = response.status;
11044 $scope.data = response.data;
11045 }, function(response) {
11046 $scope.data = response.data || "Request failed";
11047 $scope.status = response.status;
11048 });
11049 };
11050
11051 $scope.updateModel = function(method, url) {
11052 $scope.method = method;
11053 $scope.url = url;
11054 };
11055 }]);
11056</file>
11057<file name="http-hello.html">
11058 Hello, $http!
11059</file>
11060<file name="protractor.js" type="protractor">
11061 var status = element(by.binding('status'));
11062 var data = element(by.binding('data'));
11063 var fetchBtn = element(by.id('fetchbtn'));
11064 var sampleGetBtn = element(by.id('samplegetbtn'));
11065 var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
11066 var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
11067
11068 it('should make an xhr GET request', function() {
11069 sampleGetBtn.click();
11070 fetchBtn.click();
11071 expect(status.getText()).toMatch('200');
11072 expect(data.getText()).toMatch(/Hello, \$http!/);
11073 });
11074
11075// Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
11076// it('should make a JSONP request to angularjs.org', function() {
11077// sampleJsonpBtn.click();
11078// fetchBtn.click();
11079// expect(status.getText()).toMatch('200');
11080// expect(data.getText()).toMatch(/Super Hero!/);
11081// });
11082
11083 it('should make JSONP request to invalid URL and invoke the error handler',
11084 function() {
11085 invalidJsonpBtn.click();
11086 fetchBtn.click();
11087 expect(status.getText()).toMatch('0');
11088 expect(data.getText()).toMatch('Request failed');
11089 });
11090</file>
11091</example>
11092 */
11093 function $http(requestConfig) {
11094
11095 if (!isObject(requestConfig)) {
11096 throw minErr('$http')('badreq', 'Http request configuration must be an object. Received: {0}', requestConfig);
11097 }
11098
11099 if (!isString(requestConfig.url)) {
11100 throw minErr('$http')('badreq', 'Http request configuration url must be a string. Received: {0}', requestConfig.url);
11101 }
11102
11103 var config = extend({
11104 method: 'get',
11105 transformRequest: defaults.transformRequest,
11106 transformResponse: defaults.transformResponse,
11107 paramSerializer: defaults.paramSerializer
11108 }, requestConfig);
11109
11110 config.headers = mergeHeaders(requestConfig);
11111 config.method = uppercase(config.method);
11112 config.paramSerializer = isString(config.paramSerializer) ?
11113 $injector.get(config.paramSerializer) : config.paramSerializer;
11114
11115 var serverRequest = function(config) {
11116 var headers = config.headers;
11117 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
11118
11119 // strip content-type if data is undefined
11120 if (isUndefined(reqData)) {
11121 forEach(headers, function(value, header) {
11122 if (lowercase(header) === 'content-type') {
11123 delete headers[header];
11124 }
11125 });
11126 }
11127
11128 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
11129 config.withCredentials = defaults.withCredentials;
11130 }
11131
11132 // send request
11133 return sendReq(config, reqData).then(transformResponse, transformResponse);
11134 };
11135
11136 var chain = [serverRequest, undefined];
11137 var promise = $q.when(config);
11138
11139 // apply interceptors
11140 forEach(reversedInterceptors, function(interceptor) {
11141 if (interceptor.request || interceptor.requestError) {
11142 chain.unshift(interceptor.request, interceptor.requestError);
11143 }
11144 if (interceptor.response || interceptor.responseError) {
11145 chain.push(interceptor.response, interceptor.responseError);
11146 }
11147 });
11148
11149 while (chain.length) {
11150 var thenFn = chain.shift();
11151 var rejectFn = chain.shift();
11152
11153 promise = promise.then(thenFn, rejectFn);
11154 }
11155
11156 if (useLegacyPromise) {
11157 promise.success = function(fn) {
11158 assertArgFn(fn, 'fn');
11159
11160 promise.then(function(response) {
11161 fn(response.data, response.status, response.headers, config);
11162 });
11163 return promise;
11164 };
11165
11166 promise.error = function(fn) {
11167 assertArgFn(fn, 'fn');
11168
11169 promise.then(null, function(response) {
11170 fn(response.data, response.status, response.headers, config);
11171 });
11172 return promise;
11173 };
11174 } else {
11175 promise.success = $httpMinErrLegacyFn('success');
11176 promise.error = $httpMinErrLegacyFn('error');
11177 }
11178
11179 return promise;
11180
11181 function transformResponse(response) {
11182 // make a copy since the response must be cacheable
11183 var resp = extend({}, response);
11184 resp.data = transformData(response.data, response.headers, response.status,
11185 config.transformResponse);
11186 return (isSuccess(response.status))
11187 ? resp
11188 : $q.reject(resp);
11189 }
11190
11191 function executeHeaderFns(headers, config) {
11192 var headerContent, processedHeaders = {};
11193
11194 forEach(headers, function(headerFn, header) {
11195 if (isFunction(headerFn)) {
11196 headerContent = headerFn(config);
11197 if (headerContent != null) {
11198 processedHeaders[header] = headerContent;
11199 }
11200 } else {
11201 processedHeaders[header] = headerFn;
11202 }
11203 });
11204
11205 return processedHeaders;
11206 }
11207
11208 function mergeHeaders(config) {
11209 var defHeaders = defaults.headers,
11210 reqHeaders = extend({}, config.headers),
11211 defHeaderName, lowercaseDefHeaderName, reqHeaderName;
11212
11213 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
11214
11215 // using for-in instead of forEach to avoid unnecessary iteration after header has been found
11216 defaultHeadersIteration:
11217 for (defHeaderName in defHeaders) {
11218 lowercaseDefHeaderName = lowercase(defHeaderName);
11219
11220 for (reqHeaderName in reqHeaders) {
11221 if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
11222 continue defaultHeadersIteration;
11223 }
11224 }
11225
11226 reqHeaders[defHeaderName] = defHeaders[defHeaderName];
11227 }
11228
11229 // execute if header value is a function for merged headers
11230 return executeHeaderFns(reqHeaders, shallowCopy(config));
11231 }
11232 }
11233
11234 $http.pendingRequests = [];
11235
11236 /**
11237 * @ngdoc method
11238 * @name $http#get
11239 *
11240 * @description
11241 * Shortcut method to perform `GET` request.
11242 *
11243 * @param {string} url Relative or absolute URL specifying the destination of the request
11244 * @param {Object=} config Optional configuration object
11245 * @returns {HttpPromise} Future object
11246 */
11247
11248 /**
11249 * @ngdoc method
11250 * @name $http#delete
11251 *
11252 * @description
11253 * Shortcut method to perform `DELETE` request.
11254 *
11255 * @param {string} url Relative or absolute URL specifying the destination of the request
11256 * @param {Object=} config Optional configuration object
11257 * @returns {HttpPromise} Future object
11258 */
11259
11260 /**
11261 * @ngdoc method
11262 * @name $http#head
11263 *
11264 * @description
11265 * Shortcut method to perform `HEAD` request.
11266 *
11267 * @param {string} url Relative or absolute URL specifying the destination of the request
11268 * @param {Object=} config Optional configuration object
11269 * @returns {HttpPromise} Future object
11270 */
11271
11272 /**
11273 * @ngdoc method
11274 * @name $http#jsonp
11275 *
11276 * @description
11277 * Shortcut method to perform `JSONP` request.
11278 *
11279 * @param {string} url Relative or absolute URL specifying the destination of the request.
11280 * The name of the callback should be the string `JSON_CALLBACK`.
11281 * @param {Object=} config Optional configuration object
11282 * @returns {HttpPromise} Future object
11283 */
11284 createShortMethods('get', 'delete', 'head', 'jsonp');
11285
11286 /**
11287 * @ngdoc method
11288 * @name $http#post
11289 *
11290 * @description
11291 * Shortcut method to perform `POST` request.
11292 *
11293 * @param {string} url Relative or absolute URL specifying the destination of the request
11294 * @param {*} data Request content
11295 * @param {Object=} config Optional configuration object
11296 * @returns {HttpPromise} Future object
11297 */
11298
11299 /**
11300 * @ngdoc method
11301 * @name $http#put
11302 *
11303 * @description
11304 * Shortcut method to perform `PUT` request.
11305 *
11306 * @param {string} url Relative or absolute URL specifying the destination of the request
11307 * @param {*} data Request content
11308 * @param {Object=} config Optional configuration object
11309 * @returns {HttpPromise} Future object
11310 */
11311
11312 /**
11313 * @ngdoc method
11314 * @name $http#patch
11315 *
11316 * @description
11317 * Shortcut method to perform `PATCH` request.
11318 *
11319 * @param {string} url Relative or absolute URL specifying the destination of the request
11320 * @param {*} data Request content
11321 * @param {Object=} config Optional configuration object
11322 * @returns {HttpPromise} Future object
11323 */
11324 createShortMethodsWithData('post', 'put', 'patch');
11325
11326 /**
11327 * @ngdoc property
11328 * @name $http#defaults
11329 *
11330 * @description
11331 * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
11332 * default headers, withCredentials as well as request and response transformations.
11333 *
11334 * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
11335 */
11336 $http.defaults = defaults;
11337
11338
11339 return $http;
11340
11341
11342 function createShortMethods(names) {
11343 forEach(arguments, function(name) {
11344 $http[name] = function(url, config) {
11345 return $http(extend({}, config || {}, {
11346 method: name,
11347 url: url
11348 }));
11349 };
11350 });
11351 }
11352
11353
11354 function createShortMethodsWithData(name) {
11355 forEach(arguments, function(name) {
11356 $http[name] = function(url, data, config) {
11357 return $http(extend({}, config || {}, {
11358 method: name,
11359 url: url,
11360 data: data
11361 }));
11362 };
11363 });
11364 }
11365
11366
11367 /**
11368 * Makes the request.
11369 *
11370 * !!! ACCESSES CLOSURE VARS:
11371 * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
11372 */
11373 function sendReq(config, reqData) {
11374 var deferred = $q.defer(),
11375 promise = deferred.promise,
11376 cache,
11377 cachedResp,
11378 reqHeaders = config.headers,
11379 url = buildUrl(config.url, config.paramSerializer(config.params));
11380
11381 $http.pendingRequests.push(config);
11382 promise.then(removePendingReq, removePendingReq);
11383
11384
11385 if ((config.cache || defaults.cache) && config.cache !== false &&
11386 (config.method === 'GET' || config.method === 'JSONP')) {
11387 cache = isObject(config.cache) ? config.cache
11388 : isObject(defaults.cache) ? defaults.cache
11389 : defaultCache;
11390 }
11391
11392 if (cache) {
11393 cachedResp = cache.get(url);
11394 if (isDefined(cachedResp)) {
11395 if (isPromiseLike(cachedResp)) {
11396 // cached request has already been sent, but there is no response yet
11397 cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
11398 } else {
11399 // serving from cache
11400 if (isArray(cachedResp)) {
11401 resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
11402 } else {
11403 resolvePromise(cachedResp, 200, {}, 'OK');
11404 }
11405 }
11406 } else {
11407 // put the promise for the non-transformed response into cache as a placeholder
11408 cache.put(url, promise);
11409 }
11410 }
11411
11412
11413 // if we won't have the response in cache, set the xsrf headers and
11414 // send the request to the backend
11415 if (isUndefined(cachedResp)) {
11416 var xsrfValue = urlIsSameOrigin(config.url)
11417 ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
11418 : undefined;
11419 if (xsrfValue) {
11420 reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
11421 }
11422
11423 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
11424 config.withCredentials, config.responseType);
11425 }
11426
11427 return promise;
11428
11429
11430 /**
11431 * Callback registered to $httpBackend():
11432 * - caches the response if desired
11433 * - resolves the raw $http promise
11434 * - calls $apply
11435 */
11436 function done(status, response, headersString, statusText) {
11437 if (cache) {
11438 if (isSuccess(status)) {
11439 cache.put(url, [status, response, parseHeaders(headersString), statusText]);
11440 } else {
11441 // remove promise from the cache
11442 cache.remove(url);
11443 }
11444 }
11445
11446 function resolveHttpPromise() {
11447 resolvePromise(response, status, headersString, statusText);
11448 }
11449
11450 if (useApplyAsync) {
11451 $rootScope.$applyAsync(resolveHttpPromise);
11452 } else {
11453 resolveHttpPromise();
11454 if (!$rootScope.$$phase) $rootScope.$apply();
11455 }
11456 }
11457
11458
11459 /**
11460 * Resolves the raw $http promise.
11461 */
11462 function resolvePromise(response, status, headers, statusText) {
11463 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
11464 status = status >= -1 ? status : 0;
11465
11466 (isSuccess(status) ? deferred.resolve : deferred.reject)({
11467 data: response,
11468 status: status,
11469 headers: headersGetter(headers),
11470 config: config,
11471 statusText: statusText
11472 });
11473 }
11474
11475 function resolvePromiseWithResult(result) {
11476 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
11477 }
11478
11479 function removePendingReq() {
11480 var idx = $http.pendingRequests.indexOf(config);
11481 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
11482 }
11483 }
11484
11485
11486 function buildUrl(url, serializedParams) {
11487 if (serializedParams.length > 0) {
11488 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
11489 }
11490 return url;
11491 }
11492 }];
11493}
11494
11495/**
11496 * @ngdoc service
11497 * @name $xhrFactory
11498 *
11499 * @description
11500 * Factory function used to create XMLHttpRequest objects.
11501 *
11502 * Replace or decorate this service to create your own custom XMLHttpRequest objects.
11503 *
11504 * ```
11505 * angular.module('myApp', [])
11506 * .factory('$xhrFactory', function() {
11507 * return function createXhr(method, url) {
11508 * return new window.XMLHttpRequest({mozSystem: true});
11509 * };
11510 * });
11511 * ```
11512 *
11513 * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
11514 * @param {string} url URL of the request.
11515 */
11516function $xhrFactoryProvider() {
11517 this.$get = function() {
11518 return function createXhr() {
11519 return new window.XMLHttpRequest();
11520 };
11521 };
11522}
11523
11524/**
11525 * @ngdoc service
11526 * @name $httpBackend
11527 * @requires $window
11528 * @requires $document
11529 * @requires $xhrFactory
11530 *
11531 * @description
11532 * HTTP backend used by the {@link ng.$http service} that delegates to
11533 * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
11534 *
11535 * You should never need to use this service directly, instead use the higher-level abstractions:
11536 * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
11537 *
11538 * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
11539 * $httpBackend} which can be trained with responses.
11540 */
11541function $HttpBackendProvider() {
11542 this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
11543 return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
11544 }];
11545}
11546
11547function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
11548 // TODO(vojta): fix the signature
11549 return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
11550 $browser.$$incOutstandingRequestCount();
11551 url = url || $browser.url();
11552
11553 if (lowercase(method) == 'jsonp') {
11554 var callbackId = '_' + (callbacks.counter++).toString(36);
11555 callbacks[callbackId] = function(data) {
11556 callbacks[callbackId].data = data;
11557 callbacks[callbackId].called = true;
11558 };
11559
11560 var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
11561 callbackId, function(status, text) {
11562 completeRequest(callback, status, callbacks[callbackId].data, "", text);
11563 callbacks[callbackId] = noop;
11564 });
11565 } else {
11566
11567 var xhr = createXhr(method, url);
11568
11569 xhr.open(method, url, true);
11570 forEach(headers, function(value, key) {
11571 if (isDefined(value)) {
11572 xhr.setRequestHeader(key, value);
11573 }
11574 });
11575
11576 xhr.onload = function requestLoaded() {
11577 var statusText = xhr.statusText || '';
11578
11579 // responseText is the old-school way of retrieving response (supported by IE9)
11580 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
11581 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
11582
11583 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
11584 var status = xhr.status === 1223 ? 204 : xhr.status;
11585
11586 // fix status code when it is 0 (0 status is undocumented).
11587 // Occurs when accessing file resources or on Android 4.1 stock browser
11588 // while retrieving files from application cache.
11589 if (status === 0) {
11590 status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
11591 }
11592
11593 completeRequest(callback,
11594 status,
11595 response,
11596 xhr.getAllResponseHeaders(),
11597 statusText);
11598 };
11599
11600 var requestError = function() {
11601 // The response is always empty
11602 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
11603 completeRequest(callback, -1, null, null, '');
11604 };
11605
11606 xhr.onerror = requestError;
11607 xhr.onabort = requestError;
11608
11609 if (withCredentials) {
11610 xhr.withCredentials = true;
11611 }
11612
11613 if (responseType) {
11614 try {
11615 xhr.responseType = responseType;
11616 } catch (e) {
11617 // WebKit added support for the json responseType value on 09/03/2013
11618 // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
11619 // known to throw when setting the value "json" as the response type. Other older
11620 // browsers implementing the responseType
11621 //
11622 // The json response type can be ignored if not supported, because JSON payloads are
11623 // parsed on the client-side regardless.
11624 if (responseType !== 'json') {
11625 throw e;
11626 }
11627 }
11628 }
11629
11630 xhr.send(isUndefined(post) ? null : post);
11631 }
11632
11633 if (timeout > 0) {
11634 var timeoutId = $browserDefer(timeoutRequest, timeout);
11635 } else if (isPromiseLike(timeout)) {
11636 timeout.then(timeoutRequest);
11637 }
11638
11639
11640 function timeoutRequest() {
11641 jsonpDone && jsonpDone();
11642 xhr && xhr.abort();
11643 }
11644
11645 function completeRequest(callback, status, response, headersString, statusText) {
11646 // cancel timeout and subsequent timeout promise resolution
11647 if (isDefined(timeoutId)) {
11648 $browserDefer.cancel(timeoutId);
11649 }
11650 jsonpDone = xhr = null;
11651
11652 callback(status, response, headersString, statusText);
11653 $browser.$$completeOutstandingRequest(noop);
11654 }
11655 };
11656
11657 function jsonpReq(url, callbackId, done) {
11658 // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
11659 // - fetches local scripts via XHR and evals them
11660 // - adds and immediately removes script elements from the document
11661 var script = rawDocument.createElement('script'), callback = null;
11662 script.type = "text/javascript";
11663 script.src = url;
11664 script.async = true;
11665
11666 callback = function(event) {
11667 removeEventListenerFn(script, "load", callback);
11668 removeEventListenerFn(script, "error", callback);
11669 rawDocument.body.removeChild(script);
11670 script = null;
11671 var status = -1;
11672 var text = "unknown";
11673
11674 if (event) {
11675 if (event.type === "load" && !callbacks[callbackId].called) {
11676 event = { type: "error" };
11677 }
11678 text = event.type;
11679 status = event.type === "error" ? 404 : 200;
11680 }
11681
11682 if (done) {
11683 done(status, text);
11684 }
11685 };
11686
11687 addEventListenerFn(script, "load", callback);
11688 addEventListenerFn(script, "error", callback);
11689 rawDocument.body.appendChild(script);
11690 return callback;
11691 }
11692}
11693
11694var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
11695$interpolateMinErr.throwNoconcat = function(text) {
11696 throw $interpolateMinErr('noconcat',
11697 "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
11698 "interpolations that concatenate multiple expressions when a trusted value is " +
11699 "required. See http://docs.angularjs.org/api/ng.$sce", text);
11700};
11701
11702$interpolateMinErr.interr = function(text, err) {
11703 return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
11704};
11705
11706/**
11707 * @ngdoc provider
11708 * @name $interpolateProvider
11709 *
11710 * @description
11711 *
11712 * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
11713 *
11714 * <div class="alert alert-danger">
11715 * This feature is sometimes used to mix different markup languages, e.g. to wrap an Angular
11716 * template within a Python Jinja template (or any other template language). Mixing templating
11717 * languages is **very dangerous**. The embedding template language will not safely escape Angular
11718 * expressions, so any user-controlled values in the template will cause Cross Site Scripting (XSS)
11719 * security bugs!
11720 * </div>
11721 *
11722 * @example
11723<example name="custom-interpolation-markup" module="customInterpolationApp">
11724<file name="index.html">
11725<script>
11726 var customInterpolationApp = angular.module('customInterpolationApp', []);
11727
11728 customInterpolationApp.config(function($interpolateProvider) {
11729 $interpolateProvider.startSymbol('//');
11730 $interpolateProvider.endSymbol('//');
11731 });
11732
11733
11734 customInterpolationApp.controller('DemoController', function() {
11735 this.label = "This binding is brought you by // interpolation symbols.";
11736 });
11737</script>
11738<div ng-controller="DemoController as demo">
11739 //demo.label//
11740</div>
11741</file>
11742<file name="protractor.js" type="protractor">
11743 it('should interpolate binding with custom symbols', function() {
11744 expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
11745 });
11746</file>
11747</example>
11748 */
11749function $InterpolateProvider() {
11750 var startSymbol = '{{';
11751 var endSymbol = '}}';
11752
11753 /**
11754 * @ngdoc method
11755 * @name $interpolateProvider#startSymbol
11756 * @description
11757 * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
11758 *
11759 * @param {string=} value new value to set the starting symbol to.
11760 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11761 */
11762 this.startSymbol = function(value) {
11763 if (value) {
11764 startSymbol = value;
11765 return this;
11766 } else {
11767 return startSymbol;
11768 }
11769 };
11770
11771 /**
11772 * @ngdoc method
11773 * @name $interpolateProvider#endSymbol
11774 * @description
11775 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11776 *
11777 * @param {string=} value new value to set the ending symbol to.
11778 * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
11779 */
11780 this.endSymbol = function(value) {
11781 if (value) {
11782 endSymbol = value;
11783 return this;
11784 } else {
11785 return endSymbol;
11786 }
11787 };
11788
11789
11790 this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
11791 var startSymbolLength = startSymbol.length,
11792 endSymbolLength = endSymbol.length,
11793 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
11794 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
11795
11796 function escape(ch) {
11797 return '\\\\\\' + ch;
11798 }
11799
11800 function unescapeText(text) {
11801 return text.replace(escapedStartRegexp, startSymbol).
11802 replace(escapedEndRegexp, endSymbol);
11803 }
11804
11805 function stringify(value) {
11806 if (value == null) { // null || undefined
11807 return '';
11808 }
11809 switch (typeof value) {
11810 case 'string':
11811 break;
11812 case 'number':
11813 value = '' + value;
11814 break;
11815 default:
11816 value = toJson(value);
11817 }
11818
11819 return value;
11820 }
11821
11822 //TODO: this is the same as the constantWatchDelegate in parse.js
11823 function constantWatchDelegate(scope, listener, objectEquality, constantInterp) {
11824 var unwatch;
11825 return unwatch = scope.$watch(function constantInterpolateWatch(scope) {
11826 unwatch();
11827 return constantInterp(scope);
11828 }, listener, objectEquality);
11829 }
11830
11831 /**
11832 * @ngdoc service
11833 * @name $interpolate
11834 * @kind function
11835 *
11836 * @requires $parse
11837 * @requires $sce
11838 *
11839 * @description
11840 *
11841 * Compiles a string with markup into an interpolation function. This service is used by the
11842 * HTML {@link ng.$compile $compile} service for data binding. See
11843 * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11844 * interpolation markup.
11845 *
11846 *
11847 * ```js
11848 * var $interpolate = ...; // injected
11849 * var exp = $interpolate('Hello {{name | uppercase}}!');
11850 * expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11851 * ```
11852 *
11853 * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11854 * `true`, the interpolation function will return `undefined` unless all embedded expressions
11855 * evaluate to a value other than `undefined`.
11856 *
11857 * ```js
11858 * var $interpolate = ...; // injected
11859 * var context = {greeting: 'Hello', name: undefined };
11860 *
11861 * // default "forgiving" mode
11862 * var exp = $interpolate('{{greeting}} {{name}}!');
11863 * expect(exp(context)).toEqual('Hello !');
11864 *
11865 * // "allOrNothing" mode
11866 * exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11867 * expect(exp(context)).toBeUndefined();
11868 * context.name = 'Angular';
11869 * expect(exp(context)).toEqual('Hello Angular!');
11870 * ```
11871 *
11872 * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11873 *
11874 * ####Escaped Interpolation
11875 * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11876 * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11877 * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11878 * or binding.
11879 *
11880 * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11881 * degree, while also enabling code examples to work without relying on the
11882 * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11883 *
11884 * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11885 * replacing angle brackets (<, >) with &lt; and &gt; respectively, and replacing all
11886 * interpolation start/end markers with their escaped counterparts.**
11887 *
11888 * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11889 * output when the $interpolate service processes the text. So, for HTML elements interpolated
11890 * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11891 * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11892 * this is typically useful only when user-data is used in rendering a template from the server, or
11893 * when otherwise untrusted data is used by a directive.
11894 *
11895 * <example>
11896 * <file name="index.html">
11897 * <div ng-init="username='A user'">
11898 * <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11899 * </p>
11900 * <p><strong>{{username}}</strong> attempts to inject code which will deface the
11901 * application, but fails to accomplish their task, because the server has correctly
11902 * escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11903 * characters.</p>
11904 * <p>Instead, the result of the attempted script injection is visible, and can be removed
11905 * from the database by an administrator.</p>
11906 * </div>
11907 * </file>
11908 * </example>
11909 *
11910 * @param {string} text The text with markup to interpolate.
11911 * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11912 * embedded expression in order to return an interpolation function. Strings with no
11913 * embedded expression will return null for the interpolation function.
11914 * @param {string=} trustedContext when provided, the returned function passes the interpolated
11915 * result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11916 * trustedContext)} before returning it. Refer to the {@link ng.$sce $sce} service that
11917 * provides Strict Contextual Escaping for details.
11918 * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11919 * unless all embedded expressions evaluate to a value other than `undefined`.
11920 * @returns {function(context)} an interpolation function which is used to compute the
11921 * interpolated string. The function has these parameters:
11922 *
11923 * - `context`: evaluation context for all expressions embedded in the interpolated text
11924 */
11925 function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11926 // Provide a quick exit and simplified result function for text with no interpolation
11927 if (!text.length || text.indexOf(startSymbol) === -1) {
11928 var constantInterp;
11929 if (!mustHaveExpression) {
11930 var unescapedText = unescapeText(text);
11931 constantInterp = valueFn(unescapedText);
11932 constantInterp.exp = text;
11933 constantInterp.expressions = [];
11934 constantInterp.$$watchDelegate = constantWatchDelegate;
11935 }
11936 return constantInterp;
11937 }
11938
11939 allOrNothing = !!allOrNothing;
11940 var startIndex,
11941 endIndex,
11942 index = 0,
11943 expressions = [],
11944 parseFns = [],
11945 textLength = text.length,
11946 exp,
11947 concat = [],
11948 expressionPositions = [];
11949
11950 while (index < textLength) {
11951 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11952 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11953 if (index !== startIndex) {
11954 concat.push(unescapeText(text.substring(index, startIndex)));
11955 }
11956 exp = text.substring(startIndex + startSymbolLength, endIndex);
11957 expressions.push(exp);
11958 parseFns.push($parse(exp, parseStringifyInterceptor));
11959 index = endIndex + endSymbolLength;
11960 expressionPositions.push(concat.length);
11961 concat.push('');
11962 } else {
11963 // we did not find an interpolation, so we have to add the remainder to the separators array
11964 if (index !== textLength) {
11965 concat.push(unescapeText(text.substring(index)));
11966 }
11967 break;
11968 }
11969 }
11970
11971 // Concatenating expressions makes it hard to reason about whether some combination of
11972 // concatenated values are unsafe to use and could easily lead to XSS. By requiring that a
11973 // single expression be used for iframe[src], object[src], etc., we ensure that the value
11974 // that's used is assigned or constructed by some JS code somewhere that is more testable or
11975 // make it obvious that you bound the value to some user controlled value. This helps reduce
11976 // the load when auditing for XSS issues.
11977 if (trustedContext && concat.length > 1) {
11978 $interpolateMinErr.throwNoconcat(text);
11979 }
11980
11981 if (!mustHaveExpression || expressions.length) {
11982 var compute = function(values) {
11983 for (var i = 0, ii = expressions.length; i < ii; i++) {
11984 if (allOrNothing && isUndefined(values[i])) return;
11985 concat[expressionPositions[i]] = values[i];
11986 }
11987 return concat.join('');
11988 };
11989
11990 var getValue = function(value) {
11991 return trustedContext ?
11992 $sce.getTrusted(trustedContext, value) :
11993 $sce.valueOf(value);
11994 };
11995
11996 return extend(function interpolationFn(context) {
11997 var i = 0;
11998 var ii = expressions.length;
11999 var values = new Array(ii);
12000
12001 try {
12002 for (; i < ii; i++) {
12003 values[i] = parseFns[i](context);
12004 }
12005
12006 return compute(values);
12007 } catch (err) {
12008 $exceptionHandler($interpolateMinErr.interr(text, err));
12009 }
12010
12011 }, {
12012 // all of these properties are undocumented for now
12013 exp: text, //just for compatibility with regular watchers created via $watch
12014 expressions: expressions,
12015 $$watchDelegate: function(scope, listener) {
12016 var lastValue;
12017 return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
12018 var currValue = compute(values);
12019 if (isFunction(listener)) {
12020 listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
12021 }
12022 lastValue = currValue;
12023 });
12024 }
12025 });
12026 }
12027
12028 function parseStringifyInterceptor(value) {
12029 try {
12030 value = getValue(value);
12031 return allOrNothing && !isDefined(value) ? value : stringify(value);
12032 } catch (err) {
12033 $exceptionHandler($interpolateMinErr.interr(text, err));
12034 }
12035 }
12036 }
12037
12038
12039 /**
12040 * @ngdoc method
12041 * @name $interpolate#startSymbol
12042 * @description
12043 * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
12044 *
12045 * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
12046 * the symbol.
12047 *
12048 * @returns {string} start symbol.
12049 */
12050 $interpolate.startSymbol = function() {
12051 return startSymbol;
12052 };
12053
12054
12055 /**
12056 * @ngdoc method
12057 * @name $interpolate#endSymbol
12058 * @description
12059 * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
12060 *
12061 * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
12062 * the symbol.
12063 *
12064 * @returns {string} end symbol.
12065 */
12066 $interpolate.endSymbol = function() {
12067 return endSymbol;
12068 };
12069
12070 return $interpolate;
12071 }];
12072}
12073
12074function $IntervalProvider() {
12075 this.$get = ['$rootScope', '$window', '$q', '$$q', '$browser',
12076 function($rootScope, $window, $q, $$q, $browser) {
12077 var intervals = {};
12078
12079
12080 /**
12081 * @ngdoc service
12082 * @name $interval
12083 *
12084 * @description
12085 * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
12086 * milliseconds.
12087 *
12088 * The return value of registering an interval function is a promise. This promise will be
12089 * notified upon each tick of the interval, and will be resolved after `count` iterations, or
12090 * run indefinitely if `count` is not defined. The value of the notification will be the
12091 * number of iterations that have run.
12092 * To cancel an interval, call `$interval.cancel(promise)`.
12093 *
12094 * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
12095 * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
12096 * time.
12097 *
12098 * <div class="alert alert-warning">
12099 * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
12100 * with them. In particular they are not automatically destroyed when a controller's scope or a
12101 * directive's element are destroyed.
12102 * You should take this into consideration and make sure to always cancel the interval at the
12103 * appropriate moment. See the example below for more details on how and when to do this.
12104 * </div>
12105 *
12106 * @param {function()} fn A function that should be called repeatedly.
12107 * @param {number} delay Number of milliseconds between each function call.
12108 * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
12109 * indefinitely.
12110 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
12111 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
12112 * @param {...*=} Pass additional parameters to the executed function.
12113 * @returns {promise} A promise which will be notified on each iteration.
12114 *
12115 * @example
12116 * <example module="intervalExample">
12117 * <file name="index.html">
12118 * <script>
12119 * angular.module('intervalExample', [])
12120 * .controller('ExampleController', ['$scope', '$interval',
12121 * function($scope, $interval) {
12122 * $scope.format = 'M/d/yy h:mm:ss a';
12123 * $scope.blood_1 = 100;
12124 * $scope.blood_2 = 120;
12125 *
12126 * var stop;
12127 * $scope.fight = function() {
12128 * // Don't start a new fight if we are already fighting
12129 * if ( angular.isDefined(stop) ) return;
12130 *
12131 * stop = $interval(function() {
12132 * if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
12133 * $scope.blood_1 = $scope.blood_1 - 3;
12134 * $scope.blood_2 = $scope.blood_2 - 4;
12135 * } else {
12136 * $scope.stopFight();
12137 * }
12138 * }, 100);
12139 * };
12140 *
12141 * $scope.stopFight = function() {
12142 * if (angular.isDefined(stop)) {
12143 * $interval.cancel(stop);
12144 * stop = undefined;
12145 * }
12146 * };
12147 *
12148 * $scope.resetFight = function() {
12149 * $scope.blood_1 = 100;
12150 * $scope.blood_2 = 120;
12151 * };
12152 *
12153 * $scope.$on('$destroy', function() {
12154 * // Make sure that the interval is destroyed too
12155 * $scope.stopFight();
12156 * });
12157 * }])
12158 * // Register the 'myCurrentTime' directive factory method.
12159 * // We inject $interval and dateFilter service since the factory method is DI.
12160 * .directive('myCurrentTime', ['$interval', 'dateFilter',
12161 * function($interval, dateFilter) {
12162 * // return the directive link function. (compile function not needed)
12163 * return function(scope, element, attrs) {
12164 * var format, // date format
12165 * stopTime; // so that we can cancel the time updates
12166 *
12167 * // used to update the UI
12168 * function updateTime() {
12169 * element.text(dateFilter(new Date(), format));
12170 * }
12171 *
12172 * // watch the expression, and update the UI on change.
12173 * scope.$watch(attrs.myCurrentTime, function(value) {
12174 * format = value;
12175 * updateTime();
12176 * });
12177 *
12178 * stopTime = $interval(updateTime, 1000);
12179 *
12180 * // listen on DOM destroy (removal) event, and cancel the next UI update
12181 * // to prevent updating time after the DOM element was removed.
12182 * element.on('$destroy', function() {
12183 * $interval.cancel(stopTime);
12184 * });
12185 * }
12186 * }]);
12187 * </script>
12188 *
12189 * <div>
12190 * <div ng-controller="ExampleController">
12191 * <label>Date format: <input ng-model="format"></label> <hr/>
12192 * Current time is: <span my-current-time="format"></span>
12193 * <hr/>
12194 * Blood 1 : <font color='red'>{{blood_1}}</font>
12195 * Blood 2 : <font color='red'>{{blood_2}}</font>
12196 * <button type="button" data-ng-click="fight()">Fight</button>
12197 * <button type="button" data-ng-click="stopFight()">StopFight</button>
12198 * <button type="button" data-ng-click="resetFight()">resetFight</button>
12199 * </div>
12200 * </div>
12201 *
12202 * </file>
12203 * </example>
12204 */
12205 function interval(fn, delay, count, invokeApply) {
12206 var hasParams = arguments.length > 4,
12207 args = hasParams ? sliceArgs(arguments, 4) : [],
12208 setInterval = $window.setInterval,
12209 clearInterval = $window.clearInterval,
12210 iteration = 0,
12211 skipApply = (isDefined(invokeApply) && !invokeApply),
12212 deferred = (skipApply ? $$q : $q).defer(),
12213 promise = deferred.promise;
12214
12215 count = isDefined(count) ? count : 0;
12216
12217 promise.$$intervalId = setInterval(function tick() {
12218 if (skipApply) {
12219 $browser.defer(callback);
12220 } else {
12221 $rootScope.$evalAsync(callback);
12222 }
12223 deferred.notify(iteration++);
12224
12225 if (count > 0 && iteration >= count) {
12226 deferred.resolve(iteration);
12227 clearInterval(promise.$$intervalId);
12228 delete intervals[promise.$$intervalId];
12229 }
12230
12231 if (!skipApply) $rootScope.$apply();
12232
12233 }, delay);
12234
12235 intervals[promise.$$intervalId] = deferred;
12236
12237 return promise;
12238
12239 function callback() {
12240 if (!hasParams) {
12241 fn(iteration);
12242 } else {
12243 fn.apply(null, args);
12244 }
12245 }
12246 }
12247
12248
12249 /**
12250 * @ngdoc method
12251 * @name $interval#cancel
12252 *
12253 * @description
12254 * Cancels a task associated with the `promise`.
12255 *
12256 * @param {Promise=} promise returned by the `$interval` function.
12257 * @returns {boolean} Returns `true` if the task was successfully canceled.
12258 */
12259 interval.cancel = function(promise) {
12260 if (promise && promise.$$intervalId in intervals) {
12261 intervals[promise.$$intervalId].reject('canceled');
12262 $window.clearInterval(promise.$$intervalId);
12263 delete intervals[promise.$$intervalId];
12264 return true;
12265 }
12266 return false;
12267 };
12268
12269 return interval;
12270 }];
12271}
12272
12273/**
12274 * @ngdoc service
12275 * @name $locale
12276 *
12277 * @description
12278 * $locale service provides localization rules for various Angular components. As of right now the
12279 * only public api is:
12280 *
12281 * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
12282 */
12283
12284var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
12285 DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
12286var $locationMinErr = minErr('$location');
12287
12288
12289/**
12290 * Encode path using encodeUriSegment, ignoring forward slashes
12291 *
12292 * @param {string} path Path to encode
12293 * @returns {string}
12294 */
12295function encodePath(path) {
12296 var segments = path.split('/'),
12297 i = segments.length;
12298
12299 while (i--) {
12300 segments[i] = encodeUriSegment(segments[i]);
12301 }
12302
12303 return segments.join('/');
12304}
12305
12306function parseAbsoluteUrl(absoluteUrl, locationObj) {
12307 var parsedUrl = urlResolve(absoluteUrl);
12308
12309 locationObj.$$protocol = parsedUrl.protocol;
12310 locationObj.$$host = parsedUrl.hostname;
12311 locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
12312}
12313
12314
12315function parseAppUrl(relativeUrl, locationObj) {
12316 var prefixed = (relativeUrl.charAt(0) !== '/');
12317 if (prefixed) {
12318 relativeUrl = '/' + relativeUrl;
12319 }
12320 var match = urlResolve(relativeUrl);
12321 locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
12322 match.pathname.substring(1) : match.pathname);
12323 locationObj.$$search = parseKeyValue(match.search);
12324 locationObj.$$hash = decodeURIComponent(match.hash);
12325
12326 // make sure path starts with '/';
12327 if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
12328 locationObj.$$path = '/' + locationObj.$$path;
12329 }
12330}
12331
12332
12333/**
12334 *
12335 * @param {string} begin
12336 * @param {string} whole
12337 * @returns {string} returns text from whole after begin or undefined if it does not begin with
12338 * expected string.
12339 */
12340function beginsWith(begin, whole) {
12341 if (whole.indexOf(begin) === 0) {
12342 return whole.substr(begin.length);
12343 }
12344}
12345
12346
12347function stripHash(url) {
12348 var index = url.indexOf('#');
12349 return index == -1 ? url : url.substr(0, index);
12350}
12351
12352function trimEmptyHash(url) {
12353 return url.replace(/(#.+)|#$/, '$1');
12354}
12355
12356
12357function stripFile(url) {
12358 return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
12359}
12360
12361/* return the server only (scheme://host:port) */
12362function serverBase(url) {
12363 return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
12364}
12365
12366
12367/**
12368 * LocationHtml5Url represents an url
12369 * This object is exposed as $location service when HTML5 mode is enabled and supported
12370 *
12371 * @constructor
12372 * @param {string} appBase application base URL
12373 * @param {string} appBaseNoFile application base URL stripped of any filename
12374 * @param {string} basePrefix url path prefix
12375 */
12376function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
12377 this.$$html5 = true;
12378 basePrefix = basePrefix || '';
12379 parseAbsoluteUrl(appBase, this);
12380
12381
12382 /**
12383 * Parse given html5 (regular) url string into properties
12384 * @param {string} url HTML5 url
12385 * @private
12386 */
12387 this.$$parse = function(url) {
12388 var pathUrl = beginsWith(appBaseNoFile, url);
12389 if (!isString(pathUrl)) {
12390 throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
12391 appBaseNoFile);
12392 }
12393
12394 parseAppUrl(pathUrl, this);
12395
12396 if (!this.$$path) {
12397 this.$$path = '/';
12398 }
12399
12400 this.$$compose();
12401 };
12402
12403 /**
12404 * Compose url and update `absUrl` property
12405 * @private
12406 */
12407 this.$$compose = function() {
12408 var search = toKeyValue(this.$$search),
12409 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12410
12411 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12412 this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
12413 };
12414
12415 this.$$parseLinkUrl = function(url, relHref) {
12416 if (relHref && relHref[0] === '#') {
12417 // special case for links to hash fragments:
12418 // keep the old url and only replace the hash fragment
12419 this.hash(relHref.slice(1));
12420 return true;
12421 }
12422 var appUrl, prevAppUrl;
12423 var rewrittenUrl;
12424
12425 if (isDefined(appUrl = beginsWith(appBase, url))) {
12426 prevAppUrl = appUrl;
12427 if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
12428 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
12429 } else {
12430 rewrittenUrl = appBase + prevAppUrl;
12431 }
12432 } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
12433 rewrittenUrl = appBaseNoFile + appUrl;
12434 } else if (appBaseNoFile == url + '/') {
12435 rewrittenUrl = appBaseNoFile;
12436 }
12437 if (rewrittenUrl) {
12438 this.$$parse(rewrittenUrl);
12439 }
12440 return !!rewrittenUrl;
12441 };
12442}
12443
12444
12445/**
12446 * LocationHashbangUrl represents url
12447 * This object is exposed as $location service when developer doesn't opt into html5 mode.
12448 * It also serves as the base class for html5 mode fallback on legacy browsers.
12449 *
12450 * @constructor
12451 * @param {string} appBase application base URL
12452 * @param {string} appBaseNoFile application base URL stripped of any filename
12453 * @param {string} hashPrefix hashbang prefix
12454 */
12455function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
12456
12457 parseAbsoluteUrl(appBase, this);
12458
12459
12460 /**
12461 * Parse given hashbang url into properties
12462 * @param {string} url Hashbang url
12463 * @private
12464 */
12465 this.$$parse = function(url) {
12466 var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
12467 var withoutHashUrl;
12468
12469 if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
12470
12471 // The rest of the url starts with a hash so we have
12472 // got either a hashbang path or a plain hash fragment
12473 withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
12474 if (isUndefined(withoutHashUrl)) {
12475 // There was no hashbang prefix so we just have a hash fragment
12476 withoutHashUrl = withoutBaseUrl;
12477 }
12478
12479 } else {
12480 // There was no hashbang path nor hash fragment:
12481 // If we are in HTML5 mode we use what is left as the path;
12482 // Otherwise we ignore what is left
12483 if (this.$$html5) {
12484 withoutHashUrl = withoutBaseUrl;
12485 } else {
12486 withoutHashUrl = '';
12487 if (isUndefined(withoutBaseUrl)) {
12488 appBase = url;
12489 this.replace();
12490 }
12491 }
12492 }
12493
12494 parseAppUrl(withoutHashUrl, this);
12495
12496 this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
12497
12498 this.$$compose();
12499
12500 /*
12501 * In Windows, on an anchor node on documents loaded from
12502 * the filesystem, the browser will return a pathname
12503 * prefixed with the drive name ('/C:/path') when a
12504 * pathname without a drive is set:
12505 * * a.setAttribute('href', '/foo')
12506 * * a.pathname === '/C:/foo' //true
12507 *
12508 * Inside of Angular, we're always using pathnames that
12509 * do not include drive names for routing.
12510 */
12511 function removeWindowsDriveName(path, url, base) {
12512 /*
12513 Matches paths for file protocol on windows,
12514 such as /C:/foo/bar, and captures only /foo/bar.
12515 */
12516 var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
12517
12518 var firstPathSegmentMatch;
12519
12520 //Get the relative path from the input URL.
12521 if (url.indexOf(base) === 0) {
12522 url = url.replace(base, '');
12523 }
12524
12525 // The input URL intentionally contains a first path segment that ends with a colon.
12526 if (windowsFilePathExp.exec(url)) {
12527 return path;
12528 }
12529
12530 firstPathSegmentMatch = windowsFilePathExp.exec(path);
12531 return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
12532 }
12533 };
12534
12535 /**
12536 * Compose hashbang url and update `absUrl` property
12537 * @private
12538 */
12539 this.$$compose = function() {
12540 var search = toKeyValue(this.$$search),
12541 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12542
12543 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12544 this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
12545 };
12546
12547 this.$$parseLinkUrl = function(url, relHref) {
12548 if (stripHash(appBase) == stripHash(url)) {
12549 this.$$parse(url);
12550 return true;
12551 }
12552 return false;
12553 };
12554}
12555
12556
12557/**
12558 * LocationHashbangUrl represents url
12559 * This object is exposed as $location service when html5 history api is enabled but the browser
12560 * does not support it.
12561 *
12562 * @constructor
12563 * @param {string} appBase application base URL
12564 * @param {string} appBaseNoFile application base URL stripped of any filename
12565 * @param {string} hashPrefix hashbang prefix
12566 */
12567function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
12568 this.$$html5 = true;
12569 LocationHashbangUrl.apply(this, arguments);
12570
12571 this.$$parseLinkUrl = function(url, relHref) {
12572 if (relHref && relHref[0] === '#') {
12573 // special case for links to hash fragments:
12574 // keep the old url and only replace the hash fragment
12575 this.hash(relHref.slice(1));
12576 return true;
12577 }
12578
12579 var rewrittenUrl;
12580 var appUrl;
12581
12582 if (appBase == stripHash(url)) {
12583 rewrittenUrl = url;
12584 } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
12585 rewrittenUrl = appBase + hashPrefix + appUrl;
12586 } else if (appBaseNoFile === url + '/') {
12587 rewrittenUrl = appBaseNoFile;
12588 }
12589 if (rewrittenUrl) {
12590 this.$$parse(rewrittenUrl);
12591 }
12592 return !!rewrittenUrl;
12593 };
12594
12595 this.$$compose = function() {
12596 var search = toKeyValue(this.$$search),
12597 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
12598
12599 this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
12600 // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
12601 this.$$absUrl = appBase + hashPrefix + this.$$url;
12602 };
12603
12604}
12605
12606
12607var locationPrototype = {
12608
12609 /**
12610 * Are we in html5 mode?
12611 * @private
12612 */
12613 $$html5: false,
12614
12615 /**
12616 * Has any change been replacing?
12617 * @private
12618 */
12619 $$replace: false,
12620
12621 /**
12622 * @ngdoc method
12623 * @name $location#absUrl
12624 *
12625 * @description
12626 * This method is getter only.
12627 *
12628 * Return full url representation with all segments encoded according to rules specified in
12629 * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
12630 *
12631 *
12632 * ```js
12633 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12634 * var absUrl = $location.absUrl();
12635 * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
12636 * ```
12637 *
12638 * @return {string} full url
12639 */
12640 absUrl: locationGetter('$$absUrl'),
12641
12642 /**
12643 * @ngdoc method
12644 * @name $location#url
12645 *
12646 * @description
12647 * This method is getter / setter.
12648 *
12649 * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
12650 *
12651 * Change path, search and hash, when called with parameter and return `$location`.
12652 *
12653 *
12654 * ```js
12655 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12656 * var url = $location.url();
12657 * // => "/some/path?foo=bar&baz=xoxo"
12658 * ```
12659 *
12660 * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
12661 * @return {string} url
12662 */
12663 url: function(url) {
12664 if (isUndefined(url)) {
12665 return this.$$url;
12666 }
12667
12668 var match = PATH_MATCH.exec(url);
12669 if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
12670 if (match[2] || match[1] || url === '') this.search(match[3] || '');
12671 this.hash(match[5] || '');
12672
12673 return this;
12674 },
12675
12676 /**
12677 * @ngdoc method
12678 * @name $location#protocol
12679 *
12680 * @description
12681 * This method is getter only.
12682 *
12683 * Return protocol of current url.
12684 *
12685 *
12686 * ```js
12687 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12688 * var protocol = $location.protocol();
12689 * // => "http"
12690 * ```
12691 *
12692 * @return {string} protocol of current url
12693 */
12694 protocol: locationGetter('$$protocol'),
12695
12696 /**
12697 * @ngdoc method
12698 * @name $location#host
12699 *
12700 * @description
12701 * This method is getter only.
12702 *
12703 * Return host of current url.
12704 *
12705 * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
12706 *
12707 *
12708 * ```js
12709 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12710 * var host = $location.host();
12711 * // => "example.com"
12712 *
12713 * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
12714 * host = $location.host();
12715 * // => "example.com"
12716 * host = location.host;
12717 * // => "example.com:8080"
12718 * ```
12719 *
12720 * @return {string} host of current url.
12721 */
12722 host: locationGetter('$$host'),
12723
12724 /**
12725 * @ngdoc method
12726 * @name $location#port
12727 *
12728 * @description
12729 * This method is getter only.
12730 *
12731 * Return port of current url.
12732 *
12733 *
12734 * ```js
12735 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12736 * var port = $location.port();
12737 * // => 80
12738 * ```
12739 *
12740 * @return {Number} port
12741 */
12742 port: locationGetter('$$port'),
12743
12744 /**
12745 * @ngdoc method
12746 * @name $location#path
12747 *
12748 * @description
12749 * This method is getter / setter.
12750 *
12751 * Return path of current url when called without any parameter.
12752 *
12753 * Change path when called with parameter and return `$location`.
12754 *
12755 * Note: Path should always begin with forward slash (/), this method will add the forward slash
12756 * if it is missing.
12757 *
12758 *
12759 * ```js
12760 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12761 * var path = $location.path();
12762 * // => "/some/path"
12763 * ```
12764 *
12765 * @param {(string|number)=} path New path
12766 * @return {string} path
12767 */
12768 path: locationGetterSetter('$$path', function(path) {
12769 path = path !== null ? path.toString() : '';
12770 return path.charAt(0) == '/' ? path : '/' + path;
12771 }),
12772
12773 /**
12774 * @ngdoc method
12775 * @name $location#search
12776 *
12777 * @description
12778 * This method is getter / setter.
12779 *
12780 * Return search part (as object) of current url when called without any parameter.
12781 *
12782 * Change search part when called with parameter and return `$location`.
12783 *
12784 *
12785 * ```js
12786 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
12787 * var searchObject = $location.search();
12788 * // => {foo: 'bar', baz: 'xoxo'}
12789 *
12790 * // set foo to 'yipee'
12791 * $location.search('foo', 'yipee');
12792 * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
12793 * ```
12794 *
12795 * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
12796 * hash object.
12797 *
12798 * When called with a single argument the method acts as a setter, setting the `search` component
12799 * of `$location` to the specified value.
12800 *
12801 * If the argument is a hash object containing an array of values, these values will be encoded
12802 * as duplicate search parameters in the url.
12803 *
12804 * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
12805 * will override only a single search property.
12806 *
12807 * If `paramValue` is an array, it will override the property of the `search` component of
12808 * `$location` specified via the first argument.
12809 *
12810 * If `paramValue` is `null`, the property specified via the first argument will be deleted.
12811 *
12812 * If `paramValue` is `true`, the property specified via the first argument will be added with no
12813 * value nor trailing equal sign.
12814 *
12815 * @return {Object} If called with no arguments returns the parsed `search` object. If called with
12816 * one or more arguments returns `$location` object itself.
12817 */
12818 search: function(search, paramValue) {
12819 switch (arguments.length) {
12820 case 0:
12821 return this.$$search;
12822 case 1:
12823 if (isString(search) || isNumber(search)) {
12824 search = search.toString();
12825 this.$$search = parseKeyValue(search);
12826 } else if (isObject(search)) {
12827 search = copy(search, {});
12828 // remove object undefined or null properties
12829 forEach(search, function(value, key) {
12830 if (value == null) delete search[key];
12831 });
12832
12833 this.$$search = search;
12834 } else {
12835 throw $locationMinErr('isrcharg',
12836 'The first argument of the `$location#search()` call must be a string or an object.');
12837 }
12838 break;
12839 default:
12840 if (isUndefined(paramValue) || paramValue === null) {
12841 delete this.$$search[search];
12842 } else {
12843 this.$$search[search] = paramValue;
12844 }
12845 }
12846
12847 this.$$compose();
12848 return this;
12849 },
12850
12851 /**
12852 * @ngdoc method
12853 * @name $location#hash
12854 *
12855 * @description
12856 * This method is getter / setter.
12857 *
12858 * Returns the hash fragment when called without any parameters.
12859 *
12860 * Changes the hash fragment when called with a parameter and returns `$location`.
12861 *
12862 *
12863 * ```js
12864 * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12865 * var hash = $location.hash();
12866 * // => "hashValue"
12867 * ```
12868 *
12869 * @param {(string|number)=} hash New hash fragment
12870 * @return {string} hash
12871 */
12872 hash: locationGetterSetter('$$hash', function(hash) {
12873 return hash !== null ? hash.toString() : '';
12874 }),
12875
12876 /**
12877 * @ngdoc method
12878 * @name $location#replace
12879 *
12880 * @description
12881 * If called, all changes to $location during the current `$digest` will replace the current history
12882 * record, instead of adding a new one.
12883 */
12884 replace: function() {
12885 this.$$replace = true;
12886 return this;
12887 }
12888};
12889
12890forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12891 Location.prototype = Object.create(locationPrototype);
12892
12893 /**
12894 * @ngdoc method
12895 * @name $location#state
12896 *
12897 * @description
12898 * This method is getter / setter.
12899 *
12900 * Return the history state object when called without any parameter.
12901 *
12902 * Change the history state object when called with one parameter and return `$location`.
12903 * The state object is later passed to `pushState` or `replaceState`.
12904 *
12905 * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12906 * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12907 * older browsers (like IE9 or Android < 4.0), don't use this method.
12908 *
12909 * @param {object=} state State object for pushState or replaceState
12910 * @return {object} state
12911 */
12912 Location.prototype.state = function(state) {
12913 if (!arguments.length) {
12914 return this.$$state;
12915 }
12916
12917 if (Location !== LocationHtml5Url || !this.$$html5) {
12918 throw $locationMinErr('nostate', 'History API state support is available only ' +
12919 'in HTML5 mode and only in browsers supporting HTML5 History API');
12920 }
12921 // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12922 // but we're changing the $$state reference to $browser.state() during the $digest
12923 // so the modification window is narrow.
12924 this.$$state = isUndefined(state) ? null : state;
12925
12926 return this;
12927 };
12928});
12929
12930
12931function locationGetter(property) {
12932 return function() {
12933 return this[property];
12934 };
12935}
12936
12937
12938function locationGetterSetter(property, preprocess) {
12939 return function(value) {
12940 if (isUndefined(value)) {
12941 return this[property];
12942 }
12943
12944 this[property] = preprocess(value);
12945 this.$$compose();
12946
12947 return this;
12948 };
12949}
12950
12951
12952/**
12953 * @ngdoc service
12954 * @name $location
12955 *
12956 * @requires $rootElement
12957 *
12958 * @description
12959 * The $location service parses the URL in the browser address bar (based on the
12960 * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12961 * available to your application. Changes to the URL in the address bar are reflected into
12962 * $location service and changes to $location are reflected into the browser address bar.
12963 *
12964 * **The $location service:**
12965 *
12966 * - Exposes the current URL in the browser address bar, so you can
12967 * - Watch and observe the URL.
12968 * - Change the URL.
12969 * - Synchronizes the URL with the browser when the user
12970 * - Changes the address bar.
12971 * - Clicks the back or forward button (or clicks a History link).
12972 * - Clicks on a link.
12973 * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12974 *
12975 * For more information see {@link guide/$location Developer Guide: Using $location}
12976 */
12977
12978/**
12979 * @ngdoc provider
12980 * @name $locationProvider
12981 * @description
12982 * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12983 */
12984function $LocationProvider() {
12985 var hashPrefix = '',
12986 html5Mode = {
12987 enabled: false,
12988 requireBase: true,
12989 rewriteLinks: true
12990 };
12991
12992 /**
12993 * @ngdoc method
12994 * @name $locationProvider#hashPrefix
12995 * @description
12996 * @param {string=} prefix Prefix for hash part (containing path and search)
12997 * @returns {*} current value if used as getter or itself (chaining) if used as setter
12998 */
12999 this.hashPrefix = function(prefix) {
13000 if (isDefined(prefix)) {
13001 hashPrefix = prefix;
13002 return this;
13003 } else {
13004 return hashPrefix;
13005 }
13006 };
13007
13008 /**
13009 * @ngdoc method
13010 * @name $locationProvider#html5Mode
13011 * @description
13012 * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
13013 * If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
13014 * properties:
13015 * - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
13016 * change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
13017 * support `pushState`.
13018 * - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
13019 * whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
13020 * true, and a base tag is not present, an error will be thrown when `$location` is injected.
13021 * See the {@link guide/$location $location guide for more information}
13022 * - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
13023 * enables/disables url rewriting for relative links.
13024 *
13025 * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
13026 */
13027 this.html5Mode = function(mode) {
13028 if (isBoolean(mode)) {
13029 html5Mode.enabled = mode;
13030 return this;
13031 } else if (isObject(mode)) {
13032
13033 if (isBoolean(mode.enabled)) {
13034 html5Mode.enabled = mode.enabled;
13035 }
13036
13037 if (isBoolean(mode.requireBase)) {
13038 html5Mode.requireBase = mode.requireBase;
13039 }
13040
13041 if (isBoolean(mode.rewriteLinks)) {
13042 html5Mode.rewriteLinks = mode.rewriteLinks;
13043 }
13044
13045 return this;
13046 } else {
13047 return html5Mode;
13048 }
13049 };
13050
13051 /**
13052 * @ngdoc event
13053 * @name $location#$locationChangeStart
13054 * @eventType broadcast on root scope
13055 * @description
13056 * Broadcasted before a URL will change.
13057 *
13058 * This change can be prevented by calling
13059 * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
13060 * details about event object. Upon successful change
13061 * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
13062 *
13063 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
13064 * the browser supports the HTML5 History API.
13065 *
13066 * @param {Object} angularEvent Synthetic event object.
13067 * @param {string} newUrl New URL
13068 * @param {string=} oldUrl URL that was before it was changed.
13069 * @param {string=} newState New history state object
13070 * @param {string=} oldState History state object that was before it was changed.
13071 */
13072
13073 /**
13074 * @ngdoc event
13075 * @name $location#$locationChangeSuccess
13076 * @eventType broadcast on root scope
13077 * @description
13078 * Broadcasted after a URL was changed.
13079 *
13080 * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
13081 * the browser supports the HTML5 History API.
13082 *
13083 * @param {Object} angularEvent Synthetic event object.
13084 * @param {string} newUrl New URL
13085 * @param {string=} oldUrl URL that was before it was changed.
13086 * @param {string=} newState New history state object
13087 * @param {string=} oldState History state object that was before it was changed.
13088 */
13089
13090 this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
13091 function($rootScope, $browser, $sniffer, $rootElement, $window) {
13092 var $location,
13093 LocationMode,
13094 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
13095 initialUrl = $browser.url(),
13096 appBase;
13097
13098 if (html5Mode.enabled) {
13099 if (!baseHref && html5Mode.requireBase) {
13100 throw $locationMinErr('nobase',
13101 "$location in HTML5 mode requires a <base> tag to be present!");
13102 }
13103 appBase = serverBase(initialUrl) + (baseHref || '/');
13104 LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
13105 } else {
13106 appBase = stripHash(initialUrl);
13107 LocationMode = LocationHashbangUrl;
13108 }
13109 var appBaseNoFile = stripFile(appBase);
13110
13111 $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
13112 $location.$$parseLinkUrl(initialUrl, initialUrl);
13113
13114 $location.$$state = $browser.state();
13115
13116 var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
13117
13118 function setBrowserUrlWithFallback(url, replace, state) {
13119 var oldUrl = $location.url();
13120 var oldState = $location.$$state;
13121 try {
13122 $browser.url(url, replace, state);
13123
13124 // Make sure $location.state() returns referentially identical (not just deeply equal)
13125 // state object; this makes possible quick checking if the state changed in the digest
13126 // loop. Checking deep equality would be too expensive.
13127 $location.$$state = $browser.state();
13128 } catch (e) {
13129 // Restore old values if pushState fails
13130 $location.url(oldUrl);
13131 $location.$$state = oldState;
13132
13133 throw e;
13134 }
13135 }
13136
13137 $rootElement.on('click', function(event) {
13138 // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
13139 // currently we open nice url link and redirect then
13140
13141 if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
13142
13143 var elm = jqLite(event.target);
13144
13145 // traverse the DOM up to find first A tag
13146 while (nodeName_(elm[0]) !== 'a') {
13147 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
13148 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
13149 }
13150
13151 var absHref = elm.prop('href');
13152 // get the actual href attribute - see
13153 // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
13154 var relHref = elm.attr('href') || elm.attr('xlink:href');
13155
13156 if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
13157 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
13158 // an animation.
13159 absHref = urlResolve(absHref.animVal).href;
13160 }
13161
13162 // Ignore when url is started with javascript: or mailto:
13163 if (IGNORE_URI_REGEXP.test(absHref)) return;
13164
13165 if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
13166 if ($location.$$parseLinkUrl(absHref, relHref)) {
13167 // We do a preventDefault for all urls that are part of the angular application,
13168 // in html5mode and also without, so that we are able to abort navigation without
13169 // getting double entries in the location history.
13170 event.preventDefault();
13171 // update location manually
13172 if ($location.absUrl() != $browser.url()) {
13173 $rootScope.$apply();
13174 // hack to work around FF6 bug 684208 when scenario runner clicks on links
13175 $window.angular['ff-684208-preventDefault'] = true;
13176 }
13177 }
13178 }
13179 });
13180
13181
13182 // rewrite hashbang url <> html5 url
13183 if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
13184 $browser.url($location.absUrl(), true);
13185 }
13186
13187 var initializing = true;
13188
13189 // update $location when $browser url changes
13190 $browser.onUrlChange(function(newUrl, newState) {
13191
13192 if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
13193 // If we are navigating outside of the app then force a reload
13194 $window.location.href = newUrl;
13195 return;
13196 }
13197
13198 $rootScope.$evalAsync(function() {
13199 var oldUrl = $location.absUrl();
13200 var oldState = $location.$$state;
13201 var defaultPrevented;
13202 newUrl = trimEmptyHash(newUrl);
13203 $location.$$parse(newUrl);
13204 $location.$$state = newState;
13205
13206 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
13207 newState, oldState).defaultPrevented;
13208
13209 // if the location was changed by a `$locationChangeStart` handler then stop
13210 // processing this location change
13211 if ($location.absUrl() !== newUrl) return;
13212
13213 if (defaultPrevented) {
13214 $location.$$parse(oldUrl);
13215 $location.$$state = oldState;
13216 setBrowserUrlWithFallback(oldUrl, false, oldState);
13217 } else {
13218 initializing = false;
13219 afterLocationChange(oldUrl, oldState);
13220 }
13221 });
13222 if (!$rootScope.$$phase) $rootScope.$digest();
13223 });
13224
13225 // update browser
13226 $rootScope.$watch(function $locationWatch() {
13227 var oldUrl = trimEmptyHash($browser.url());
13228 var newUrl = trimEmptyHash($location.absUrl());
13229 var oldState = $browser.state();
13230 var currentReplace = $location.$$replace;
13231 var urlOrStateChanged = oldUrl !== newUrl ||
13232 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
13233
13234 if (initializing || urlOrStateChanged) {
13235 initializing = false;
13236
13237 $rootScope.$evalAsync(function() {
13238 var newUrl = $location.absUrl();
13239 var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
13240 $location.$$state, oldState).defaultPrevented;
13241
13242 // if the location was changed by a `$locationChangeStart` handler then stop
13243 // processing this location change
13244 if ($location.absUrl() !== newUrl) return;
13245
13246 if (defaultPrevented) {
13247 $location.$$parse(oldUrl);
13248 $location.$$state = oldState;
13249 } else {
13250 if (urlOrStateChanged) {
13251 setBrowserUrlWithFallback(newUrl, currentReplace,
13252 oldState === $location.$$state ? null : $location.$$state);
13253 }
13254 afterLocationChange(oldUrl, oldState);
13255 }
13256 });
13257 }
13258
13259 $location.$$replace = false;
13260
13261 // we don't need to return anything because $evalAsync will make the digest loop dirty when
13262 // there is a change
13263 });
13264
13265 return $location;
13266
13267 function afterLocationChange(oldUrl, oldState) {
13268 $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
13269 $location.$$state, oldState);
13270 }
13271}];
13272}
13273
13274/**
13275 * @ngdoc service
13276 * @name $log
13277 * @requires $window
13278 *
13279 * @description
13280 * Simple service for logging. Default implementation safely writes the message
13281 * into the browser's console (if present).
13282 *
13283 * The main purpose of this service is to simplify debugging and troubleshooting.
13284 *
13285 * The default is to log `debug` messages. You can use
13286 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
13287 *
13288 * @example
13289 <example module="logExample">
13290 <file name="script.js">
13291 angular.module('logExample', [])
13292 .controller('LogController', ['$scope', '$log', function($scope, $log) {
13293 $scope.$log = $log;
13294 $scope.message = 'Hello World!';
13295 }]);
13296 </file>
13297 <file name="index.html">
13298 <div ng-controller="LogController">
13299 <p>Reload this page with open console, enter text and hit the log button...</p>
13300 <label>Message:
13301 <input type="text" ng-model="message" /></label>
13302 <button ng-click="$log.log(message)">log</button>
13303 <button ng-click="$log.warn(message)">warn</button>
13304 <button ng-click="$log.info(message)">info</button>
13305 <button ng-click="$log.error(message)">error</button>
13306 <button ng-click="$log.debug(message)">debug</button>
13307 </div>
13308 </file>
13309 </example>
13310 */
13311
13312/**
13313 * @ngdoc provider
13314 * @name $logProvider
13315 * @description
13316 * Use the `$logProvider` to configure how the application logs messages
13317 */
13318function $LogProvider() {
13319 var debug = true,
13320 self = this;
13321
13322 /**
13323 * @ngdoc method
13324 * @name $logProvider#debugEnabled
13325 * @description
13326 * @param {boolean=} flag enable or disable debug level messages
13327 * @returns {*} current value if used as getter or itself (chaining) if used as setter
13328 */
13329 this.debugEnabled = function(flag) {
13330 if (isDefined(flag)) {
13331 debug = flag;
13332 return this;
13333 } else {
13334 return debug;
13335 }
13336 };
13337
13338 this.$get = ['$window', function($window) {
13339 return {
13340 /**
13341 * @ngdoc method
13342 * @name $log#log
13343 *
13344 * @description
13345 * Write a log message
13346 */
13347 log: consoleLog('log'),
13348
13349 /**
13350 * @ngdoc method
13351 * @name $log#info
13352 *
13353 * @description
13354 * Write an information message
13355 */
13356 info: consoleLog('info'),
13357
13358 /**
13359 * @ngdoc method
13360 * @name $log#warn
13361 *
13362 * @description
13363 * Write a warning message
13364 */
13365 warn: consoleLog('warn'),
13366
13367 /**
13368 * @ngdoc method
13369 * @name $log#error
13370 *
13371 * @description
13372 * Write an error message
13373 */
13374 error: consoleLog('error'),
13375
13376 /**
13377 * @ngdoc method
13378 * @name $log#debug
13379 *
13380 * @description
13381 * Write a debug message
13382 */
13383 debug: (function() {
13384 var fn = consoleLog('debug');
13385
13386 return function() {
13387 if (debug) {
13388 fn.apply(self, arguments);
13389 }
13390 };
13391 }())
13392 };
13393
13394 function formatError(arg) {
13395 if (arg instanceof Error) {
13396 if (arg.stack) {
13397 arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
13398 ? 'Error: ' + arg.message + '\n' + arg.stack
13399 : arg.stack;
13400 } else if (arg.sourceURL) {
13401 arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
13402 }
13403 }
13404 return arg;
13405 }
13406
13407 function consoleLog(type) {
13408 var console = $window.console || {},
13409 logFn = console[type] || console.log || noop,
13410 hasApply = false;
13411
13412 // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
13413 // The reason behind this is that console.log has type "object" in IE8...
13414 try {
13415 hasApply = !!logFn.apply;
13416 } catch (e) {}
13417
13418 if (hasApply) {
13419 return function() {
13420 var args = [];
13421 forEach(arguments, function(arg) {
13422 args.push(formatError(arg));
13423 });
13424 return logFn.apply(console, args);
13425 };
13426 }
13427
13428 // we are IE which either doesn't have window.console => this is noop and we do nothing,
13429 // or we are IE where console.log doesn't have apply so we log at least first 2 args
13430 return function(arg1, arg2) {
13431 logFn(arg1, arg2 == null ? '' : arg2);
13432 };
13433 }
13434 }];
13435}
13436
13437/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
13438 * Any commits to this file should be reviewed with security in mind. *
13439 * Changes to this file can potentially create security vulnerabilities. *
13440 * An approval from 2 Core members with history of modifying *
13441 * this file is required. *
13442 * *
13443 * Does the change somehow allow for arbitrary javascript to be executed? *
13444 * Or allows for someone to change the prototype of built-in objects? *
13445 * Or gives undesired access to variables likes document or window? *
13446 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13447
13448var $parseMinErr = minErr('$parse');
13449
13450// Sandboxing Angular Expressions
13451// ------------------------------
13452// Angular expressions are generally considered safe because these expressions only have direct
13453// access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
13454// obtaining a reference to native JS functions such as the Function constructor.
13455//
13456// As an example, consider the following Angular expression:
13457//
13458// {}.toString.constructor('alert("evil JS code")')
13459//
13460// This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
13461// against the expression language, but not to prevent exploits that were enabled by exposing
13462// sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
13463// practice and therefore we are not even trying to protect against interaction with an object
13464// explicitly exposed in this way.
13465//
13466// In general, it is not possible to access a Window object from an angular expression unless a
13467// window or some DOM object that has a reference to window is published onto a Scope.
13468// Similarly we prevent invocations of function known to be dangerous, as well as assignments to
13469// native objects.
13470//
13471// See https://docs.angularjs.org/guide/security
13472
13473
13474function ensureSafeMemberName(name, fullExpression) {
13475 if (name === "__defineGetter__" || name === "__defineSetter__"
13476 || name === "__lookupGetter__" || name === "__lookupSetter__"
13477 || name === "__proto__") {
13478 throw $parseMinErr('isecfld',
13479 'Attempting to access a disallowed field in Angular expressions! '
13480 + 'Expression: {0}', fullExpression);
13481 }
13482 return name;
13483}
13484
13485function getStringValue(name) {
13486 // Property names must be strings. This means that non-string objects cannot be used
13487 // as keys in an object. Any non-string object, including a number, is typecasted
13488 // into a string via the toString method.
13489 // -- MDN, https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Property_accessors#Property_names
13490 //
13491 // So, to ensure that we are checking the same `name` that JavaScript would use, we cast it
13492 // to a string. It's not always possible. If `name` is an object and its `toString` method is
13493 // 'broken' (doesn't return a string, isn't a function, etc.), an error will be thrown:
13494 //
13495 // TypeError: Cannot convert object to primitive value
13496 //
13497 // For performance reasons, we don't catch this error here and allow it to propagate up the call
13498 // stack. Note that you'll get the same error in JavaScript if you try to access a property using
13499 // such a 'broken' object as a key.
13500 return name + '';
13501}
13502
13503function ensureSafeObject(obj, fullExpression) {
13504 // nifty check if obj is Function that is fast and works across iframes and other contexts
13505 if (obj) {
13506 if (obj.constructor === obj) {
13507 throw $parseMinErr('isecfn',
13508 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
13509 fullExpression);
13510 } else if (// isWindow(obj)
13511 obj.window === obj) {
13512 throw $parseMinErr('isecwindow',
13513 'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
13514 fullExpression);
13515 } else if (// isElement(obj)
13516 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
13517 throw $parseMinErr('isecdom',
13518 'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
13519 fullExpression);
13520 } else if (// block Object so that we can't get hold of dangerous Object.* methods
13521 obj === Object) {
13522 throw $parseMinErr('isecobj',
13523 'Referencing Object in Angular expressions is disallowed! Expression: {0}',
13524 fullExpression);
13525 }
13526 }
13527 return obj;
13528}
13529
13530var CALL = Function.prototype.call;
13531var APPLY = Function.prototype.apply;
13532var BIND = Function.prototype.bind;
13533
13534function ensureSafeFunction(obj, fullExpression) {
13535 if (obj) {
13536 if (obj.constructor === obj) {
13537 throw $parseMinErr('isecfn',
13538 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
13539 fullExpression);
13540 } else if (obj === CALL || obj === APPLY || obj === BIND) {
13541 throw $parseMinErr('isecff',
13542 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
13543 fullExpression);
13544 }
13545 }
13546}
13547
13548function ensureSafeAssignContext(obj, fullExpression) {
13549 if (obj) {
13550 if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
13551 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
13552 throw $parseMinErr('isecaf',
13553 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
13554 }
13555 }
13556}
13557
13558var OPERATORS = createMap();
13559forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
13560var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
13561
13562
13563/////////////////////////////////////////
13564
13565
13566/**
13567 * @constructor
13568 */
13569var Lexer = function(options) {
13570 this.options = options;
13571};
13572
13573Lexer.prototype = {
13574 constructor: Lexer,
13575
13576 lex: function(text) {
13577 this.text = text;
13578 this.index = 0;
13579 this.tokens = [];
13580
13581 while (this.index < this.text.length) {
13582 var ch = this.text.charAt(this.index);
13583 if (ch === '"' || ch === "'") {
13584 this.readString(ch);
13585 } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
13586 this.readNumber();
13587 } else if (this.isIdent(ch)) {
13588 this.readIdent();
13589 } else if (this.is(ch, '(){}[].,;:?')) {
13590 this.tokens.push({index: this.index, text: ch});
13591 this.index++;
13592 } else if (this.isWhitespace(ch)) {
13593 this.index++;
13594 } else {
13595 var ch2 = ch + this.peek();
13596 var ch3 = ch2 + this.peek(2);
13597 var op1 = OPERATORS[ch];
13598 var op2 = OPERATORS[ch2];
13599 var op3 = OPERATORS[ch3];
13600 if (op1 || op2 || op3) {
13601 var token = op3 ? ch3 : (op2 ? ch2 : ch);
13602 this.tokens.push({index: this.index, text: token, operator: true});
13603 this.index += token.length;
13604 } else {
13605 this.throwError('Unexpected next character ', this.index, this.index + 1);
13606 }
13607 }
13608 }
13609 return this.tokens;
13610 },
13611
13612 is: function(ch, chars) {
13613 return chars.indexOf(ch) !== -1;
13614 },
13615
13616 peek: function(i) {
13617 var num = i || 1;
13618 return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
13619 },
13620
13621 isNumber: function(ch) {
13622 return ('0' <= ch && ch <= '9') && typeof ch === "string";
13623 },
13624
13625 isWhitespace: function(ch) {
13626 // IE treats non-breaking space as \u00A0
13627 return (ch === ' ' || ch === '\r' || ch === '\t' ||
13628 ch === '\n' || ch === '\v' || ch === '\u00A0');
13629 },
13630
13631 isIdent: function(ch) {
13632 return ('a' <= ch && ch <= 'z' ||
13633 'A' <= ch && ch <= 'Z' ||
13634 '_' === ch || ch === '$');
13635 },
13636
13637 isExpOperator: function(ch) {
13638 return (ch === '-' || ch === '+' || this.isNumber(ch));
13639 },
13640
13641 throwError: function(error, start, end) {
13642 end = end || this.index;
13643 var colStr = (isDefined(start)
13644 ? 's ' + start + '-' + this.index + ' [' + this.text.substring(start, end) + ']'
13645 : ' ' + end);
13646 throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
13647 error, colStr, this.text);
13648 },
13649
13650 readNumber: function() {
13651 var number = '';
13652 var start = this.index;
13653 while (this.index < this.text.length) {
13654 var ch = lowercase(this.text.charAt(this.index));
13655 if (ch == '.' || this.isNumber(ch)) {
13656 number += ch;
13657 } else {
13658 var peekCh = this.peek();
13659 if (ch == 'e' && this.isExpOperator(peekCh)) {
13660 number += ch;
13661 } else if (this.isExpOperator(ch) &&
13662 peekCh && this.isNumber(peekCh) &&
13663 number.charAt(number.length - 1) == 'e') {
13664 number += ch;
13665 } else if (this.isExpOperator(ch) &&
13666 (!peekCh || !this.isNumber(peekCh)) &&
13667 number.charAt(number.length - 1) == 'e') {
13668 this.throwError('Invalid exponent');
13669 } else {
13670 break;
13671 }
13672 }
13673 this.index++;
13674 }
13675 this.tokens.push({
13676 index: start,
13677 text: number,
13678 constant: true,
13679 value: Number(number)
13680 });
13681 },
13682
13683 readIdent: function() {
13684 var start = this.index;
13685 while (this.index < this.text.length) {
13686 var ch = this.text.charAt(this.index);
13687 if (!(this.isIdent(ch) || this.isNumber(ch))) {
13688 break;
13689 }
13690 this.index++;
13691 }
13692 this.tokens.push({
13693 index: start,
13694 text: this.text.slice(start, this.index),
13695 identifier: true
13696 });
13697 },
13698
13699 readString: function(quote) {
13700 var start = this.index;
13701 this.index++;
13702 var string = '';
13703 var rawString = quote;
13704 var escape = false;
13705 while (this.index < this.text.length) {
13706 var ch = this.text.charAt(this.index);
13707 rawString += ch;
13708 if (escape) {
13709 if (ch === 'u') {
13710 var hex = this.text.substring(this.index + 1, this.index + 5);
13711 if (!hex.match(/[\da-f]{4}/i)) {
13712 this.throwError('Invalid unicode escape [\\u' + hex + ']');
13713 }
13714 this.index += 4;
13715 string += String.fromCharCode(parseInt(hex, 16));
13716 } else {
13717 var rep = ESCAPE[ch];
13718 string = string + (rep || ch);
13719 }
13720 escape = false;
13721 } else if (ch === '\\') {
13722 escape = true;
13723 } else if (ch === quote) {
13724 this.index++;
13725 this.tokens.push({
13726 index: start,
13727 text: rawString,
13728 constant: true,
13729 value: string
13730 });
13731 return;
13732 } else {
13733 string += ch;
13734 }
13735 this.index++;
13736 }
13737 this.throwError('Unterminated quote', start);
13738 }
13739};
13740
13741var AST = function(lexer, options) {
13742 this.lexer = lexer;
13743 this.options = options;
13744};
13745
13746AST.Program = 'Program';
13747AST.ExpressionStatement = 'ExpressionStatement';
13748AST.AssignmentExpression = 'AssignmentExpression';
13749AST.ConditionalExpression = 'ConditionalExpression';
13750AST.LogicalExpression = 'LogicalExpression';
13751AST.BinaryExpression = 'BinaryExpression';
13752AST.UnaryExpression = 'UnaryExpression';
13753AST.CallExpression = 'CallExpression';
13754AST.MemberExpression = 'MemberExpression';
13755AST.Identifier = 'Identifier';
13756AST.Literal = 'Literal';
13757AST.ArrayExpression = 'ArrayExpression';
13758AST.Property = 'Property';
13759AST.ObjectExpression = 'ObjectExpression';
13760AST.ThisExpression = 'ThisExpression';
13761AST.LocalsExpression = 'LocalsExpression';
13762
13763// Internal use only
13764AST.NGValueParameter = 'NGValueParameter';
13765
13766AST.prototype = {
13767 ast: function(text) {
13768 this.text = text;
13769 this.tokens = this.lexer.lex(text);
13770
13771 var value = this.program();
13772
13773 if (this.tokens.length !== 0) {
13774 this.throwError('is an unexpected token', this.tokens[0]);
13775 }
13776
13777 return value;
13778 },
13779
13780 program: function() {
13781 var body = [];
13782 while (true) {
13783 if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
13784 body.push(this.expressionStatement());
13785 if (!this.expect(';')) {
13786 return { type: AST.Program, body: body};
13787 }
13788 }
13789 },
13790
13791 expressionStatement: function() {
13792 return { type: AST.ExpressionStatement, expression: this.filterChain() };
13793 },
13794
13795 filterChain: function() {
13796 var left = this.expression();
13797 var token;
13798 while ((token = this.expect('|'))) {
13799 left = this.filter(left);
13800 }
13801 return left;
13802 },
13803
13804 expression: function() {
13805 return this.assignment();
13806 },
13807
13808 assignment: function() {
13809 var result = this.ternary();
13810 if (this.expect('=')) {
13811 result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
13812 }
13813 return result;
13814 },
13815
13816 ternary: function() {
13817 var test = this.logicalOR();
13818 var alternate;
13819 var consequent;
13820 if (this.expect('?')) {
13821 alternate = this.expression();
13822 if (this.consume(':')) {
13823 consequent = this.expression();
13824 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
13825 }
13826 }
13827 return test;
13828 },
13829
13830 logicalOR: function() {
13831 var left = this.logicalAND();
13832 while (this.expect('||')) {
13833 left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13834 }
13835 return left;
13836 },
13837
13838 logicalAND: function() {
13839 var left = this.equality();
13840 while (this.expect('&&')) {
13841 left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13842 }
13843 return left;
13844 },
13845
13846 equality: function() {
13847 var left = this.relational();
13848 var token;
13849 while ((token = this.expect('==','!=','===','!=='))) {
13850 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13851 }
13852 return left;
13853 },
13854
13855 relational: function() {
13856 var left = this.additive();
13857 var token;
13858 while ((token = this.expect('<', '>', '<=', '>='))) {
13859 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13860 }
13861 return left;
13862 },
13863
13864 additive: function() {
13865 var left = this.multiplicative();
13866 var token;
13867 while ((token = this.expect('+','-'))) {
13868 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13869 }
13870 return left;
13871 },
13872
13873 multiplicative: function() {
13874 var left = this.unary();
13875 var token;
13876 while ((token = this.expect('*','/','%'))) {
13877 left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13878 }
13879 return left;
13880 },
13881
13882 unary: function() {
13883 var token;
13884 if ((token = this.expect('+', '-', '!'))) {
13885 return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13886 } else {
13887 return this.primary();
13888 }
13889 },
13890
13891 primary: function() {
13892 var primary;
13893 if (this.expect('(')) {
13894 primary = this.filterChain();
13895 this.consume(')');
13896 } else if (this.expect('[')) {
13897 primary = this.arrayDeclaration();
13898 } else if (this.expect('{')) {
13899 primary = this.object();
13900 } else if (this.selfReferential.hasOwnProperty(this.peek().text)) {
13901 primary = copy(this.selfReferential[this.consume().text]);
13902 } else if (this.options.literals.hasOwnProperty(this.peek().text)) {
13903 primary = { type: AST.Literal, value: this.options.literals[this.consume().text]};
13904 } else if (this.peek().identifier) {
13905 primary = this.identifier();
13906 } else if (this.peek().constant) {
13907 primary = this.constant();
13908 } else {
13909 this.throwError('not a primary expression', this.peek());
13910 }
13911
13912 var next;
13913 while ((next = this.expect('(', '[', '.'))) {
13914 if (next.text === '(') {
13915 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13916 this.consume(')');
13917 } else if (next.text === '[') {
13918 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13919 this.consume(']');
13920 } else if (next.text === '.') {
13921 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13922 } else {
13923 this.throwError('IMPOSSIBLE');
13924 }
13925 }
13926 return primary;
13927 },
13928
13929 filter: function(baseExpression) {
13930 var args = [baseExpression];
13931 var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13932
13933 while (this.expect(':')) {
13934 args.push(this.expression());
13935 }
13936
13937 return result;
13938 },
13939
13940 parseArguments: function() {
13941 var args = [];
13942 if (this.peekToken().text !== ')') {
13943 do {
13944 args.push(this.expression());
13945 } while (this.expect(','));
13946 }
13947 return args;
13948 },
13949
13950 identifier: function() {
13951 var token = this.consume();
13952 if (!token.identifier) {
13953 this.throwError('is not a valid identifier', token);
13954 }
13955 return { type: AST.Identifier, name: token.text };
13956 },
13957
13958 constant: function() {
13959 // TODO check that it is a constant
13960 return { type: AST.Literal, value: this.consume().value };
13961 },
13962
13963 arrayDeclaration: function() {
13964 var elements = [];
13965 if (this.peekToken().text !== ']') {
13966 do {
13967 if (this.peek(']')) {
13968 // Support trailing commas per ES5.1.
13969 break;
13970 }
13971 elements.push(this.expression());
13972 } while (this.expect(','));
13973 }
13974 this.consume(']');
13975
13976 return { type: AST.ArrayExpression, elements: elements };
13977 },
13978
13979 object: function() {
13980 var properties = [], property;
13981 if (this.peekToken().text !== '}') {
13982 do {
13983 if (this.peek('}')) {
13984 // Support trailing commas per ES5.1.
13985 break;
13986 }
13987 property = {type: AST.Property, kind: 'init'};
13988 if (this.peek().constant) {
13989 property.key = this.constant();
13990 } else if (this.peek().identifier) {
13991 property.key = this.identifier();
13992 } else {
13993 this.throwError("invalid key", this.peek());
13994 }
13995 this.consume(':');
13996 property.value = this.expression();
13997 properties.push(property);
13998 } while (this.expect(','));
13999 }
14000 this.consume('}');
14001
14002 return {type: AST.ObjectExpression, properties: properties };
14003 },
14004
14005 throwError: function(msg, token) {
14006 throw $parseMinErr('syntax',
14007 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
14008 token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
14009 },
14010
14011 consume: function(e1) {
14012 if (this.tokens.length === 0) {
14013 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
14014 }
14015
14016 var token = this.expect(e1);
14017 if (!token) {
14018 this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
14019 }
14020 return token;
14021 },
14022
14023 peekToken: function() {
14024 if (this.tokens.length === 0) {
14025 throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
14026 }
14027 return this.tokens[0];
14028 },
14029
14030 peek: function(e1, e2, e3, e4) {
14031 return this.peekAhead(0, e1, e2, e3, e4);
14032 },
14033
14034 peekAhead: function(i, e1, e2, e3, e4) {
14035 if (this.tokens.length > i) {
14036 var token = this.tokens[i];
14037 var t = token.text;
14038 if (t === e1 || t === e2 || t === e3 || t === e4 ||
14039 (!e1 && !e2 && !e3 && !e4)) {
14040 return token;
14041 }
14042 }
14043 return false;
14044 },
14045
14046 expect: function(e1, e2, e3, e4) {
14047 var token = this.peek(e1, e2, e3, e4);
14048 if (token) {
14049 this.tokens.shift();
14050 return token;
14051 }
14052 return false;
14053 },
14054
14055 selfReferential: {
14056 'this': {type: AST.ThisExpression },
14057 '$locals': {type: AST.LocalsExpression }
14058 }
14059};
14060
14061function ifDefined(v, d) {
14062 return typeof v !== 'undefined' ? v : d;
14063}
14064
14065function plusFn(l, r) {
14066 if (typeof l === 'undefined') return r;
14067 if (typeof r === 'undefined') return l;
14068 return l + r;
14069}
14070
14071function isStateless($filter, filterName) {
14072 var fn = $filter(filterName);
14073 return !fn.$stateful;
14074}
14075
14076function findConstantAndWatchExpressions(ast, $filter) {
14077 var allConstants;
14078 var argsToWatch;
14079 switch (ast.type) {
14080 case AST.Program:
14081 allConstants = true;
14082 forEach(ast.body, function(expr) {
14083 findConstantAndWatchExpressions(expr.expression, $filter);
14084 allConstants = allConstants && expr.expression.constant;
14085 });
14086 ast.constant = allConstants;
14087 break;
14088 case AST.Literal:
14089 ast.constant = true;
14090 ast.toWatch = [];
14091 break;
14092 case AST.UnaryExpression:
14093 findConstantAndWatchExpressions(ast.argument, $filter);
14094 ast.constant = ast.argument.constant;
14095 ast.toWatch = ast.argument.toWatch;
14096 break;
14097 case AST.BinaryExpression:
14098 findConstantAndWatchExpressions(ast.left, $filter);
14099 findConstantAndWatchExpressions(ast.right, $filter);
14100 ast.constant = ast.left.constant && ast.right.constant;
14101 ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
14102 break;
14103 case AST.LogicalExpression:
14104 findConstantAndWatchExpressions(ast.left, $filter);
14105 findConstantAndWatchExpressions(ast.right, $filter);
14106 ast.constant = ast.left.constant && ast.right.constant;
14107 ast.toWatch = ast.constant ? [] : [ast];
14108 break;
14109 case AST.ConditionalExpression:
14110 findConstantAndWatchExpressions(ast.test, $filter);
14111 findConstantAndWatchExpressions(ast.alternate, $filter);
14112 findConstantAndWatchExpressions(ast.consequent, $filter);
14113 ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
14114 ast.toWatch = ast.constant ? [] : [ast];
14115 break;
14116 case AST.Identifier:
14117 ast.constant = false;
14118 ast.toWatch = [ast];
14119 break;
14120 case AST.MemberExpression:
14121 findConstantAndWatchExpressions(ast.object, $filter);
14122 if (ast.computed) {
14123 findConstantAndWatchExpressions(ast.property, $filter);
14124 }
14125 ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
14126 ast.toWatch = [ast];
14127 break;
14128 case AST.CallExpression:
14129 allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
14130 argsToWatch = [];
14131 forEach(ast.arguments, function(expr) {
14132 findConstantAndWatchExpressions(expr, $filter);
14133 allConstants = allConstants && expr.constant;
14134 if (!expr.constant) {
14135 argsToWatch.push.apply(argsToWatch, expr.toWatch);
14136 }
14137 });
14138 ast.constant = allConstants;
14139 ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
14140 break;
14141 case AST.AssignmentExpression:
14142 findConstantAndWatchExpressions(ast.left, $filter);
14143 findConstantAndWatchExpressions(ast.right, $filter);
14144 ast.constant = ast.left.constant && ast.right.constant;
14145 ast.toWatch = [ast];
14146 break;
14147 case AST.ArrayExpression:
14148 allConstants = true;
14149 argsToWatch = [];
14150 forEach(ast.elements, function(expr) {
14151 findConstantAndWatchExpressions(expr, $filter);
14152 allConstants = allConstants && expr.constant;
14153 if (!expr.constant) {
14154 argsToWatch.push.apply(argsToWatch, expr.toWatch);
14155 }
14156 });
14157 ast.constant = allConstants;
14158 ast.toWatch = argsToWatch;
14159 break;
14160 case AST.ObjectExpression:
14161 allConstants = true;
14162 argsToWatch = [];
14163 forEach(ast.properties, function(property) {
14164 findConstantAndWatchExpressions(property.value, $filter);
14165 allConstants = allConstants && property.value.constant;
14166 if (!property.value.constant) {
14167 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
14168 }
14169 });
14170 ast.constant = allConstants;
14171 ast.toWatch = argsToWatch;
14172 break;
14173 case AST.ThisExpression:
14174 ast.constant = false;
14175 ast.toWatch = [];
14176 break;
14177 case AST.LocalsExpression:
14178 ast.constant = false;
14179 ast.toWatch = [];
14180 break;
14181 }
14182}
14183
14184function getInputs(body) {
14185 if (body.length != 1) return;
14186 var lastExpression = body[0].expression;
14187 var candidate = lastExpression.toWatch;
14188 if (candidate.length !== 1) return candidate;
14189 return candidate[0] !== lastExpression ? candidate : undefined;
14190}
14191
14192function isAssignable(ast) {
14193 return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
14194}
14195
14196function assignableAST(ast) {
14197 if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
14198 return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
14199 }
14200}
14201
14202function isLiteral(ast) {
14203 return ast.body.length === 0 ||
14204 ast.body.length === 1 && (
14205 ast.body[0].expression.type === AST.Literal ||
14206 ast.body[0].expression.type === AST.ArrayExpression ||
14207 ast.body[0].expression.type === AST.ObjectExpression);
14208}
14209
14210function isConstant(ast) {
14211 return ast.constant;
14212}
14213
14214function ASTCompiler(astBuilder, $filter) {
14215 this.astBuilder = astBuilder;
14216 this.$filter = $filter;
14217}
14218
14219ASTCompiler.prototype = {
14220 compile: function(expression, expensiveChecks) {
14221 var self = this;
14222 var ast = this.astBuilder.ast(expression);
14223 this.state = {
14224 nextId: 0,
14225 filters: {},
14226 expensiveChecks: expensiveChecks,
14227 fn: {vars: [], body: [], own: {}},
14228 assign: {vars: [], body: [], own: {}},
14229 inputs: []
14230 };
14231 findConstantAndWatchExpressions(ast, self.$filter);
14232 var extra = '';
14233 var assignable;
14234 this.stage = 'assign';
14235 if ((assignable = assignableAST(ast))) {
14236 this.state.computing = 'assign';
14237 var result = this.nextId();
14238 this.recurse(assignable, result);
14239 this.return_(result);
14240 extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
14241 }
14242 var toWatch = getInputs(ast.body);
14243 self.stage = 'inputs';
14244 forEach(toWatch, function(watch, key) {
14245 var fnKey = 'fn' + key;
14246 self.state[fnKey] = {vars: [], body: [], own: {}};
14247 self.state.computing = fnKey;
14248 var intoId = self.nextId();
14249 self.recurse(watch, intoId);
14250 self.return_(intoId);
14251 self.state.inputs.push(fnKey);
14252 watch.watchId = key;
14253 });
14254 this.state.computing = 'fn';
14255 this.stage = 'main';
14256 this.recurse(ast);
14257 var fnString =
14258 // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
14259 // This is a workaround for this until we do a better job at only removing the prefix only when we should.
14260 '"' + this.USE + ' ' + this.STRICT + '";\n' +
14261 this.filterPrefix() +
14262 'var fn=' + this.generateFunction('fn', 's,l,a,i') +
14263 extra +
14264 this.watchFns() +
14265 'return fn;';
14266
14267 /* jshint -W054 */
14268 var fn = (new Function('$filter',
14269 'ensureSafeMemberName',
14270 'ensureSafeObject',
14271 'ensureSafeFunction',
14272 'getStringValue',
14273 'ensureSafeAssignContext',
14274 'ifDefined',
14275 'plus',
14276 'text',
14277 fnString))(
14278 this.$filter,
14279 ensureSafeMemberName,
14280 ensureSafeObject,
14281 ensureSafeFunction,
14282 getStringValue,
14283 ensureSafeAssignContext,
14284 ifDefined,
14285 plusFn,
14286 expression);
14287 /* jshint +W054 */
14288 this.state = this.stage = undefined;
14289 fn.literal = isLiteral(ast);
14290 fn.constant = isConstant(ast);
14291 return fn;
14292 },
14293
14294 USE: 'use',
14295
14296 STRICT: 'strict',
14297
14298 watchFns: function() {
14299 var result = [];
14300 var fns = this.state.inputs;
14301 var self = this;
14302 forEach(fns, function(name) {
14303 result.push('var ' + name + '=' + self.generateFunction(name, 's'));
14304 });
14305 if (fns.length) {
14306 result.push('fn.inputs=[' + fns.join(',') + '];');
14307 }
14308 return result.join('');
14309 },
14310
14311 generateFunction: function(name, params) {
14312 return 'function(' + params + '){' +
14313 this.varsPrefix(name) +
14314 this.body(name) +
14315 '};';
14316 },
14317
14318 filterPrefix: function() {
14319 var parts = [];
14320 var self = this;
14321 forEach(this.state.filters, function(id, filter) {
14322 parts.push(id + '=$filter(' + self.escape(filter) + ')');
14323 });
14324 if (parts.length) return 'var ' + parts.join(',') + ';';
14325 return '';
14326 },
14327
14328 varsPrefix: function(section) {
14329 return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
14330 },
14331
14332 body: function(section) {
14333 return this.state[section].body.join('');
14334 },
14335
14336 recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14337 var left, right, self = this, args, expression;
14338 recursionFn = recursionFn || noop;
14339 if (!skipWatchIdCheck && isDefined(ast.watchId)) {
14340 intoId = intoId || this.nextId();
14341 this.if_('i',
14342 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
14343 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
14344 );
14345 return;
14346 }
14347 switch (ast.type) {
14348 case AST.Program:
14349 forEach(ast.body, function(expression, pos) {
14350 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
14351 if (pos !== ast.body.length - 1) {
14352 self.current().body.push(right, ';');
14353 } else {
14354 self.return_(right);
14355 }
14356 });
14357 break;
14358 case AST.Literal:
14359 expression = this.escape(ast.value);
14360 this.assign(intoId, expression);
14361 recursionFn(expression);
14362 break;
14363 case AST.UnaryExpression:
14364 this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
14365 expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
14366 this.assign(intoId, expression);
14367 recursionFn(expression);
14368 break;
14369 case AST.BinaryExpression:
14370 this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
14371 this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
14372 if (ast.operator === '+') {
14373 expression = this.plus(left, right);
14374 } else if (ast.operator === '-') {
14375 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
14376 } else {
14377 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
14378 }
14379 this.assign(intoId, expression);
14380 recursionFn(expression);
14381 break;
14382 case AST.LogicalExpression:
14383 intoId = intoId || this.nextId();
14384 self.recurse(ast.left, intoId);
14385 self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
14386 recursionFn(intoId);
14387 break;
14388 case AST.ConditionalExpression:
14389 intoId = intoId || this.nextId();
14390 self.recurse(ast.test, intoId);
14391 self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
14392 recursionFn(intoId);
14393 break;
14394 case AST.Identifier:
14395 intoId = intoId || this.nextId();
14396 if (nameId) {
14397 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
14398 nameId.computed = false;
14399 nameId.name = ast.name;
14400 }
14401 ensureSafeMemberName(ast.name);
14402 self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
14403 function() {
14404 self.if_(self.stage === 'inputs' || 's', function() {
14405 if (create && create !== 1) {
14406 self.if_(
14407 self.not(self.nonComputedMember('s', ast.name)),
14408 self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
14409 }
14410 self.assign(intoId, self.nonComputedMember('s', ast.name));
14411 });
14412 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
14413 );
14414 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
14415 self.addEnsureSafeObject(intoId);
14416 }
14417 recursionFn(intoId);
14418 break;
14419 case AST.MemberExpression:
14420 left = nameId && (nameId.context = this.nextId()) || this.nextId();
14421 intoId = intoId || this.nextId();
14422 self.recurse(ast.object, left, undefined, function() {
14423 self.if_(self.notNull(left), function() {
14424 if (create && create !== 1) {
14425 self.addEnsureSafeAssignContext(left);
14426 }
14427 if (ast.computed) {
14428 right = self.nextId();
14429 self.recurse(ast.property, right);
14430 self.getStringValue(right);
14431 self.addEnsureSafeMemberName(right);
14432 if (create && create !== 1) {
14433 self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
14434 }
14435 expression = self.ensureSafeObject(self.computedMember(left, right));
14436 self.assign(intoId, expression);
14437 if (nameId) {
14438 nameId.computed = true;
14439 nameId.name = right;
14440 }
14441 } else {
14442 ensureSafeMemberName(ast.property.name);
14443 if (create && create !== 1) {
14444 self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
14445 }
14446 expression = self.nonComputedMember(left, ast.property.name);
14447 if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
14448 expression = self.ensureSafeObject(expression);
14449 }
14450 self.assign(intoId, expression);
14451 if (nameId) {
14452 nameId.computed = false;
14453 nameId.name = ast.property.name;
14454 }
14455 }
14456 }, function() {
14457 self.assign(intoId, 'undefined');
14458 });
14459 recursionFn(intoId);
14460 }, !!create);
14461 break;
14462 case AST.CallExpression:
14463 intoId = intoId || this.nextId();
14464 if (ast.filter) {
14465 right = self.filter(ast.callee.name);
14466 args = [];
14467 forEach(ast.arguments, function(expr) {
14468 var argument = self.nextId();
14469 self.recurse(expr, argument);
14470 args.push(argument);
14471 });
14472 expression = right + '(' + args.join(',') + ')';
14473 self.assign(intoId, expression);
14474 recursionFn(intoId);
14475 } else {
14476 right = self.nextId();
14477 left = {};
14478 args = [];
14479 self.recurse(ast.callee, right, left, function() {
14480 self.if_(self.notNull(right), function() {
14481 self.addEnsureSafeFunction(right);
14482 forEach(ast.arguments, function(expr) {
14483 self.recurse(expr, self.nextId(), undefined, function(argument) {
14484 args.push(self.ensureSafeObject(argument));
14485 });
14486 });
14487 if (left.name) {
14488 if (!self.state.expensiveChecks) {
14489 self.addEnsureSafeObject(left.context);
14490 }
14491 expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
14492 } else {
14493 expression = right + '(' + args.join(',') + ')';
14494 }
14495 expression = self.ensureSafeObject(expression);
14496 self.assign(intoId, expression);
14497 }, function() {
14498 self.assign(intoId, 'undefined');
14499 });
14500 recursionFn(intoId);
14501 });
14502 }
14503 break;
14504 case AST.AssignmentExpression:
14505 right = this.nextId();
14506 left = {};
14507 if (!isAssignable(ast.left)) {
14508 throw $parseMinErr('lval', 'Trying to assign a value to a non l-value');
14509 }
14510 this.recurse(ast.left, undefined, left, function() {
14511 self.if_(self.notNull(left.context), function() {
14512 self.recurse(ast.right, right);
14513 self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
14514 self.addEnsureSafeAssignContext(left.context);
14515 expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
14516 self.assign(intoId, expression);
14517 recursionFn(intoId || expression);
14518 });
14519 }, 1);
14520 break;
14521 case AST.ArrayExpression:
14522 args = [];
14523 forEach(ast.elements, function(expr) {
14524 self.recurse(expr, self.nextId(), undefined, function(argument) {
14525 args.push(argument);
14526 });
14527 });
14528 expression = '[' + args.join(',') + ']';
14529 this.assign(intoId, expression);
14530 recursionFn(expression);
14531 break;
14532 case AST.ObjectExpression:
14533 args = [];
14534 forEach(ast.properties, function(property) {
14535 self.recurse(property.value, self.nextId(), undefined, function(expr) {
14536 args.push(self.escape(
14537 property.key.type === AST.Identifier ? property.key.name :
14538 ('' + property.key.value)) +
14539 ':' + expr);
14540 });
14541 });
14542 expression = '{' + args.join(',') + '}';
14543 this.assign(intoId, expression);
14544 recursionFn(expression);
14545 break;
14546 case AST.ThisExpression:
14547 this.assign(intoId, 's');
14548 recursionFn('s');
14549 break;
14550 case AST.LocalsExpression:
14551 this.assign(intoId, 'l');
14552 recursionFn('l');
14553 break;
14554 case AST.NGValueParameter:
14555 this.assign(intoId, 'v');
14556 recursionFn('v');
14557 break;
14558 }
14559 },
14560
14561 getHasOwnProperty: function(element, property) {
14562 var key = element + '.' + property;
14563 var own = this.current().own;
14564 if (!own.hasOwnProperty(key)) {
14565 own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
14566 }
14567 return own[key];
14568 },
14569
14570 assign: function(id, value) {
14571 if (!id) return;
14572 this.current().body.push(id, '=', value, ';');
14573 return id;
14574 },
14575
14576 filter: function(filterName) {
14577 if (!this.state.filters.hasOwnProperty(filterName)) {
14578 this.state.filters[filterName] = this.nextId(true);
14579 }
14580 return this.state.filters[filterName];
14581 },
14582
14583 ifDefined: function(id, defaultValue) {
14584 return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
14585 },
14586
14587 plus: function(left, right) {
14588 return 'plus(' + left + ',' + right + ')';
14589 },
14590
14591 return_: function(id) {
14592 this.current().body.push('return ', id, ';');
14593 },
14594
14595 if_: function(test, alternate, consequent) {
14596 if (test === true) {
14597 alternate();
14598 } else {
14599 var body = this.current().body;
14600 body.push('if(', test, '){');
14601 alternate();
14602 body.push('}');
14603 if (consequent) {
14604 body.push('else{');
14605 consequent();
14606 body.push('}');
14607 }
14608 }
14609 },
14610
14611 not: function(expression) {
14612 return '!(' + expression + ')';
14613 },
14614
14615 notNull: function(expression) {
14616 return expression + '!=null';
14617 },
14618
14619 nonComputedMember: function(left, right) {
14620 return left + '.' + right;
14621 },
14622
14623 computedMember: function(left, right) {
14624 return left + '[' + right + ']';
14625 },
14626
14627 member: function(left, right, computed) {
14628 if (computed) return this.computedMember(left, right);
14629 return this.nonComputedMember(left, right);
14630 },
14631
14632 addEnsureSafeObject: function(item) {
14633 this.current().body.push(this.ensureSafeObject(item), ';');
14634 },
14635
14636 addEnsureSafeMemberName: function(item) {
14637 this.current().body.push(this.ensureSafeMemberName(item), ';');
14638 },
14639
14640 addEnsureSafeFunction: function(item) {
14641 this.current().body.push(this.ensureSafeFunction(item), ';');
14642 },
14643
14644 addEnsureSafeAssignContext: function(item) {
14645 this.current().body.push(this.ensureSafeAssignContext(item), ';');
14646 },
14647
14648 ensureSafeObject: function(item) {
14649 return 'ensureSafeObject(' + item + ',text)';
14650 },
14651
14652 ensureSafeMemberName: function(item) {
14653 return 'ensureSafeMemberName(' + item + ',text)';
14654 },
14655
14656 ensureSafeFunction: function(item) {
14657 return 'ensureSafeFunction(' + item + ',text)';
14658 },
14659
14660 getStringValue: function(item) {
14661 this.assign(item, 'getStringValue(' + item + ')');
14662 },
14663
14664 ensureSafeAssignContext: function(item) {
14665 return 'ensureSafeAssignContext(' + item + ',text)';
14666 },
14667
14668 lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
14669 var self = this;
14670 return function() {
14671 self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
14672 };
14673 },
14674
14675 lazyAssign: function(id, value) {
14676 var self = this;
14677 return function() {
14678 self.assign(id, value);
14679 };
14680 },
14681
14682 stringEscapeRegex: /[^ a-zA-Z0-9]/g,
14683
14684 stringEscapeFn: function(c) {
14685 return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
14686 },
14687
14688 escape: function(value) {
14689 if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
14690 if (isNumber(value)) return value.toString();
14691 if (value === true) return 'true';
14692 if (value === false) return 'false';
14693 if (value === null) return 'null';
14694 if (typeof value === 'undefined') return 'undefined';
14695
14696 throw $parseMinErr('esc', 'IMPOSSIBLE');
14697 },
14698
14699 nextId: function(skip, init) {
14700 var id = 'v' + (this.state.nextId++);
14701 if (!skip) {
14702 this.current().vars.push(id + (init ? '=' + init : ''));
14703 }
14704 return id;
14705 },
14706
14707 current: function() {
14708 return this.state[this.state.computing];
14709 }
14710};
14711
14712
14713function ASTInterpreter(astBuilder, $filter) {
14714 this.astBuilder = astBuilder;
14715 this.$filter = $filter;
14716}
14717
14718ASTInterpreter.prototype = {
14719 compile: function(expression, expensiveChecks) {
14720 var self = this;
14721 var ast = this.astBuilder.ast(expression);
14722 this.expression = expression;
14723 this.expensiveChecks = expensiveChecks;
14724 findConstantAndWatchExpressions(ast, self.$filter);
14725 var assignable;
14726 var assign;
14727 if ((assignable = assignableAST(ast))) {
14728 assign = this.recurse(assignable);
14729 }
14730 var toWatch = getInputs(ast.body);
14731 var inputs;
14732 if (toWatch) {
14733 inputs = [];
14734 forEach(toWatch, function(watch, key) {
14735 var input = self.recurse(watch);
14736 watch.input = input;
14737 inputs.push(input);
14738 watch.watchId = key;
14739 });
14740 }
14741 var expressions = [];
14742 forEach(ast.body, function(expression) {
14743 expressions.push(self.recurse(expression.expression));
14744 });
14745 var fn = ast.body.length === 0 ? noop :
14746 ast.body.length === 1 ? expressions[0] :
14747 function(scope, locals) {
14748 var lastValue;
14749 forEach(expressions, function(exp) {
14750 lastValue = exp(scope, locals);
14751 });
14752 return lastValue;
14753 };
14754 if (assign) {
14755 fn.assign = function(scope, value, locals) {
14756 return assign(scope, locals, value);
14757 };
14758 }
14759 if (inputs) {
14760 fn.inputs = inputs;
14761 }
14762 fn.literal = isLiteral(ast);
14763 fn.constant = isConstant(ast);
14764 return fn;
14765 },
14766
14767 recurse: function(ast, context, create) {
14768 var left, right, self = this, args, expression;
14769 if (ast.input) {
14770 return this.inputs(ast.input, ast.watchId);
14771 }
14772 switch (ast.type) {
14773 case AST.Literal:
14774 return this.value(ast.value, context);
14775 case AST.UnaryExpression:
14776 right = this.recurse(ast.argument);
14777 return this['unary' + ast.operator](right, context);
14778 case AST.BinaryExpression:
14779 left = this.recurse(ast.left);
14780 right = this.recurse(ast.right);
14781 return this['binary' + ast.operator](left, right, context);
14782 case AST.LogicalExpression:
14783 left = this.recurse(ast.left);
14784 right = this.recurse(ast.right);
14785 return this['binary' + ast.operator](left, right, context);
14786 case AST.ConditionalExpression:
14787 return this['ternary?:'](
14788 this.recurse(ast.test),
14789 this.recurse(ast.alternate),
14790 this.recurse(ast.consequent),
14791 context
14792 );
14793 case AST.Identifier:
14794 ensureSafeMemberName(ast.name, self.expression);
14795 return self.identifier(ast.name,
14796 self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
14797 context, create, self.expression);
14798 case AST.MemberExpression:
14799 left = this.recurse(ast.object, false, !!create);
14800 if (!ast.computed) {
14801 ensureSafeMemberName(ast.property.name, self.expression);
14802 right = ast.property.name;
14803 }
14804 if (ast.computed) right = this.recurse(ast.property);
14805 return ast.computed ?
14806 this.computedMember(left, right, context, create, self.expression) :
14807 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
14808 case AST.CallExpression:
14809 args = [];
14810 forEach(ast.arguments, function(expr) {
14811 args.push(self.recurse(expr));
14812 });
14813 if (ast.filter) right = this.$filter(ast.callee.name);
14814 if (!ast.filter) right = this.recurse(ast.callee, true);
14815 return ast.filter ?
14816 function(scope, locals, assign, inputs) {
14817 var values = [];
14818 for (var i = 0; i < args.length; ++i) {
14819 values.push(args[i](scope, locals, assign, inputs));
14820 }
14821 var value = right.apply(undefined, values, inputs);
14822 return context ? {context: undefined, name: undefined, value: value} : value;
14823 } :
14824 function(scope, locals, assign, inputs) {
14825 var rhs = right(scope, locals, assign, inputs);
14826 var value;
14827 if (rhs.value != null) {
14828 ensureSafeObject(rhs.context, self.expression);
14829 ensureSafeFunction(rhs.value, self.expression);
14830 var values = [];
14831 for (var i = 0; i < args.length; ++i) {
14832 values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
14833 }
14834 value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
14835 }
14836 return context ? {value: value} : value;
14837 };
14838 case AST.AssignmentExpression:
14839 left = this.recurse(ast.left, true, 1);
14840 right = this.recurse(ast.right);
14841 return function(scope, locals, assign, inputs) {
14842 var lhs = left(scope, locals, assign, inputs);
14843 var rhs = right(scope, locals, assign, inputs);
14844 ensureSafeObject(lhs.value, self.expression);
14845 ensureSafeAssignContext(lhs.context);
14846 lhs.context[lhs.name] = rhs;
14847 return context ? {value: rhs} : rhs;
14848 };
14849 case AST.ArrayExpression:
14850 args = [];
14851 forEach(ast.elements, function(expr) {
14852 args.push(self.recurse(expr));
14853 });
14854 return function(scope, locals, assign, inputs) {
14855 var value = [];
14856 for (var i = 0; i < args.length; ++i) {
14857 value.push(args[i](scope, locals, assign, inputs));
14858 }
14859 return context ? {value: value} : value;
14860 };
14861 case AST.ObjectExpression:
14862 args = [];
14863 forEach(ast.properties, function(property) {
14864 args.push({key: property.key.type === AST.Identifier ?
14865 property.key.name :
14866 ('' + property.key.value),
14867 value: self.recurse(property.value)
14868 });
14869 });
14870 return function(scope, locals, assign, inputs) {
14871 var value = {};
14872 for (var i = 0; i < args.length; ++i) {
14873 value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14874 }
14875 return context ? {value: value} : value;
14876 };
14877 case AST.ThisExpression:
14878 return function(scope) {
14879 return context ? {value: scope} : scope;
14880 };
14881 case AST.LocalsExpression:
14882 return function(scope, locals) {
14883 return context ? {value: locals} : locals;
14884 };
14885 case AST.NGValueParameter:
14886 return function(scope, locals, assign) {
14887 return context ? {value: assign} : assign;
14888 };
14889 }
14890 },
14891
14892 'unary+': function(argument, context) {
14893 return function(scope, locals, assign, inputs) {
14894 var arg = argument(scope, locals, assign, inputs);
14895 if (isDefined(arg)) {
14896 arg = +arg;
14897 } else {
14898 arg = 0;
14899 }
14900 return context ? {value: arg} : arg;
14901 };
14902 },
14903 'unary-': function(argument, context) {
14904 return function(scope, locals, assign, inputs) {
14905 var arg = argument(scope, locals, assign, inputs);
14906 if (isDefined(arg)) {
14907 arg = -arg;
14908 } else {
14909 arg = 0;
14910 }
14911 return context ? {value: arg} : arg;
14912 };
14913 },
14914 'unary!': function(argument, context) {
14915 return function(scope, locals, assign, inputs) {
14916 var arg = !argument(scope, locals, assign, inputs);
14917 return context ? {value: arg} : arg;
14918 };
14919 },
14920 'binary+': function(left, right, context) {
14921 return function(scope, locals, assign, inputs) {
14922 var lhs = left(scope, locals, assign, inputs);
14923 var rhs = right(scope, locals, assign, inputs);
14924 var arg = plusFn(lhs, rhs);
14925 return context ? {value: arg} : arg;
14926 };
14927 },
14928 'binary-': function(left, right, context) {
14929 return function(scope, locals, assign, inputs) {
14930 var lhs = left(scope, locals, assign, inputs);
14931 var rhs = right(scope, locals, assign, inputs);
14932 var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14933 return context ? {value: arg} : arg;
14934 };
14935 },
14936 'binary*': function(left, right, context) {
14937 return function(scope, locals, assign, inputs) {
14938 var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14939 return context ? {value: arg} : arg;
14940 };
14941 },
14942 'binary/': function(left, right, context) {
14943 return function(scope, locals, assign, inputs) {
14944 var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14945 return context ? {value: arg} : arg;
14946 };
14947 },
14948 'binary%': function(left, right, context) {
14949 return function(scope, locals, assign, inputs) {
14950 var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14951 return context ? {value: arg} : arg;
14952 };
14953 },
14954 'binary===': function(left, right, context) {
14955 return function(scope, locals, assign, inputs) {
14956 var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14957 return context ? {value: arg} : arg;
14958 };
14959 },
14960 'binary!==': function(left, right, context) {
14961 return function(scope, locals, assign, inputs) {
14962 var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14963 return context ? {value: arg} : arg;
14964 };
14965 },
14966 'binary==': function(left, right, context) {
14967 return function(scope, locals, assign, inputs) {
14968 var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14969 return context ? {value: arg} : arg;
14970 };
14971 },
14972 'binary!=': function(left, right, context) {
14973 return function(scope, locals, assign, inputs) {
14974 var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14975 return context ? {value: arg} : arg;
14976 };
14977 },
14978 'binary<': function(left, right, context) {
14979 return function(scope, locals, assign, inputs) {
14980 var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14981 return context ? {value: arg} : arg;
14982 };
14983 },
14984 'binary>': function(left, right, context) {
14985 return function(scope, locals, assign, inputs) {
14986 var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14987 return context ? {value: arg} : arg;
14988 };
14989 },
14990 'binary<=': function(left, right, context) {
14991 return function(scope, locals, assign, inputs) {
14992 var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14993 return context ? {value: arg} : arg;
14994 };
14995 },
14996 'binary>=': function(left, right, context) {
14997 return function(scope, locals, assign, inputs) {
14998 var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14999 return context ? {value: arg} : arg;
15000 };
15001 },
15002 'binary&&': function(left, right, context) {
15003 return function(scope, locals, assign, inputs) {
15004 var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
15005 return context ? {value: arg} : arg;
15006 };
15007 },
15008 'binary||': function(left, right, context) {
15009 return function(scope, locals, assign, inputs) {
15010 var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
15011 return context ? {value: arg} : arg;
15012 };
15013 },
15014 'ternary?:': function(test, alternate, consequent, context) {
15015 return function(scope, locals, assign, inputs) {
15016 var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
15017 return context ? {value: arg} : arg;
15018 };
15019 },
15020 value: function(value, context) {
15021 return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
15022 },
15023 identifier: function(name, expensiveChecks, context, create, expression) {
15024 return function(scope, locals, assign, inputs) {
15025 var base = locals && (name in locals) ? locals : scope;
15026 if (create && create !== 1 && base && !(base[name])) {
15027 base[name] = {};
15028 }
15029 var value = base ? base[name] : undefined;
15030 if (expensiveChecks) {
15031 ensureSafeObject(value, expression);
15032 }
15033 if (context) {
15034 return {context: base, name: name, value: value};
15035 } else {
15036 return value;
15037 }
15038 };
15039 },
15040 computedMember: function(left, right, context, create, expression) {
15041 return function(scope, locals, assign, inputs) {
15042 var lhs = left(scope, locals, assign, inputs);
15043 var rhs;
15044 var value;
15045 if (lhs != null) {
15046 rhs = right(scope, locals, assign, inputs);
15047 rhs = getStringValue(rhs);
15048 ensureSafeMemberName(rhs, expression);
15049 if (create && create !== 1) {
15050 ensureSafeAssignContext(lhs);
15051 if (lhs && !(lhs[rhs])) {
15052 lhs[rhs] = {};
15053 }
15054 }
15055 value = lhs[rhs];
15056 ensureSafeObject(value, expression);
15057 }
15058 if (context) {
15059 return {context: lhs, name: rhs, value: value};
15060 } else {
15061 return value;
15062 }
15063 };
15064 },
15065 nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
15066 return function(scope, locals, assign, inputs) {
15067 var lhs = left(scope, locals, assign, inputs);
15068 if (create && create !== 1) {
15069 ensureSafeAssignContext(lhs);
15070 if (lhs && !(lhs[right])) {
15071 lhs[right] = {};
15072 }
15073 }
15074 var value = lhs != null ? lhs[right] : undefined;
15075 if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
15076 ensureSafeObject(value, expression);
15077 }
15078 if (context) {
15079 return {context: lhs, name: right, value: value};
15080 } else {
15081 return value;
15082 }
15083 };
15084 },
15085 inputs: function(input, watchId) {
15086 return function(scope, value, locals, inputs) {
15087 if (inputs) return inputs[watchId];
15088 return input(scope, value, locals);
15089 };
15090 }
15091};
15092
15093/**
15094 * @constructor
15095 */
15096var Parser = function(lexer, $filter, options) {
15097 this.lexer = lexer;
15098 this.$filter = $filter;
15099 this.options = options;
15100 this.ast = new AST(lexer, options);
15101 this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
15102 new ASTCompiler(this.ast, $filter);
15103};
15104
15105Parser.prototype = {
15106 constructor: Parser,
15107
15108 parse: function(text) {
15109 return this.astCompiler.compile(text, this.options.expensiveChecks);
15110 }
15111};
15112
15113function isPossiblyDangerousMemberName(name) {
15114 return name == 'constructor';
15115}
15116
15117var objectValueOf = Object.prototype.valueOf;
15118
15119function getValueOf(value) {
15120 return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
15121}
15122
15123///////////////////////////////////
15124
15125/**
15126 * @ngdoc service
15127 * @name $parse
15128 * @kind function
15129 *
15130 * @description
15131 *
15132 * Converts Angular {@link guide/expression expression} into a function.
15133 *
15134 * ```js
15135 * var getter = $parse('user.name');
15136 * var setter = getter.assign;
15137 * var context = {user:{name:'angular'}};
15138 * var locals = {user:{name:'local'}};
15139 *
15140 * expect(getter(context)).toEqual('angular');
15141 * setter(context, 'newValue');
15142 * expect(context.user.name).toEqual('newValue');
15143 * expect(getter(context, locals)).toEqual('local');
15144 * ```
15145 *
15146 *
15147 * @param {string} expression String expression to compile.
15148 * @returns {function(context, locals)} a function which represents the compiled expression:
15149 *
15150 * * `context` – `{object}` – an object against which any expressions embedded in the strings
15151 * are evaluated against (typically a scope object).
15152 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
15153 * `context`.
15154 *
15155 * The returned function also has the following properties:
15156 * * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
15157 * literal.
15158 * * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
15159 * constant literals.
15160 * * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
15161 * set to a function to change its value on the given context.
15162 *
15163 */
15164
15165
15166/**
15167 * @ngdoc provider
15168 * @name $parseProvider
15169 *
15170 * @description
15171 * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
15172 * service.
15173 */
15174function $ParseProvider() {
15175 var cacheDefault = createMap();
15176 var cacheExpensive = createMap();
15177 var literals = {
15178 'true': true,
15179 'false': false,
15180 'null': null,
15181 'undefined': undefined
15182 };
15183
15184 /**
15185 * @ngdoc method
15186 * @name $parseProvider#addLiteral
15187 * @description
15188 *
15189 * Configure $parse service to add literal values that will be present as literal at expressions.
15190 *
15191 * @param {string} literalName Token for the literal value. The literal name value must be a valid literal name.
15192 * @param {*} literalValue Value for this literal. All literal values must be primitives or `undefined`.
15193 *
15194 **/
15195 this.addLiteral = function(literalName, literalValue) {
15196 literals[literalName] = literalValue;
15197 };
15198
15199 this.$get = ['$filter', function($filter) {
15200 var noUnsafeEval = csp().noUnsafeEval;
15201 var $parseOptions = {
15202 csp: noUnsafeEval,
15203 expensiveChecks: false,
15204 literals: copy(literals)
15205 },
15206 $parseOptionsExpensive = {
15207 csp: noUnsafeEval,
15208 expensiveChecks: true,
15209 literals: copy(literals)
15210 };
15211 var runningChecksEnabled = false;
15212
15213 $parse.$$runningExpensiveChecks = function() {
15214 return runningChecksEnabled;
15215 };
15216
15217 return $parse;
15218
15219 function $parse(exp, interceptorFn, expensiveChecks) {
15220 var parsedExpression, oneTime, cacheKey;
15221
15222 expensiveChecks = expensiveChecks || runningChecksEnabled;
15223
15224 switch (typeof exp) {
15225 case 'string':
15226 exp = exp.trim();
15227 cacheKey = exp;
15228
15229 var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
15230 parsedExpression = cache[cacheKey];
15231
15232 if (!parsedExpression) {
15233 if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
15234 oneTime = true;
15235 exp = exp.substring(2);
15236 }
15237 var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
15238 var lexer = new Lexer(parseOptions);
15239 var parser = new Parser(lexer, $filter, parseOptions);
15240 parsedExpression = parser.parse(exp);
15241 if (parsedExpression.constant) {
15242 parsedExpression.$$watchDelegate = constantWatchDelegate;
15243 } else if (oneTime) {
15244 parsedExpression.$$watchDelegate = parsedExpression.literal ?
15245 oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
15246 } else if (parsedExpression.inputs) {
15247 parsedExpression.$$watchDelegate = inputsWatchDelegate;
15248 }
15249 if (expensiveChecks) {
15250 parsedExpression = expensiveChecksInterceptor(parsedExpression);
15251 }
15252 cache[cacheKey] = parsedExpression;
15253 }
15254 return addInterceptor(parsedExpression, interceptorFn);
15255
15256 case 'function':
15257 return addInterceptor(exp, interceptorFn);
15258
15259 default:
15260 return addInterceptor(noop, interceptorFn);
15261 }
15262 }
15263
15264 function expensiveChecksInterceptor(fn) {
15265 if (!fn) return fn;
15266 expensiveCheckFn.$$watchDelegate = fn.$$watchDelegate;
15267 expensiveCheckFn.assign = expensiveChecksInterceptor(fn.assign);
15268 expensiveCheckFn.constant = fn.constant;
15269 expensiveCheckFn.literal = fn.literal;
15270 for (var i = 0; fn.inputs && i < fn.inputs.length; ++i) {
15271 fn.inputs[i] = expensiveChecksInterceptor(fn.inputs[i]);
15272 }
15273 expensiveCheckFn.inputs = fn.inputs;
15274
15275 return expensiveCheckFn;
15276
15277 function expensiveCheckFn(scope, locals, assign, inputs) {
15278 var expensiveCheckOldValue = runningChecksEnabled;
15279 runningChecksEnabled = true;
15280 try {
15281 return fn(scope, locals, assign, inputs);
15282 } finally {
15283 runningChecksEnabled = expensiveCheckOldValue;
15284 }
15285 }
15286 }
15287
15288 function expressionInputDirtyCheck(newValue, oldValueOfValue) {
15289
15290 if (newValue == null || oldValueOfValue == null) { // null/undefined
15291 return newValue === oldValueOfValue;
15292 }
15293
15294 if (typeof newValue === 'object') {
15295
15296 // attempt to convert the value to a primitive type
15297 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
15298 // be cheaply dirty-checked
15299 newValue = getValueOf(newValue);
15300
15301 if (typeof newValue === 'object') {
15302 // objects/arrays are not supported - deep-watching them would be too expensive
15303 return false;
15304 }
15305
15306 // fall-through to the primitive equality check
15307 }
15308
15309 //Primitive or NaN
15310 return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
15311 }
15312
15313 function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
15314 var inputExpressions = parsedExpression.inputs;
15315 var lastResult;
15316
15317 if (inputExpressions.length === 1) {
15318 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
15319 inputExpressions = inputExpressions[0];
15320 return scope.$watch(function expressionInputWatch(scope) {
15321 var newInputValue = inputExpressions(scope);
15322 if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
15323 lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
15324 oldInputValueOf = newInputValue && getValueOf(newInputValue);
15325 }
15326 return lastResult;
15327 }, listener, objectEquality, prettyPrintExpression);
15328 }
15329
15330 var oldInputValueOfValues = [];
15331 var oldInputValues = [];
15332 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15333 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
15334 oldInputValues[i] = null;
15335 }
15336
15337 return scope.$watch(function expressionInputsWatch(scope) {
15338 var changed = false;
15339
15340 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
15341 var newInputValue = inputExpressions[i](scope);
15342 if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
15343 oldInputValues[i] = newInputValue;
15344 oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
15345 }
15346 }
15347
15348 if (changed) {
15349 lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
15350 }
15351
15352 return lastResult;
15353 }, listener, objectEquality, prettyPrintExpression);
15354 }
15355
15356 function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15357 var unwatch, lastValue;
15358 return unwatch = scope.$watch(function oneTimeWatch(scope) {
15359 return parsedExpression(scope);
15360 }, function oneTimeListener(value, old, scope) {
15361 lastValue = value;
15362 if (isFunction(listener)) {
15363 listener.apply(this, arguments);
15364 }
15365 if (isDefined(value)) {
15366 scope.$$postDigest(function() {
15367 if (isDefined(lastValue)) {
15368 unwatch();
15369 }
15370 });
15371 }
15372 }, objectEquality);
15373 }
15374
15375 function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15376 var unwatch, lastValue;
15377 return unwatch = scope.$watch(function oneTimeWatch(scope) {
15378 return parsedExpression(scope);
15379 }, function oneTimeListener(value, old, scope) {
15380 lastValue = value;
15381 if (isFunction(listener)) {
15382 listener.call(this, value, old, scope);
15383 }
15384 if (isAllDefined(value)) {
15385 scope.$$postDigest(function() {
15386 if (isAllDefined(lastValue)) unwatch();
15387 });
15388 }
15389 }, objectEquality);
15390
15391 function isAllDefined(value) {
15392 var allDefined = true;
15393 forEach(value, function(val) {
15394 if (!isDefined(val)) allDefined = false;
15395 });
15396 return allDefined;
15397 }
15398 }
15399
15400 function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
15401 var unwatch;
15402 return unwatch = scope.$watch(function constantWatch(scope) {
15403 unwatch();
15404 return parsedExpression(scope);
15405 }, listener, objectEquality);
15406 }
15407
15408 function addInterceptor(parsedExpression, interceptorFn) {
15409 if (!interceptorFn) return parsedExpression;
15410 var watchDelegate = parsedExpression.$$watchDelegate;
15411 var useInputs = false;
15412
15413 var regularWatch =
15414 watchDelegate !== oneTimeLiteralWatchDelegate &&
15415 watchDelegate !== oneTimeWatchDelegate;
15416
15417 var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
15418 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
15419 return interceptorFn(value, scope, locals);
15420 } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
15421 var value = parsedExpression(scope, locals, assign, inputs);
15422 var result = interceptorFn(value, scope, locals);
15423 // we only return the interceptor's result if the
15424 // initial value is defined (for bind-once)
15425 return isDefined(value) ? result : value;
15426 };
15427
15428 // Propagate $$watchDelegates other then inputsWatchDelegate
15429 if (parsedExpression.$$watchDelegate &&
15430 parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
15431 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
15432 } else if (!interceptorFn.$stateful) {
15433 // If there is an interceptor, but no watchDelegate then treat the interceptor like
15434 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
15435 fn.$$watchDelegate = inputsWatchDelegate;
15436 useInputs = !parsedExpression.inputs;
15437 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
15438 }
15439
15440 return fn;
15441 }
15442 }];
15443}
15444
15445/**
15446 * @ngdoc service
15447 * @name $q
15448 * @requires $rootScope
15449 *
15450 * @description
15451 * A service that helps you run functions asynchronously, and use their return values (or exceptions)
15452 * when they are done processing.
15453 *
15454 * This is an implementation of promises/deferred objects inspired by
15455 * [Kris Kowal's Q](https://github.com/kriskowal/q).
15456 *
15457 * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
15458 * implementations, and the other which resembles ES6 (ES2015) promises to some degree.
15459 *
15460 * # $q constructor
15461 *
15462 * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
15463 * function as the first argument. This is similar to the native Promise implementation from ES6,
15464 * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
15465 *
15466 * While the constructor-style use is supported, not all of the supporting methods from ES6 promises are
15467 * available yet.
15468 *
15469 * It can be used like so:
15470 *
15471 * ```js
15472 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
15473 * // are available in the current lexical scope (they could have been injected or passed in).
15474 *
15475 * function asyncGreet(name) {
15476 * // perform some asynchronous operation, resolve or reject the promise when appropriate.
15477 * return $q(function(resolve, reject) {
15478 * setTimeout(function() {
15479 * if (okToGreet(name)) {
15480 * resolve('Hello, ' + name + '!');
15481 * } else {
15482 * reject('Greeting ' + name + ' is not allowed.');
15483 * }
15484 * }, 1000);
15485 * });
15486 * }
15487 *
15488 * var promise = asyncGreet('Robin Hood');
15489 * promise.then(function(greeting) {
15490 * alert('Success: ' + greeting);
15491 * }, function(reason) {
15492 * alert('Failed: ' + reason);
15493 * });
15494 * ```
15495 *
15496 * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
15497 *
15498 * Note: unlike ES6 behavior, an exception thrown in the constructor function will NOT implicitly reject the promise.
15499 *
15500 * However, the more traditional CommonJS-style usage is still available, and documented below.
15501 *
15502 * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
15503 * interface for interacting with an object that represents the result of an action that is
15504 * performed asynchronously, and may or may not be finished at any given point in time.
15505 *
15506 * From the perspective of dealing with error handling, deferred and promise APIs are to
15507 * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
15508 *
15509 * ```js
15510 * // for the purpose of this example let's assume that variables `$q` and `okToGreet`
15511 * // are available in the current lexical scope (they could have been injected or passed in).
15512 *
15513 * function asyncGreet(name) {
15514 * var deferred = $q.defer();
15515 *
15516 * setTimeout(function() {
15517 * deferred.notify('About to greet ' + name + '.');
15518 *
15519 * if (okToGreet(name)) {
15520 * deferred.resolve('Hello, ' + name + '!');
15521 * } else {
15522 * deferred.reject('Greeting ' + name + ' is not allowed.');
15523 * }
15524 * }, 1000);
15525 *
15526 * return deferred.promise;
15527 * }
15528 *
15529 * var promise = asyncGreet('Robin Hood');
15530 * promise.then(function(greeting) {
15531 * alert('Success: ' + greeting);
15532 * }, function(reason) {
15533 * alert('Failed: ' + reason);
15534 * }, function(update) {
15535 * alert('Got notification: ' + update);
15536 * });
15537 * ```
15538 *
15539 * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
15540 * comes in the way of guarantees that promise and deferred APIs make, see
15541 * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
15542 *
15543 * Additionally the promise api allows for composition that is very hard to do with the
15544 * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
15545 * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
15546 * section on serial or parallel joining of promises.
15547 *
15548 * # The Deferred API
15549 *
15550 * A new instance of deferred is constructed by calling `$q.defer()`.
15551 *
15552 * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
15553 * that can be used for signaling the successful or unsuccessful completion, as well as the status
15554 * of the task.
15555 *
15556 * **Methods**
15557 *
15558 * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
15559 * constructed via `$q.reject`, the promise will be rejected instead.
15560 * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
15561 * resolving it with a rejection constructed via `$q.reject`.
15562 * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
15563 * multiple times before the promise is either resolved or rejected.
15564 *
15565 * **Properties**
15566 *
15567 * - promise – `{Promise}` – promise object associated with this deferred.
15568 *
15569 *
15570 * # The Promise API
15571 *
15572 * A new promise instance is created when a deferred instance is created and can be retrieved by
15573 * calling `deferred.promise`.
15574 *
15575 * The purpose of the promise object is to allow for interested parties to get access to the result
15576 * of the deferred task when it completes.
15577 *
15578 * **Methods**
15579 *
15580 * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
15581 * will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
15582 * as soon as the result is available. The callbacks are called with a single argument: the result
15583 * or rejection reason. Additionally, the notify callback may be called zero or more times to
15584 * provide a progress indication, before the promise is resolved or rejected.
15585 *
15586 * This method *returns a new promise* which is resolved or rejected via the return value of the
15587 * `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
15588 * with the value which is resolved in that promise using
15589 * [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
15590 * It also notifies via the return value of the `notifyCallback` method. The promise cannot be
15591 * resolved or rejected from the notifyCallback method.
15592 *
15593 * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
15594 *
15595 * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
15596 * but to do so without modifying the final value. This is useful to release resources or do some
15597 * clean-up that needs to be done whether the promise was rejected or resolved. See the [full
15598 * specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
15599 * more information.
15600 *
15601 * # Chaining promises
15602 *
15603 * Because calling the `then` method of a promise returns a new derived promise, it is easily
15604 * possible to create a chain of promises:
15605 *
15606 * ```js
15607 * promiseB = promiseA.then(function(result) {
15608 * return result + 1;
15609 * });
15610 *
15611 * // promiseB will be resolved immediately after promiseA is resolved and its value
15612 * // will be the result of promiseA incremented by 1
15613 * ```
15614 *
15615 * It is possible to create chains of any length and since a promise can be resolved with another
15616 * promise (which will defer its resolution further), it is possible to pause/defer resolution of
15617 * the promises at any point in the chain. This makes it possible to implement powerful APIs like
15618 * $http's response interceptors.
15619 *
15620 *
15621 * # Differences between Kris Kowal's Q and $q
15622 *
15623 * There are two main differences:
15624 *
15625 * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
15626 * mechanism in angular, which means faster propagation of resolution or rejection into your
15627 * models and avoiding unnecessary browser repaints, which would result in flickering UI.
15628 * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
15629 * all the important functionality needed for common async tasks.
15630 *
15631 * # Testing
15632 *
15633 * ```js
15634 * it('should simulate promise', inject(function($q, $rootScope) {
15635 * var deferred = $q.defer();
15636 * var promise = deferred.promise;
15637 * var resolvedValue;
15638 *
15639 * promise.then(function(value) { resolvedValue = value; });
15640 * expect(resolvedValue).toBeUndefined();
15641 *
15642 * // Simulate resolving of promise
15643 * deferred.resolve(123);
15644 * // Note that the 'then' function does not get called synchronously.
15645 * // This is because we want the promise API to always be async, whether or not
15646 * // it got called synchronously or asynchronously.
15647 * expect(resolvedValue).toBeUndefined();
15648 *
15649 * // Propagate promise resolution to 'then' functions using $apply().
15650 * $rootScope.$apply();
15651 * expect(resolvedValue).toEqual(123);
15652 * }));
15653 * ```
15654 *
15655 * @param {function(function, function)} resolver Function which is responsible for resolving or
15656 * rejecting the newly created promise. The first parameter is a function which resolves the
15657 * promise, the second parameter is a function which rejects the promise.
15658 *
15659 * @returns {Promise} The newly created promise.
15660 */
15661function $QProvider() {
15662
15663 this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
15664 return qFactory(function(callback) {
15665 $rootScope.$evalAsync(callback);
15666 }, $exceptionHandler);
15667 }];
15668}
15669
15670function $$QProvider() {
15671 this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
15672 return qFactory(function(callback) {
15673 $browser.defer(callback);
15674 }, $exceptionHandler);
15675 }];
15676}
15677
15678/**
15679 * Constructs a promise manager.
15680 *
15681 * @param {function(function)} nextTick Function for executing functions in the next turn.
15682 * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
15683 * debugging purposes.
15684 * @returns {object} Promise manager.
15685 */
15686function qFactory(nextTick, exceptionHandler) {
15687 var $qMinErr = minErr('$q', TypeError);
15688
15689 /**
15690 * @ngdoc method
15691 * @name ng.$q#defer
15692 * @kind function
15693 *
15694 * @description
15695 * Creates a `Deferred` object which represents a task which will finish in the future.
15696 *
15697 * @returns {Deferred} Returns a new instance of deferred.
15698 */
15699 var defer = function() {
15700 var d = new Deferred();
15701 //Necessary to support unbound execution :/
15702 d.resolve = simpleBind(d, d.resolve);
15703 d.reject = simpleBind(d, d.reject);
15704 d.notify = simpleBind(d, d.notify);
15705 return d;
15706 };
15707
15708 function Promise() {
15709 this.$$state = { status: 0 };
15710 }
15711
15712 extend(Promise.prototype, {
15713 then: function(onFulfilled, onRejected, progressBack) {
15714 if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
15715 return this;
15716 }
15717 var result = new Deferred();
15718
15719 this.$$state.pending = this.$$state.pending || [];
15720 this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
15721 if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
15722
15723 return result.promise;
15724 },
15725
15726 "catch": function(callback) {
15727 return this.then(null, callback);
15728 },
15729
15730 "finally": function(callback, progressBack) {
15731 return this.then(function(value) {
15732 return handleCallback(value, true, callback);
15733 }, function(error) {
15734 return handleCallback(error, false, callback);
15735 }, progressBack);
15736 }
15737 });
15738
15739 //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
15740 function simpleBind(context, fn) {
15741 return function(value) {
15742 fn.call(context, value);
15743 };
15744 }
15745
15746 function processQueue(state) {
15747 var fn, deferred, pending;
15748
15749 pending = state.pending;
15750 state.processScheduled = false;
15751 state.pending = undefined;
15752 for (var i = 0, ii = pending.length; i < ii; ++i) {
15753 deferred = pending[i][0];
15754 fn = pending[i][state.status];
15755 try {
15756 if (isFunction(fn)) {
15757 deferred.resolve(fn(state.value));
15758 } else if (state.status === 1) {
15759 deferred.resolve(state.value);
15760 } else {
15761 deferred.reject(state.value);
15762 }
15763 } catch (e) {
15764 deferred.reject(e);
15765 exceptionHandler(e);
15766 }
15767 }
15768 }
15769
15770 function scheduleProcessQueue(state) {
15771 if (state.processScheduled || !state.pending) return;
15772 state.processScheduled = true;
15773 nextTick(function() { processQueue(state); });
15774 }
15775
15776 function Deferred() {
15777 this.promise = new Promise();
15778 }
15779
15780 extend(Deferred.prototype, {
15781 resolve: function(val) {
15782 if (this.promise.$$state.status) return;
15783 if (val === this.promise) {
15784 this.$$reject($qMinErr(
15785 'qcycle',
15786 "Expected promise to be resolved with value other than itself '{0}'",
15787 val));
15788 } else {
15789 this.$$resolve(val);
15790 }
15791
15792 },
15793
15794 $$resolve: function(val) {
15795 var then;
15796 var that = this;
15797 var done = false;
15798 try {
15799 if ((isObject(val) || isFunction(val))) then = val && val.then;
15800 if (isFunction(then)) {
15801 this.promise.$$state.status = -1;
15802 then.call(val, resolvePromise, rejectPromise, simpleBind(this, this.notify));
15803 } else {
15804 this.promise.$$state.value = val;
15805 this.promise.$$state.status = 1;
15806 scheduleProcessQueue(this.promise.$$state);
15807 }
15808 } catch (e) {
15809 rejectPromise(e);
15810 exceptionHandler(e);
15811 }
15812
15813 function resolvePromise(val) {
15814 if (done) return;
15815 done = true;
15816 that.$$resolve(val);
15817 }
15818 function rejectPromise(val) {
15819 if (done) return;
15820 done = true;
15821 that.$$reject(val);
15822 }
15823 },
15824
15825 reject: function(reason) {
15826 if (this.promise.$$state.status) return;
15827 this.$$reject(reason);
15828 },
15829
15830 $$reject: function(reason) {
15831 this.promise.$$state.value = reason;
15832 this.promise.$$state.status = 2;
15833 scheduleProcessQueue(this.promise.$$state);
15834 },
15835
15836 notify: function(progress) {
15837 var callbacks = this.promise.$$state.pending;
15838
15839 if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
15840 nextTick(function() {
15841 var callback, result;
15842 for (var i = 0, ii = callbacks.length; i < ii; i++) {
15843 result = callbacks[i][0];
15844 callback = callbacks[i][3];
15845 try {
15846 result.notify(isFunction(callback) ? callback(progress) : progress);
15847 } catch (e) {
15848 exceptionHandler(e);
15849 }
15850 }
15851 });
15852 }
15853 }
15854 });
15855
15856 /**
15857 * @ngdoc method
15858 * @name $q#reject
15859 * @kind function
15860 *
15861 * @description
15862 * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
15863 * used to forward rejection in a chain of promises. If you are dealing with the last promise in
15864 * a promise chain, you don't need to worry about it.
15865 *
15866 * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
15867 * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
15868 * a promise error callback and you want to forward the error to the promise derived from the
15869 * current promise, you have to "rethrow" the error by returning a rejection constructed via
15870 * `reject`.
15871 *
15872 * ```js
15873 * promiseB = promiseA.then(function(result) {
15874 * // success: do something and resolve promiseB
15875 * // with the old or a new result
15876 * return result;
15877 * }, function(reason) {
15878 * // error: handle the error if possible and
15879 * // resolve promiseB with newPromiseOrValue,
15880 * // otherwise forward the rejection to promiseB
15881 * if (canHandle(reason)) {
15882 * // handle the error and recover
15883 * return newPromiseOrValue;
15884 * }
15885 * return $q.reject(reason);
15886 * });
15887 * ```
15888 *
15889 * @param {*} reason Constant, message, exception or an object representing the rejection reason.
15890 * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
15891 */
15892 var reject = function(reason) {
15893 var result = new Deferred();
15894 result.reject(reason);
15895 return result.promise;
15896 };
15897
15898 var makePromise = function makePromise(value, resolved) {
15899 var result = new Deferred();
15900 if (resolved) {
15901 result.resolve(value);
15902 } else {
15903 result.reject(value);
15904 }
15905 return result.promise;
15906 };
15907
15908 var handleCallback = function handleCallback(value, isResolved, callback) {
15909 var callbackOutput = null;
15910 try {
15911 if (isFunction(callback)) callbackOutput = callback();
15912 } catch (e) {
15913 return makePromise(e, false);
15914 }
15915 if (isPromiseLike(callbackOutput)) {
15916 return callbackOutput.then(function() {
15917 return makePromise(value, isResolved);
15918 }, function(error) {
15919 return makePromise(error, false);
15920 });
15921 } else {
15922 return makePromise(value, isResolved);
15923 }
15924 };
15925
15926 /**
15927 * @ngdoc method
15928 * @name $q#when
15929 * @kind function
15930 *
15931 * @description
15932 * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15933 * This is useful when you are dealing with an object that might or might not be a promise, or if
15934 * the promise comes from a source that can't be trusted.
15935 *
15936 * @param {*} value Value or a promise
15937 * @param {Function=} successCallback
15938 * @param {Function=} errorCallback
15939 * @param {Function=} progressCallback
15940 * @returns {Promise} Returns a promise of the passed value or promise
15941 */
15942
15943
15944 var when = function(value, callback, errback, progressBack) {
15945 var result = new Deferred();
15946 result.resolve(value);
15947 return result.promise.then(callback, errback, progressBack);
15948 };
15949
15950 /**
15951 * @ngdoc method
15952 * @name $q#resolve
15953 * @kind function
15954 *
15955 * @description
15956 * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15957 *
15958 * @param {*} value Value or a promise
15959 * @param {Function=} successCallback
15960 * @param {Function=} errorCallback
15961 * @param {Function=} progressCallback
15962 * @returns {Promise} Returns a promise of the passed value or promise
15963 */
15964 var resolve = when;
15965
15966 /**
15967 * @ngdoc method
15968 * @name $q#all
15969 * @kind function
15970 *
15971 * @description
15972 * Combines multiple promises into a single promise that is resolved when all of the input
15973 * promises are resolved.
15974 *
15975 * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15976 * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15977 * each value corresponding to the promise at the same index/key in the `promises` array/hash.
15978 * If any of the promises is resolved with a rejection, this resulting promise will be rejected
15979 * with the same rejection value.
15980 */
15981
15982 function all(promises) {
15983 var deferred = new Deferred(),
15984 counter = 0,
15985 results = isArray(promises) ? [] : {};
15986
15987 forEach(promises, function(promise, key) {
15988 counter++;
15989 when(promise).then(function(value) {
15990 if (results.hasOwnProperty(key)) return;
15991 results[key] = value;
15992 if (!(--counter)) deferred.resolve(results);
15993 }, function(reason) {
15994 if (results.hasOwnProperty(key)) return;
15995 deferred.reject(reason);
15996 });
15997 });
15998
15999 if (counter === 0) {
16000 deferred.resolve(results);
16001 }
16002
16003 return deferred.promise;
16004 }
16005
16006 var $Q = function Q(resolver) {
16007 if (!isFunction(resolver)) {
16008 throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
16009 }
16010
16011 var deferred = new Deferred();
16012
16013 function resolveFn(value) {
16014 deferred.resolve(value);
16015 }
16016
16017 function rejectFn(reason) {
16018 deferred.reject(reason);
16019 }
16020
16021 resolver(resolveFn, rejectFn);
16022
16023 return deferred.promise;
16024 };
16025
16026 // Let's make the instanceof operator work for promises, so that
16027 // `new $q(fn) instanceof $q` would evaluate to true.
16028 $Q.prototype = Promise.prototype;
16029
16030 $Q.defer = defer;
16031 $Q.reject = reject;
16032 $Q.when = when;
16033 $Q.resolve = resolve;
16034 $Q.all = all;
16035
16036 return $Q;
16037}
16038
16039function $$RAFProvider() { //rAF
16040 this.$get = ['$window', '$timeout', function($window, $timeout) {
16041 var requestAnimationFrame = $window.requestAnimationFrame ||
16042 $window.webkitRequestAnimationFrame;
16043
16044 var cancelAnimationFrame = $window.cancelAnimationFrame ||
16045 $window.webkitCancelAnimationFrame ||
16046 $window.webkitCancelRequestAnimationFrame;
16047
16048 var rafSupported = !!requestAnimationFrame;
16049 var raf = rafSupported
16050 ? function(fn) {
16051 var id = requestAnimationFrame(fn);
16052 return function() {
16053 cancelAnimationFrame(id);
16054 };
16055 }
16056 : function(fn) {
16057 var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
16058 return function() {
16059 $timeout.cancel(timer);
16060 };
16061 };
16062
16063 raf.supported = rafSupported;
16064
16065 return raf;
16066 }];
16067}
16068
16069/**
16070 * DESIGN NOTES
16071 *
16072 * The design decisions behind the scope are heavily favored for speed and memory consumption.
16073 *
16074 * The typical use of scope is to watch the expressions, which most of the time return the same
16075 * value as last time so we optimize the operation.
16076 *
16077 * Closures construction is expensive in terms of speed as well as memory:
16078 * - No closures, instead use prototypical inheritance for API
16079 * - Internal state needs to be stored on scope directly, which means that private state is
16080 * exposed as $$____ properties
16081 *
16082 * Loop operations are optimized by using while(count--) { ... }
16083 * - This means that in order to keep the same order of execution as addition we have to add
16084 * items to the array at the beginning (unshift) instead of at the end (push)
16085 *
16086 * Child scopes are created and removed often
16087 * - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
16088 *
16089 * There are fewer watches than observers. This is why you don't want the observer to be implemented
16090 * in the same way as watch. Watch requires return of the initialization function which is expensive
16091 * to construct.
16092 */
16093
16094
16095/**
16096 * @ngdoc provider
16097 * @name $rootScopeProvider
16098 * @description
16099 *
16100 * Provider for the $rootScope service.
16101 */
16102
16103/**
16104 * @ngdoc method
16105 * @name $rootScopeProvider#digestTtl
16106 * @description
16107 *
16108 * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
16109 * assuming that the model is unstable.
16110 *
16111 * The current default is 10 iterations.
16112 *
16113 * In complex applications it's possible that the dependencies between `$watch`s will result in
16114 * several digest iterations. However if an application needs more than the default 10 digest
16115 * iterations for its model to stabilize then you should investigate what is causing the model to
16116 * continuously change during the digest.
16117 *
16118 * Increasing the TTL could have performance implications, so you should not change it without
16119 * proper justification.
16120 *
16121 * @param {number} limit The number of digest iterations.
16122 */
16123
16124
16125/**
16126 * @ngdoc service
16127 * @name $rootScope
16128 * @description
16129 *
16130 * Every application has a single root {@link ng.$rootScope.Scope scope}.
16131 * All other scopes are descendant scopes of the root scope. Scopes provide separation
16132 * between the model and the view, via a mechanism for watching the model for changes.
16133 * They also provide event emission/broadcast and subscription facility. See the
16134 * {@link guide/scope developer guide on scopes}.
16135 */
16136function $RootScopeProvider() {
16137 var TTL = 10;
16138 var $rootScopeMinErr = minErr('$rootScope');
16139 var lastDirtyWatch = null;
16140 var applyAsyncId = null;
16141
16142 this.digestTtl = function(value) {
16143 if (arguments.length) {
16144 TTL = value;
16145 }
16146 return TTL;
16147 };
16148
16149 function createChildScopeClass(parent) {
16150 function ChildScope() {
16151 this.$$watchers = this.$$nextSibling =
16152 this.$$childHead = this.$$childTail = null;
16153 this.$$listeners = {};
16154 this.$$listenerCount = {};
16155 this.$$watchersCount = 0;
16156 this.$id = nextUid();
16157 this.$$ChildScope = null;
16158 }
16159 ChildScope.prototype = parent;
16160 return ChildScope;
16161 }
16162
16163 this.$get = ['$exceptionHandler', '$parse', '$browser',
16164 function($exceptionHandler, $parse, $browser) {
16165
16166 function destroyChildScope($event) {
16167 $event.currentScope.$$destroyed = true;
16168 }
16169
16170 function cleanUpScope($scope) {
16171
16172 if (msie === 9) {
16173 // There is a memory leak in IE9 if all child scopes are not disconnected
16174 // completely when a scope is destroyed. So this code will recurse up through
16175 // all this scopes children
16176 //
16177 // See issue https://github.com/angular/angular.js/issues/10706
16178 $scope.$$childHead && cleanUpScope($scope.$$childHead);
16179 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
16180 }
16181
16182 // The code below works around IE9 and V8's memory leaks
16183 //
16184 // See:
16185 // - https://code.google.com/p/v8/issues/detail?id=2073#c26
16186 // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
16187 // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
16188
16189 $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
16190 $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
16191 }
16192
16193 /**
16194 * @ngdoc type
16195 * @name $rootScope.Scope
16196 *
16197 * @description
16198 * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
16199 * {@link auto.$injector $injector}. Child scopes are created using the
16200 * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
16201 * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
16202 * an in-depth introduction and usage examples.
16203 *
16204 *
16205 * # Inheritance
16206 * A scope can inherit from a parent scope, as in this example:
16207 * ```js
16208 var parent = $rootScope;
16209 var child = parent.$new();
16210
16211 parent.salutation = "Hello";
16212 expect(child.salutation).toEqual('Hello');
16213
16214 child.salutation = "Welcome";
16215 expect(child.salutation).toEqual('Welcome');
16216 expect(parent.salutation).toEqual('Hello');
16217 * ```
16218 *
16219 * When interacting with `Scope` in tests, additional helper methods are available on the
16220 * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
16221 * details.
16222 *
16223 *
16224 * @param {Object.<string, function()>=} providers Map of service factory which need to be
16225 * provided for the current scope. Defaults to {@link ng}.
16226 * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
16227 * append/override services provided by `providers`. This is handy
16228 * when unit-testing and having the need to override a default
16229 * service.
16230 * @returns {Object} Newly created scope.
16231 *
16232 */
16233 function Scope() {
16234 this.$id = nextUid();
16235 this.$$phase = this.$parent = this.$$watchers =
16236 this.$$nextSibling = this.$$prevSibling =
16237 this.$$childHead = this.$$childTail = null;
16238 this.$root = this;
16239 this.$$destroyed = false;
16240 this.$$listeners = {};
16241 this.$$listenerCount = {};
16242 this.$$watchersCount = 0;
16243 this.$$isolateBindings = null;
16244 }
16245
16246 /**
16247 * @ngdoc property
16248 * @name $rootScope.Scope#$id
16249 *
16250 * @description
16251 * Unique scope ID (monotonically increasing) useful for debugging.
16252 */
16253
16254 /**
16255 * @ngdoc property
16256 * @name $rootScope.Scope#$parent
16257 *
16258 * @description
16259 * Reference to the parent scope.
16260 */
16261
16262 /**
16263 * @ngdoc property
16264 * @name $rootScope.Scope#$root
16265 *
16266 * @description
16267 * Reference to the root scope.
16268 */
16269
16270 Scope.prototype = {
16271 constructor: Scope,
16272 /**
16273 * @ngdoc method
16274 * @name $rootScope.Scope#$new
16275 * @kind function
16276 *
16277 * @description
16278 * Creates a new child {@link ng.$rootScope.Scope scope}.
16279 *
16280 * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
16281 * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
16282 *
16283 * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
16284 * desired for the scope and its child scopes to be permanently detached from the parent and
16285 * thus stop participating in model change detection and listener notification by invoking.
16286 *
16287 * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
16288 * parent scope. The scope is isolated, as it can not see parent scope properties.
16289 * When creating widgets, it is useful for the widget to not accidentally read parent
16290 * state.
16291 *
16292 * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
16293 * of the newly created scope. Defaults to `this` scope if not provided.
16294 * This is used when creating a transclude scope to correctly place it
16295 * in the scope hierarchy while maintaining the correct prototypical
16296 * inheritance.
16297 *
16298 * @returns {Object} The newly created child scope.
16299 *
16300 */
16301 $new: function(isolate, parent) {
16302 var child;
16303
16304 parent = parent || this;
16305
16306 if (isolate) {
16307 child = new Scope();
16308 child.$root = this.$root;
16309 } else {
16310 // Only create a child scope class if somebody asks for one,
16311 // but cache it to allow the VM to optimize lookups.
16312 if (!this.$$ChildScope) {
16313 this.$$ChildScope = createChildScopeClass(this);
16314 }
16315 child = new this.$$ChildScope();
16316 }
16317 child.$parent = parent;
16318 child.$$prevSibling = parent.$$childTail;
16319 if (parent.$$childHead) {
16320 parent.$$childTail.$$nextSibling = child;
16321 parent.$$childTail = child;
16322 } else {
16323 parent.$$childHead = parent.$$childTail = child;
16324 }
16325
16326 // When the new scope is not isolated or we inherit from `this`, and
16327 // the parent scope is destroyed, the property `$$destroyed` is inherited
16328 // prototypically. In all other cases, this property needs to be set
16329 // when the parent scope is destroyed.
16330 // The listener needs to be added after the parent is set
16331 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
16332
16333 return child;
16334 },
16335
16336 /**
16337 * @ngdoc method
16338 * @name $rootScope.Scope#$watch
16339 * @kind function
16340 *
16341 * @description
16342 * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
16343 *
16344 * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
16345 * $digest()} and should return the value that will be watched. (`watchExpression` should not change
16346 * its value when executed multiple times with the same input because it may be executed multiple
16347 * times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
16348 * [idempotent](http://en.wikipedia.org/wiki/Idempotence).
16349 * - The `listener` is called only when the value from the current `watchExpression` and the
16350 * previous call to `watchExpression` are not equal (with the exception of the initial run,
16351 * see below). Inequality is determined according to reference inequality,
16352 * [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
16353 * via the `!==` Javascript operator, unless `objectEquality == true`
16354 * (see next point)
16355 * - When `objectEquality == true`, inequality of the `watchExpression` is determined
16356 * according to the {@link angular.equals} function. To save the value of the object for
16357 * later comparison, the {@link angular.copy} function is used. This therefore means that
16358 * watching complex objects will have adverse memory and performance implications.
16359 * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
16360 * This is achieved by rerunning the watchers until no changes are detected. The rerun
16361 * iteration limit is 10 to prevent an infinite loop deadlock.
16362 *
16363 *
16364 * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
16365 * you can register a `watchExpression` function with no `listener`. (Be prepared for
16366 * multiple calls to your `watchExpression` because it will execute multiple times in a
16367 * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
16368 *
16369 * After a watcher is registered with the scope, the `listener` fn is called asynchronously
16370 * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
16371 * watcher. In rare cases, this is undesirable because the listener is called when the result
16372 * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
16373 * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
16374 * listener was called due to initialization.
16375 *
16376 *
16377 *
16378 * # Example
16379 * ```js
16380 // let's assume that scope was dependency injected as the $rootScope
16381 var scope = $rootScope;
16382 scope.name = 'misko';
16383 scope.counter = 0;
16384
16385 expect(scope.counter).toEqual(0);
16386 scope.$watch('name', function(newValue, oldValue) {
16387 scope.counter = scope.counter + 1;
16388 });
16389 expect(scope.counter).toEqual(0);
16390
16391 scope.$digest();
16392 // the listener is always called during the first $digest loop after it was registered
16393 expect(scope.counter).toEqual(1);
16394
16395 scope.$digest();
16396 // but now it will not be called unless the value changes
16397 expect(scope.counter).toEqual(1);
16398
16399 scope.name = 'adam';
16400 scope.$digest();
16401 expect(scope.counter).toEqual(2);
16402
16403
16404
16405 // Using a function as a watchExpression
16406 var food;
16407 scope.foodCounter = 0;
16408 expect(scope.foodCounter).toEqual(0);
16409 scope.$watch(
16410 // This function returns the value being watched. It is called for each turn of the $digest loop
16411 function() { return food; },
16412 // This is the change listener, called when the value returned from the above function changes
16413 function(newValue, oldValue) {
16414 if ( newValue !== oldValue ) {
16415 // Only increment the counter if the value changed
16416 scope.foodCounter = scope.foodCounter + 1;
16417 }
16418 }
16419 );
16420 // No digest has been run so the counter will be zero
16421 expect(scope.foodCounter).toEqual(0);
16422
16423 // Run the digest but since food has not changed count will still be zero
16424 scope.$digest();
16425 expect(scope.foodCounter).toEqual(0);
16426
16427 // Update food and run digest. Now the counter will increment
16428 food = 'cheeseburger';
16429 scope.$digest();
16430 expect(scope.foodCounter).toEqual(1);
16431
16432 * ```
16433 *
16434 *
16435 *
16436 * @param {(function()|string)} watchExpression Expression that is evaluated on each
16437 * {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
16438 * a call to the `listener`.
16439 *
16440 * - `string`: Evaluated as {@link guide/expression expression}
16441 * - `function(scope)`: called with current `scope` as a parameter.
16442 * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
16443 * of `watchExpression` changes.
16444 *
16445 * - `newVal` contains the current value of the `watchExpression`
16446 * - `oldVal` contains the previous value of the `watchExpression`
16447 * - `scope` refers to the current scope
16448 * @param {boolean=} [objectEquality=false] Compare for object equality using {@link angular.equals} instead of
16449 * comparing for reference equality.
16450 * @returns {function()} Returns a deregistration function for this listener.
16451 */
16452 $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
16453 var get = $parse(watchExp);
16454
16455 if (get.$$watchDelegate) {
16456 return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
16457 }
16458 var scope = this,
16459 array = scope.$$watchers,
16460 watcher = {
16461 fn: listener,
16462 last: initWatchVal,
16463 get: get,
16464 exp: prettyPrintExpression || watchExp,
16465 eq: !!objectEquality
16466 };
16467
16468 lastDirtyWatch = null;
16469
16470 if (!isFunction(listener)) {
16471 watcher.fn = noop;
16472 }
16473
16474 if (!array) {
16475 array = scope.$$watchers = [];
16476 }
16477 // we use unshift since we use a while loop in $digest for speed.
16478 // the while loop reads in reverse order.
16479 array.unshift(watcher);
16480 incrementWatchersCount(this, 1);
16481
16482 return function deregisterWatch() {
16483 if (arrayRemove(array, watcher) >= 0) {
16484 incrementWatchersCount(scope, -1);
16485 }
16486 lastDirtyWatch = null;
16487 };
16488 },
16489
16490 /**
16491 * @ngdoc method
16492 * @name $rootScope.Scope#$watchGroup
16493 * @kind function
16494 *
16495 * @description
16496 * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
16497 * If any one expression in the collection changes the `listener` is executed.
16498 *
16499 * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
16500 * call to $digest() to see if any items changes.
16501 * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
16502 *
16503 * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
16504 * watched using {@link ng.$rootScope.Scope#$watch $watch()}
16505 *
16506 * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
16507 * expression in `watchExpressions` changes
16508 * The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
16509 * those of `watchExpression`
16510 * and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
16511 * those of `watchExpression`
16512 * The `scope` refers to the current scope.
16513 * @returns {function()} Returns a de-registration function for all listeners.
16514 */
16515 $watchGroup: function(watchExpressions, listener) {
16516 var oldValues = new Array(watchExpressions.length);
16517 var newValues = new Array(watchExpressions.length);
16518 var deregisterFns = [];
16519 var self = this;
16520 var changeReactionScheduled = false;
16521 var firstRun = true;
16522
16523 if (!watchExpressions.length) {
16524 // No expressions means we call the listener ASAP
16525 var shouldCall = true;
16526 self.$evalAsync(function() {
16527 if (shouldCall) listener(newValues, newValues, self);
16528 });
16529 return function deregisterWatchGroup() {
16530 shouldCall = false;
16531 };
16532 }
16533
16534 if (watchExpressions.length === 1) {
16535 // Special case size of one
16536 return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
16537 newValues[0] = value;
16538 oldValues[0] = oldValue;
16539 listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
16540 });
16541 }
16542
16543 forEach(watchExpressions, function(expr, i) {
16544 var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
16545 newValues[i] = value;
16546 oldValues[i] = oldValue;
16547 if (!changeReactionScheduled) {
16548 changeReactionScheduled = true;
16549 self.$evalAsync(watchGroupAction);
16550 }
16551 });
16552 deregisterFns.push(unwatchFn);
16553 });
16554
16555 function watchGroupAction() {
16556 changeReactionScheduled = false;
16557
16558 if (firstRun) {
16559 firstRun = false;
16560 listener(newValues, newValues, self);
16561 } else {
16562 listener(newValues, oldValues, self);
16563 }
16564 }
16565
16566 return function deregisterWatchGroup() {
16567 while (deregisterFns.length) {
16568 deregisterFns.shift()();
16569 }
16570 };
16571 },
16572
16573
16574 /**
16575 * @ngdoc method
16576 * @name $rootScope.Scope#$watchCollection
16577 * @kind function
16578 *
16579 * @description
16580 * Shallow watches the properties of an object and fires whenever any of the properties change
16581 * (for arrays, this implies watching the array items; for object maps, this implies watching
16582 * the properties). If a change is detected, the `listener` callback is fired.
16583 *
16584 * - The `obj` collection is observed via standard $watch operation and is examined on every
16585 * call to $digest() to see if any items have been added, removed, or moved.
16586 * - The `listener` is called whenever anything within the `obj` has changed. Examples include
16587 * adding, removing, and moving items belonging to an object or array.
16588 *
16589 *
16590 * # Example
16591 * ```js
16592 $scope.names = ['igor', 'matias', 'misko', 'james'];
16593 $scope.dataCount = 4;
16594
16595 $scope.$watchCollection('names', function(newNames, oldNames) {
16596 $scope.dataCount = newNames.length;
16597 });
16598
16599 expect($scope.dataCount).toEqual(4);
16600 $scope.$digest();
16601
16602 //still at 4 ... no changes
16603 expect($scope.dataCount).toEqual(4);
16604
16605 $scope.names.pop();
16606 $scope.$digest();
16607
16608 //now there's been a change
16609 expect($scope.dataCount).toEqual(3);
16610 * ```
16611 *
16612 *
16613 * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
16614 * expression value should evaluate to an object or an array which is observed on each
16615 * {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
16616 * collection will trigger a call to the `listener`.
16617 *
16618 * @param {function(newCollection, oldCollection, scope)} listener a callback function called
16619 * when a change is detected.
16620 * - The `newCollection` object is the newly modified data obtained from the `obj` expression
16621 * - The `oldCollection` object is a copy of the former collection data.
16622 * Due to performance considerations, the`oldCollection` value is computed only if the
16623 * `listener` function declares two or more arguments.
16624 * - The `scope` argument refers to the current scope.
16625 *
16626 * @returns {function()} Returns a de-registration function for this listener. When the
16627 * de-registration function is executed, the internal watch operation is terminated.
16628 */
16629 $watchCollection: function(obj, listener) {
16630 $watchCollectionInterceptor.$stateful = true;
16631
16632 var self = this;
16633 // the current value, updated on each dirty-check run
16634 var newValue;
16635 // a shallow copy of the newValue from the last dirty-check run,
16636 // updated to match newValue during dirty-check run
16637 var oldValue;
16638 // a shallow copy of the newValue from when the last change happened
16639 var veryOldValue;
16640 // only track veryOldValue if the listener is asking for it
16641 var trackVeryOldValue = (listener.length > 1);
16642 var changeDetected = 0;
16643 var changeDetector = $parse(obj, $watchCollectionInterceptor);
16644 var internalArray = [];
16645 var internalObject = {};
16646 var initRun = true;
16647 var oldLength = 0;
16648
16649 function $watchCollectionInterceptor(_value) {
16650 newValue = _value;
16651 var newLength, key, bothNaN, newItem, oldItem;
16652
16653 // If the new value is undefined, then return undefined as the watch may be a one-time watch
16654 if (isUndefined(newValue)) return;
16655
16656 if (!isObject(newValue)) { // if primitive
16657 if (oldValue !== newValue) {
16658 oldValue = newValue;
16659 changeDetected++;
16660 }
16661 } else if (isArrayLike(newValue)) {
16662 if (oldValue !== internalArray) {
16663 // we are transitioning from something which was not an array into array.
16664 oldValue = internalArray;
16665 oldLength = oldValue.length = 0;
16666 changeDetected++;
16667 }
16668
16669 newLength = newValue.length;
16670
16671 if (oldLength !== newLength) {
16672 // if lengths do not match we need to trigger change notification
16673 changeDetected++;
16674 oldValue.length = oldLength = newLength;
16675 }
16676 // copy the items to oldValue and look for changes.
16677 for (var i = 0; i < newLength; i++) {
16678 oldItem = oldValue[i];
16679 newItem = newValue[i];
16680
16681 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16682 if (!bothNaN && (oldItem !== newItem)) {
16683 changeDetected++;
16684 oldValue[i] = newItem;
16685 }
16686 }
16687 } else {
16688 if (oldValue !== internalObject) {
16689 // we are transitioning from something which was not an object into object.
16690 oldValue = internalObject = {};
16691 oldLength = 0;
16692 changeDetected++;
16693 }
16694 // copy the items to oldValue and look for changes.
16695 newLength = 0;
16696 for (key in newValue) {
16697 if (hasOwnProperty.call(newValue, key)) {
16698 newLength++;
16699 newItem = newValue[key];
16700 oldItem = oldValue[key];
16701
16702 if (key in oldValue) {
16703 bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
16704 if (!bothNaN && (oldItem !== newItem)) {
16705 changeDetected++;
16706 oldValue[key] = newItem;
16707 }
16708 } else {
16709 oldLength++;
16710 oldValue[key] = newItem;
16711 changeDetected++;
16712 }
16713 }
16714 }
16715 if (oldLength > newLength) {
16716 // we used to have more keys, need to find them and destroy them.
16717 changeDetected++;
16718 for (key in oldValue) {
16719 if (!hasOwnProperty.call(newValue, key)) {
16720 oldLength--;
16721 delete oldValue[key];
16722 }
16723 }
16724 }
16725 }
16726 return changeDetected;
16727 }
16728
16729 function $watchCollectionAction() {
16730 if (initRun) {
16731 initRun = false;
16732 listener(newValue, newValue, self);
16733 } else {
16734 listener(newValue, veryOldValue, self);
16735 }
16736
16737 // make a copy for the next time a collection is changed
16738 if (trackVeryOldValue) {
16739 if (!isObject(newValue)) {
16740 //primitive
16741 veryOldValue = newValue;
16742 } else if (isArrayLike(newValue)) {
16743 veryOldValue = new Array(newValue.length);
16744 for (var i = 0; i < newValue.length; i++) {
16745 veryOldValue[i] = newValue[i];
16746 }
16747 } else { // if object
16748 veryOldValue = {};
16749 for (var key in newValue) {
16750 if (hasOwnProperty.call(newValue, key)) {
16751 veryOldValue[key] = newValue[key];
16752 }
16753 }
16754 }
16755 }
16756 }
16757
16758 return this.$watch(changeDetector, $watchCollectionAction);
16759 },
16760
16761 /**
16762 * @ngdoc method
16763 * @name $rootScope.Scope#$digest
16764 * @kind function
16765 *
16766 * @description
16767 * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
16768 * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
16769 * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
16770 * until no more listeners are firing. This means that it is possible to get into an infinite
16771 * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
16772 * iterations exceeds 10.
16773 *
16774 * Usually, you don't call `$digest()` directly in
16775 * {@link ng.directive:ngController controllers} or in
16776 * {@link ng.$compileProvider#directive directives}.
16777 * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
16778 * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
16779 *
16780 * If you want to be notified whenever `$digest()` is called,
16781 * you can register a `watchExpression` function with
16782 * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
16783 *
16784 * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
16785 *
16786 * # Example
16787 * ```js
16788 var scope = ...;
16789 scope.name = 'misko';
16790 scope.counter = 0;
16791
16792 expect(scope.counter).toEqual(0);
16793 scope.$watch('name', function(newValue, oldValue) {
16794 scope.counter = scope.counter + 1;
16795 });
16796 expect(scope.counter).toEqual(0);
16797
16798 scope.$digest();
16799 // the listener is always called during the first $digest loop after it was registered
16800 expect(scope.counter).toEqual(1);
16801
16802 scope.$digest();
16803 // but now it will not be called unless the value changes
16804 expect(scope.counter).toEqual(1);
16805
16806 scope.name = 'adam';
16807 scope.$digest();
16808 expect(scope.counter).toEqual(2);
16809 * ```
16810 *
16811 */
16812 $digest: function() {
16813 var watch, value, last, fn, get,
16814 watchers,
16815 length,
16816 dirty, ttl = TTL,
16817 next, current, target = this,
16818 watchLog = [],
16819 logIdx, asyncTask;
16820
16821 beginPhase('$digest');
16822 // Check for changes to browser url that happened in sync before the call to $digest
16823 $browser.$$checkUrlChange();
16824
16825 if (this === $rootScope && applyAsyncId !== null) {
16826 // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
16827 // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
16828 $browser.defer.cancel(applyAsyncId);
16829 flushApplyAsync();
16830 }
16831
16832 lastDirtyWatch = null;
16833
16834 do { // "while dirty" loop
16835 dirty = false;
16836 current = target;
16837
16838 while (asyncQueue.length) {
16839 try {
16840 asyncTask = asyncQueue.shift();
16841 asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
16842 } catch (e) {
16843 $exceptionHandler(e);
16844 }
16845 lastDirtyWatch = null;
16846 }
16847
16848 traverseScopesLoop:
16849 do { // "traverse the scopes" loop
16850 if ((watchers = current.$$watchers)) {
16851 // process our watches
16852 length = watchers.length;
16853 while (length--) {
16854 try {
16855 watch = watchers[length];
16856 // Most common watches are on primitives, in which case we can short
16857 // circuit it with === operator, only when === fails do we use .equals
16858 if (watch) {
16859 get = watch.get;
16860 if ((value = get(current)) !== (last = watch.last) &&
16861 !(watch.eq
16862 ? equals(value, last)
16863 : (typeof value === 'number' && typeof last === 'number'
16864 && isNaN(value) && isNaN(last)))) {
16865 dirty = true;
16866 lastDirtyWatch = watch;
16867 watch.last = watch.eq ? copy(value, null) : value;
16868 fn = watch.fn;
16869 fn(value, ((last === initWatchVal) ? value : last), current);
16870 if (ttl < 5) {
16871 logIdx = 4 - ttl;
16872 if (!watchLog[logIdx]) watchLog[logIdx] = [];
16873 watchLog[logIdx].push({
16874 msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
16875 newVal: value,
16876 oldVal: last
16877 });
16878 }
16879 } else if (watch === lastDirtyWatch) {
16880 // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
16881 // have already been tested.
16882 dirty = false;
16883 break traverseScopesLoop;
16884 }
16885 }
16886 } catch (e) {
16887 $exceptionHandler(e);
16888 }
16889 }
16890 }
16891
16892 // Insanity Warning: scope depth-first traversal
16893 // yes, this code is a bit crazy, but it works and we have tests to prove it!
16894 // this piece should be kept in sync with the traversal in $broadcast
16895 if (!(next = ((current.$$watchersCount && current.$$childHead) ||
16896 (current !== target && current.$$nextSibling)))) {
16897 while (current !== target && !(next = current.$$nextSibling)) {
16898 current = current.$parent;
16899 }
16900 }
16901 } while ((current = next));
16902
16903 // `break traverseScopesLoop;` takes us to here
16904
16905 if ((dirty || asyncQueue.length) && !(ttl--)) {
16906 clearPhase();
16907 throw $rootScopeMinErr('infdig',
16908 '{0} $digest() iterations reached. Aborting!\n' +
16909 'Watchers fired in the last 5 iterations: {1}',
16910 TTL, watchLog);
16911 }
16912
16913 } while (dirty || asyncQueue.length);
16914
16915 clearPhase();
16916
16917 while (postDigestQueue.length) {
16918 try {
16919 postDigestQueue.shift()();
16920 } catch (e) {
16921 $exceptionHandler(e);
16922 }
16923 }
16924 },
16925
16926
16927 /**
16928 * @ngdoc event
16929 * @name $rootScope.Scope#$destroy
16930 * @eventType broadcast on scope being destroyed
16931 *
16932 * @description
16933 * Broadcasted when a scope and its children are being destroyed.
16934 *
16935 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16936 * clean up DOM bindings before an element is removed from the DOM.
16937 */
16938
16939 /**
16940 * @ngdoc method
16941 * @name $rootScope.Scope#$destroy
16942 * @kind function
16943 *
16944 * @description
16945 * Removes the current scope (and all of its children) from the parent scope. Removal implies
16946 * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16947 * propagate to the current scope and its children. Removal also implies that the current
16948 * scope is eligible for garbage collection.
16949 *
16950 * The `$destroy()` is usually used by directives such as
16951 * {@link ng.directive:ngRepeat ngRepeat} for managing the
16952 * unrolling of the loop.
16953 *
16954 * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16955 * Application code can register a `$destroy` event handler that will give it a chance to
16956 * perform any necessary cleanup.
16957 *
16958 * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16959 * clean up DOM bindings before an element is removed from the DOM.
16960 */
16961 $destroy: function() {
16962 // We can't destroy a scope that has been already destroyed.
16963 if (this.$$destroyed) return;
16964 var parent = this.$parent;
16965
16966 this.$broadcast('$destroy');
16967 this.$$destroyed = true;
16968
16969 if (this === $rootScope) {
16970 //Remove handlers attached to window when $rootScope is removed
16971 $browser.$$applicationDestroyed();
16972 }
16973
16974 incrementWatchersCount(this, -this.$$watchersCount);
16975 for (var eventName in this.$$listenerCount) {
16976 decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16977 }
16978
16979 // sever all the references to parent scopes (after this cleanup, the current scope should
16980 // not be retained by any of our references and should be eligible for garbage collection)
16981 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16982 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16983 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16984 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16985
16986 // Disable listeners, watchers and apply/digest methods
16987 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16988 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16989 this.$$listeners = {};
16990
16991 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16992 this.$$nextSibling = null;
16993 cleanUpScope(this);
16994 },
16995
16996 /**
16997 * @ngdoc method
16998 * @name $rootScope.Scope#$eval
16999 * @kind function
17000 *
17001 * @description
17002 * Executes the `expression` on the current scope and returns the result. Any exceptions in
17003 * the expression are propagated (uncaught). This is useful when evaluating Angular
17004 * expressions.
17005 *
17006 * # Example
17007 * ```js
17008 var scope = ng.$rootScope.Scope();
17009 scope.a = 1;
17010 scope.b = 2;
17011
17012 expect(scope.$eval('a+b')).toEqual(3);
17013 expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
17014 * ```
17015 *
17016 * @param {(string|function())=} expression An angular expression to be executed.
17017 *
17018 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17019 * - `function(scope)`: execute the function with the current `scope` parameter.
17020 *
17021 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
17022 * @returns {*} The result of evaluating the expression.
17023 */
17024 $eval: function(expr, locals) {
17025 return $parse(expr)(this, locals);
17026 },
17027
17028 /**
17029 * @ngdoc method
17030 * @name $rootScope.Scope#$evalAsync
17031 * @kind function
17032 *
17033 * @description
17034 * Executes the expression on the current scope at a later point in time.
17035 *
17036 * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
17037 * that:
17038 *
17039 * - it will execute after the function that scheduled the evaluation (preferably before DOM
17040 * rendering).
17041 * - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
17042 * `expression` execution.
17043 *
17044 * Any exceptions from the execution of the expression are forwarded to the
17045 * {@link ng.$exceptionHandler $exceptionHandler} service.
17046 *
17047 * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
17048 * will be scheduled. However, it is encouraged to always call code that changes the model
17049 * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
17050 *
17051 * @param {(string|function())=} expression An angular expression to be executed.
17052 *
17053 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17054 * - `function(scope)`: execute the function with the current `scope` parameter.
17055 *
17056 * @param {(object)=} locals Local variables object, useful for overriding values in scope.
17057 */
17058 $evalAsync: function(expr, locals) {
17059 // if we are outside of an $digest loop and this is the first time we are scheduling async
17060 // task also schedule async auto-flush
17061 if (!$rootScope.$$phase && !asyncQueue.length) {
17062 $browser.defer(function() {
17063 if (asyncQueue.length) {
17064 $rootScope.$digest();
17065 }
17066 });
17067 }
17068
17069 asyncQueue.push({scope: this, expression: $parse(expr), locals: locals});
17070 },
17071
17072 $$postDigest: function(fn) {
17073 postDigestQueue.push(fn);
17074 },
17075
17076 /**
17077 * @ngdoc method
17078 * @name $rootScope.Scope#$apply
17079 * @kind function
17080 *
17081 * @description
17082 * `$apply()` is used to execute an expression in angular from outside of the angular
17083 * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
17084 * Because we are calling into the angular framework we need to perform proper scope life
17085 * cycle of {@link ng.$exceptionHandler exception handling},
17086 * {@link ng.$rootScope.Scope#$digest executing watches}.
17087 *
17088 * ## Life cycle
17089 *
17090 * # Pseudo-Code of `$apply()`
17091 * ```js
17092 function $apply(expr) {
17093 try {
17094 return $eval(expr);
17095 } catch (e) {
17096 $exceptionHandler(e);
17097 } finally {
17098 $root.$digest();
17099 }
17100 }
17101 * ```
17102 *
17103 *
17104 * Scope's `$apply()` method transitions through the following stages:
17105 *
17106 * 1. The {@link guide/expression expression} is executed using the
17107 * {@link ng.$rootScope.Scope#$eval $eval()} method.
17108 * 2. Any exceptions from the execution of the expression are forwarded to the
17109 * {@link ng.$exceptionHandler $exceptionHandler} service.
17110 * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
17111 * expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
17112 *
17113 *
17114 * @param {(string|function())=} exp An angular expression to be executed.
17115 *
17116 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17117 * - `function(scope)`: execute the function with current `scope` parameter.
17118 *
17119 * @returns {*} The result of evaluating the expression.
17120 */
17121 $apply: function(expr) {
17122 try {
17123 beginPhase('$apply');
17124 try {
17125 return this.$eval(expr);
17126 } finally {
17127 clearPhase();
17128 }
17129 } catch (e) {
17130 $exceptionHandler(e);
17131 } finally {
17132 try {
17133 $rootScope.$digest();
17134 } catch (e) {
17135 $exceptionHandler(e);
17136 throw e;
17137 }
17138 }
17139 },
17140
17141 /**
17142 * @ngdoc method
17143 * @name $rootScope.Scope#$applyAsync
17144 * @kind function
17145 *
17146 * @description
17147 * Schedule the invocation of $apply to occur at a later time. The actual time difference
17148 * varies across browsers, but is typically around ~10 milliseconds.
17149 *
17150 * This can be used to queue up multiple expressions which need to be evaluated in the same
17151 * digest.
17152 *
17153 * @param {(string|function())=} exp An angular expression to be executed.
17154 *
17155 * - `string`: execute using the rules as defined in {@link guide/expression expression}.
17156 * - `function(scope)`: execute the function with current `scope` parameter.
17157 */
17158 $applyAsync: function(expr) {
17159 var scope = this;
17160 expr && applyAsyncQueue.push($applyAsyncExpression);
17161 expr = $parse(expr);
17162 scheduleApplyAsync();
17163
17164 function $applyAsyncExpression() {
17165 scope.$eval(expr);
17166 }
17167 },
17168
17169 /**
17170 * @ngdoc method
17171 * @name $rootScope.Scope#$on
17172 * @kind function
17173 *
17174 * @description
17175 * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
17176 * discussion of event life cycle.
17177 *
17178 * The event listener function format is: `function(event, args...)`. The `event` object
17179 * passed into the listener has the following attributes:
17180 *
17181 * - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
17182 * `$broadcast`-ed.
17183 * - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
17184 * event propagates through the scope hierarchy, this property is set to null.
17185 * - `name` - `{string}`: name of the event.
17186 * - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
17187 * further event propagation (available only for events that were `$emit`-ed).
17188 * - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
17189 * to true.
17190 * - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
17191 *
17192 * @param {string} name Event name to listen on.
17193 * @param {function(event, ...args)} listener Function to call when the event is emitted.
17194 * @returns {function()} Returns a deregistration function for this listener.
17195 */
17196 $on: function(name, listener) {
17197 var namedListeners = this.$$listeners[name];
17198 if (!namedListeners) {
17199 this.$$listeners[name] = namedListeners = [];
17200 }
17201 namedListeners.push(listener);
17202
17203 var current = this;
17204 do {
17205 if (!current.$$listenerCount[name]) {
17206 current.$$listenerCount[name] = 0;
17207 }
17208 current.$$listenerCount[name]++;
17209 } while ((current = current.$parent));
17210
17211 var self = this;
17212 return function() {
17213 var indexOfListener = namedListeners.indexOf(listener);
17214 if (indexOfListener !== -1) {
17215 namedListeners[indexOfListener] = null;
17216 decrementListenerCount(self, 1, name);
17217 }
17218 };
17219 },
17220
17221
17222 /**
17223 * @ngdoc method
17224 * @name $rootScope.Scope#$emit
17225 * @kind function
17226 *
17227 * @description
17228 * Dispatches an event `name` upwards through the scope hierarchy notifying the
17229 * registered {@link ng.$rootScope.Scope#$on} listeners.
17230 *
17231 * The event life cycle starts at the scope on which `$emit` was called. All
17232 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
17233 * notified. Afterwards, the event traverses upwards toward the root scope and calls all
17234 * registered listeners along the way. The event will stop propagating if one of the listeners
17235 * cancels it.
17236 *
17237 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17238 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17239 *
17240 * @param {string} name Event name to emit.
17241 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
17242 * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
17243 */
17244 $emit: function(name, args) {
17245 var empty = [],
17246 namedListeners,
17247 scope = this,
17248 stopPropagation = false,
17249 event = {
17250 name: name,
17251 targetScope: scope,
17252 stopPropagation: function() {stopPropagation = true;},
17253 preventDefault: function() {
17254 event.defaultPrevented = true;
17255 },
17256 defaultPrevented: false
17257 },
17258 listenerArgs = concat([event], arguments, 1),
17259 i, length;
17260
17261 do {
17262 namedListeners = scope.$$listeners[name] || empty;
17263 event.currentScope = scope;
17264 for (i = 0, length = namedListeners.length; i < length; i++) {
17265
17266 // if listeners were deregistered, defragment the array
17267 if (!namedListeners[i]) {
17268 namedListeners.splice(i, 1);
17269 i--;
17270 length--;
17271 continue;
17272 }
17273 try {
17274 //allow all listeners attached to the current scope to run
17275 namedListeners[i].apply(null, listenerArgs);
17276 } catch (e) {
17277 $exceptionHandler(e);
17278 }
17279 }
17280 //if any listener on the current scope stops propagation, prevent bubbling
17281 if (stopPropagation) {
17282 event.currentScope = null;
17283 return event;
17284 }
17285 //traverse upwards
17286 scope = scope.$parent;
17287 } while (scope);
17288
17289 event.currentScope = null;
17290
17291 return event;
17292 },
17293
17294
17295 /**
17296 * @ngdoc method
17297 * @name $rootScope.Scope#$broadcast
17298 * @kind function
17299 *
17300 * @description
17301 * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
17302 * registered {@link ng.$rootScope.Scope#$on} listeners.
17303 *
17304 * The event life cycle starts at the scope on which `$broadcast` was called. All
17305 * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
17306 * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
17307 * scope and calls all registered listeners along the way. The event cannot be canceled.
17308 *
17309 * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
17310 * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
17311 *
17312 * @param {string} name Event name to broadcast.
17313 * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
17314 * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
17315 */
17316 $broadcast: function(name, args) {
17317 var target = this,
17318 current = target,
17319 next = target,
17320 event = {
17321 name: name,
17322 targetScope: target,
17323 preventDefault: function() {
17324 event.defaultPrevented = true;
17325 },
17326 defaultPrevented: false
17327 };
17328
17329 if (!target.$$listenerCount[name]) return event;
17330
17331 var listenerArgs = concat([event], arguments, 1),
17332 listeners, i, length;
17333
17334 //down while you can, then up and next sibling or up and next sibling until back at root
17335 while ((current = next)) {
17336 event.currentScope = current;
17337 listeners = current.$$listeners[name] || [];
17338 for (i = 0, length = listeners.length; i < length; i++) {
17339 // if listeners were deregistered, defragment the array
17340 if (!listeners[i]) {
17341 listeners.splice(i, 1);
17342 i--;
17343 length--;
17344 continue;
17345 }
17346
17347 try {
17348 listeners[i].apply(null, listenerArgs);
17349 } catch (e) {
17350 $exceptionHandler(e);
17351 }
17352 }
17353
17354 // Insanity Warning: scope depth-first traversal
17355 // yes, this code is a bit crazy, but it works and we have tests to prove it!
17356 // this piece should be kept in sync with the traversal in $digest
17357 // (though it differs due to having the extra check for $$listenerCount)
17358 if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
17359 (current !== target && current.$$nextSibling)))) {
17360 while (current !== target && !(next = current.$$nextSibling)) {
17361 current = current.$parent;
17362 }
17363 }
17364 }
17365
17366 event.currentScope = null;
17367 return event;
17368 }
17369 };
17370
17371 var $rootScope = new Scope();
17372
17373 //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
17374 var asyncQueue = $rootScope.$$asyncQueue = [];
17375 var postDigestQueue = $rootScope.$$postDigestQueue = [];
17376 var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
17377
17378 return $rootScope;
17379
17380
17381 function beginPhase(phase) {
17382 if ($rootScope.$$phase) {
17383 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
17384 }
17385
17386 $rootScope.$$phase = phase;
17387 }
17388
17389 function clearPhase() {
17390 $rootScope.$$phase = null;
17391 }
17392
17393 function incrementWatchersCount(current, count) {
17394 do {
17395 current.$$watchersCount += count;
17396 } while ((current = current.$parent));
17397 }
17398
17399 function decrementListenerCount(current, count, name) {
17400 do {
17401 current.$$listenerCount[name] -= count;
17402
17403 if (current.$$listenerCount[name] === 0) {
17404 delete current.$$listenerCount[name];
17405 }
17406 } while ((current = current.$parent));
17407 }
17408
17409 /**
17410 * function used as an initial value for watchers.
17411 * because it's unique we can easily tell it apart from other values
17412 */
17413 function initWatchVal() {}
17414
17415 function flushApplyAsync() {
17416 while (applyAsyncQueue.length) {
17417 try {
17418 applyAsyncQueue.shift()();
17419 } catch (e) {
17420 $exceptionHandler(e);
17421 }
17422 }
17423 applyAsyncId = null;
17424 }
17425
17426 function scheduleApplyAsync() {
17427 if (applyAsyncId === null) {
17428 applyAsyncId = $browser.defer(function() {
17429 $rootScope.$apply(flushApplyAsync);
17430 });
17431 }
17432 }
17433 }];
17434}
17435
17436/**
17437 * @ngdoc service
17438 * @name $rootElement
17439 *
17440 * @description
17441 * The root element of Angular application. This is either the element where {@link
17442 * ng.directive:ngApp ngApp} was declared or the element passed into
17443 * {@link angular.bootstrap}. The element represents the root element of application. It is also the
17444 * location where the application's {@link auto.$injector $injector} service gets
17445 * published, and can be retrieved using `$rootElement.injector()`.
17446 */
17447
17448
17449// the implementation is in angular.bootstrap
17450
17451/**
17452 * @description
17453 * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
17454 */
17455function $$SanitizeUriProvider() {
17456 var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
17457 imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
17458
17459 /**
17460 * @description
17461 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
17462 * urls during a[href] sanitization.
17463 *
17464 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
17465 *
17466 * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
17467 * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
17468 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
17469 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
17470 *
17471 * @param {RegExp=} regexp New regexp to whitelist urls with.
17472 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
17473 * chaining otherwise.
17474 */
17475 this.aHrefSanitizationWhitelist = function(regexp) {
17476 if (isDefined(regexp)) {
17477 aHrefSanitizationWhitelist = regexp;
17478 return this;
17479 }
17480 return aHrefSanitizationWhitelist;
17481 };
17482
17483
17484 /**
17485 * @description
17486 * Retrieves or overrides the default regular expression that is used for whitelisting of safe
17487 * urls during img[src] sanitization.
17488 *
17489 * The sanitization is a security measure aimed at prevent XSS attacks via html links.
17490 *
17491 * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
17492 * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
17493 * regular expression. If a match is found, the original url is written into the dom. Otherwise,
17494 * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
17495 *
17496 * @param {RegExp=} regexp New regexp to whitelist urls with.
17497 * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
17498 * chaining otherwise.
17499 */
17500 this.imgSrcSanitizationWhitelist = function(regexp) {
17501 if (isDefined(regexp)) {
17502 imgSrcSanitizationWhitelist = regexp;
17503 return this;
17504 }
17505 return imgSrcSanitizationWhitelist;
17506 };
17507
17508 this.$get = function() {
17509 return function sanitizeUri(uri, isImage) {
17510 var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
17511 var normalizedVal;
17512 normalizedVal = urlResolve(uri).href;
17513 if (normalizedVal !== '' && !normalizedVal.match(regex)) {
17514 return 'unsafe:' + normalizedVal;
17515 }
17516 return uri;
17517 };
17518 };
17519}
17520
17521/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
17522 * Any commits to this file should be reviewed with security in mind. *
17523 * Changes to this file can potentially create security vulnerabilities. *
17524 * An approval from 2 Core members with history of modifying *
17525 * this file is required. *
17526 * *
17527 * Does the change somehow allow for arbitrary javascript to be executed? *
17528 * Or allows for someone to change the prototype of built-in objects? *
17529 * Or gives undesired access to variables likes document or window? *
17530 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
17531
17532var $sceMinErr = minErr('$sce');
17533
17534var SCE_CONTEXTS = {
17535 HTML: 'html',
17536 CSS: 'css',
17537 URL: 'url',
17538 // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
17539 // url. (e.g. ng-include, script src, templateUrl)
17540 RESOURCE_URL: 'resourceUrl',
17541 JS: 'js'
17542};
17543
17544// Helper functions follow.
17545
17546function adjustMatcher(matcher) {
17547 if (matcher === 'self') {
17548 return matcher;
17549 } else if (isString(matcher)) {
17550 // Strings match exactly except for 2 wildcards - '*' and '**'.
17551 // '*' matches any character except those from the set ':/.?&'.
17552 // '**' matches any character (like .* in a RegExp).
17553 // More than 2 *'s raises an error as it's ill defined.
17554 if (matcher.indexOf('***') > -1) {
17555 throw $sceMinErr('iwcard',
17556 'Illegal sequence *** in string matcher. String: {0}', matcher);
17557 }
17558 matcher = escapeForRegexp(matcher).
17559 replace('\\*\\*', '.*').
17560 replace('\\*', '[^:/.?&;]*');
17561 return new RegExp('^' + matcher + '$');
17562 } else if (isRegExp(matcher)) {
17563 // The only other type of matcher allowed is a Regexp.
17564 // Match entire URL / disallow partial matches.
17565 // Flags are reset (i.e. no global, ignoreCase or multiline)
17566 return new RegExp('^' + matcher.source + '$');
17567 } else {
17568 throw $sceMinErr('imatcher',
17569 'Matchers may only be "self", string patterns or RegExp objects');
17570 }
17571}
17572
17573
17574function adjustMatchers(matchers) {
17575 var adjustedMatchers = [];
17576 if (isDefined(matchers)) {
17577 forEach(matchers, function(matcher) {
17578 adjustedMatchers.push(adjustMatcher(matcher));
17579 });
17580 }
17581 return adjustedMatchers;
17582}
17583
17584
17585/**
17586 * @ngdoc service
17587 * @name $sceDelegate
17588 * @kind function
17589 *
17590 * @description
17591 *
17592 * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
17593 * Contextual Escaping (SCE)} services to AngularJS.
17594 *
17595 * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
17596 * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS. This is
17597 * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
17598 * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
17599 * work because `$sce` delegates to `$sceDelegate` for these operations.
17600 *
17601 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
17602 *
17603 * The default instance of `$sceDelegate` should work out of the box with little pain. While you
17604 * can override it completely to change the behavior of `$sce`, the common case would
17605 * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
17606 * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
17607 * templates. Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
17608 * $sceDelegateProvider.resourceUrlWhitelist} and {@link
17609 * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
17610 */
17611
17612/**
17613 * @ngdoc provider
17614 * @name $sceDelegateProvider
17615 * @description
17616 *
17617 * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
17618 * $sceDelegate} service. This allows one to get/set the whitelists and blacklists used to ensure
17619 * that the URLs used for sourcing Angular templates are safe. Refer {@link
17620 * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
17621 * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
17622 *
17623 * For the general details about this service in Angular, read the main page for {@link ng.$sce
17624 * Strict Contextual Escaping (SCE)}.
17625 *
17626 * **Example**: Consider the following case. <a name="example"></a>
17627 *
17628 * - your app is hosted at url `http://myapp.example.com/`
17629 * - but some of your templates are hosted on other domains you control such as
17630 * `http://srv01.assets.example.com/`, `http://srv02.assets.example.com/`, etc.
17631 * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
17632 *
17633 * Here is what a secure configuration for this scenario might look like:
17634 *
17635 * ```
17636 * angular.module('myApp', []).config(function($sceDelegateProvider) {
17637 * $sceDelegateProvider.resourceUrlWhitelist([
17638 * // Allow same origin resource loads.
17639 * 'self',
17640 * // Allow loading from our assets domain. Notice the difference between * and **.
17641 * 'http://srv*.assets.example.com/**'
17642 * ]);
17643 *
17644 * // The blacklist overrides the whitelist so the open redirect here is blocked.
17645 * $sceDelegateProvider.resourceUrlBlacklist([
17646 * 'http://myapp.example.com/clickThru**'
17647 * ]);
17648 * });
17649 * ```
17650 */
17651
17652function $SceDelegateProvider() {
17653 this.SCE_CONTEXTS = SCE_CONTEXTS;
17654
17655 // Resource URLs can also be trusted by policy.
17656 var resourceUrlWhitelist = ['self'],
17657 resourceUrlBlacklist = [];
17658
17659 /**
17660 * @ngdoc method
17661 * @name $sceDelegateProvider#resourceUrlWhitelist
17662 * @kind function
17663 *
17664 * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
17665 * provided. This must be an array or null. A snapshot of this array is used so further
17666 * changes to the array are ignored.
17667 *
17668 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17669 * allowed in this array.
17670 *
17671 * <div class="alert alert-warning">
17672 * **Note:** an empty whitelist array will block all URLs!
17673 * </div>
17674 *
17675 * @return {Array} the currently set whitelist array.
17676 *
17677 * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
17678 * same origin resource requests.
17679 *
17680 * @description
17681 * Sets/Gets the whitelist of trusted resource URLs.
17682 */
17683 this.resourceUrlWhitelist = function(value) {
17684 if (arguments.length) {
17685 resourceUrlWhitelist = adjustMatchers(value);
17686 }
17687 return resourceUrlWhitelist;
17688 };
17689
17690 /**
17691 * @ngdoc method
17692 * @name $sceDelegateProvider#resourceUrlBlacklist
17693 * @kind function
17694 *
17695 * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
17696 * provided. This must be an array or null. A snapshot of this array is used so further
17697 * changes to the array are ignored.
17698 *
17699 * Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
17700 * allowed in this array.
17701 *
17702 * The typical usage for the blacklist is to **block
17703 * [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
17704 * these would otherwise be trusted but actually return content from the redirected domain.
17705 *
17706 * Finally, **the blacklist overrides the whitelist** and has the final say.
17707 *
17708 * @return {Array} the currently set blacklist array.
17709 *
17710 * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
17711 * is no blacklist.)
17712 *
17713 * @description
17714 * Sets/Gets the blacklist of trusted resource URLs.
17715 */
17716
17717 this.resourceUrlBlacklist = function(value) {
17718 if (arguments.length) {
17719 resourceUrlBlacklist = adjustMatchers(value);
17720 }
17721 return resourceUrlBlacklist;
17722 };
17723
17724 this.$get = ['$injector', function($injector) {
17725
17726 var htmlSanitizer = function htmlSanitizer(html) {
17727 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17728 };
17729
17730 if ($injector.has('$sanitize')) {
17731 htmlSanitizer = $injector.get('$sanitize');
17732 }
17733
17734
17735 function matchUrl(matcher, parsedUrl) {
17736 if (matcher === 'self') {
17737 return urlIsSameOrigin(parsedUrl);
17738 } else {
17739 // definitely a regex. See adjustMatchers()
17740 return !!matcher.exec(parsedUrl.href);
17741 }
17742 }
17743
17744 function isResourceUrlAllowedByPolicy(url) {
17745 var parsedUrl = urlResolve(url.toString());
17746 var i, n, allowed = false;
17747 // Ensure that at least one item from the whitelist allows this url.
17748 for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
17749 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
17750 allowed = true;
17751 break;
17752 }
17753 }
17754 if (allowed) {
17755 // Ensure that no item from the blacklist blocked this url.
17756 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
17757 if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
17758 allowed = false;
17759 break;
17760 }
17761 }
17762 }
17763 return allowed;
17764 }
17765
17766 function generateHolderType(Base) {
17767 var holderType = function TrustedValueHolderType(trustedValue) {
17768 this.$$unwrapTrustedValue = function() {
17769 return trustedValue;
17770 };
17771 };
17772 if (Base) {
17773 holderType.prototype = new Base();
17774 }
17775 holderType.prototype.valueOf = function sceValueOf() {
17776 return this.$$unwrapTrustedValue();
17777 };
17778 holderType.prototype.toString = function sceToString() {
17779 return this.$$unwrapTrustedValue().toString();
17780 };
17781 return holderType;
17782 }
17783
17784 var trustedValueHolderBase = generateHolderType(),
17785 byType = {};
17786
17787 byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
17788 byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
17789 byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
17790 byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
17791 byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
17792
17793 /**
17794 * @ngdoc method
17795 * @name $sceDelegate#trustAs
17796 *
17797 * @description
17798 * Returns an object that is trusted by angular for use in specified strict
17799 * contextual escaping contexts (such as ng-bind-html, ng-include, any src
17800 * attribute interpolation, any dom event binding attribute interpolation
17801 * such as for onclick, etc.) that uses the provided value.
17802 * See {@link ng.$sce $sce} for enabling strict contextual escaping.
17803 *
17804 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
17805 * resourceUrl, html, js and css.
17806 * @param {*} value The value that that should be considered trusted/safe.
17807 * @returns {*} A value that can be used to stand in for the provided `value` in places
17808 * where Angular expects a $sce.trustAs() return value.
17809 */
17810 function trustAs(type, trustedValue) {
17811 var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17812 if (!Constructor) {
17813 throw $sceMinErr('icontext',
17814 'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
17815 type, trustedValue);
17816 }
17817 if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
17818 return trustedValue;
17819 }
17820 // All the current contexts in SCE_CONTEXTS happen to be strings. In order to avoid trusting
17821 // mutable objects, we ensure here that the value passed in is actually a string.
17822 if (typeof trustedValue !== 'string') {
17823 throw $sceMinErr('itype',
17824 'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
17825 type);
17826 }
17827 return new Constructor(trustedValue);
17828 }
17829
17830 /**
17831 * @ngdoc method
17832 * @name $sceDelegate#valueOf
17833 *
17834 * @description
17835 * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
17836 * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
17837 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
17838 *
17839 * If the passed parameter is not a value that had been returned by {@link
17840 * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
17841 *
17842 * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
17843 * call or anything else.
17844 * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
17845 * `$sceDelegate.trustAs`} if `value` is the result of such a call. Otherwise, returns
17846 * `value` unchanged.
17847 */
17848 function valueOf(maybeTrusted) {
17849 if (maybeTrusted instanceof trustedValueHolderBase) {
17850 return maybeTrusted.$$unwrapTrustedValue();
17851 } else {
17852 return maybeTrusted;
17853 }
17854 }
17855
17856 /**
17857 * @ngdoc method
17858 * @name $sceDelegate#getTrusted
17859 *
17860 * @description
17861 * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
17862 * returns the originally supplied value if the queried context type is a supertype of the
17863 * created type. If this condition isn't satisfied, throws an exception.
17864 *
17865 * <div class="alert alert-danger">
17866 * Disabling auto-escaping is extremely dangerous, it usually creates a Cross Site Scripting
17867 * (XSS) vulnerability in your application.
17868 * </div>
17869 *
17870 * @param {string} type The kind of context in which this value is to be used.
17871 * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
17872 * `$sceDelegate.trustAs`} call.
17873 * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
17874 * `$sceDelegate.trustAs`} if valid in this context. Otherwise, throws an exception.
17875 */
17876 function getTrusted(type, maybeTrusted) {
17877 if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
17878 return maybeTrusted;
17879 }
17880 var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
17881 if (constructor && maybeTrusted instanceof constructor) {
17882 return maybeTrusted.$$unwrapTrustedValue();
17883 }
17884 // If we get here, then we may only take one of two actions.
17885 // 1. sanitize the value for the requested type, or
17886 // 2. throw an exception.
17887 if (type === SCE_CONTEXTS.RESOURCE_URL) {
17888 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
17889 return maybeTrusted;
17890 } else {
17891 throw $sceMinErr('insecurl',
17892 'Blocked loading resource from url not allowed by $sceDelegate policy. URL: {0}',
17893 maybeTrusted.toString());
17894 }
17895 } else if (type === SCE_CONTEXTS.HTML) {
17896 return htmlSanitizer(maybeTrusted);
17897 }
17898 throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
17899 }
17900
17901 return { trustAs: trustAs,
17902 getTrusted: getTrusted,
17903 valueOf: valueOf };
17904 }];
17905}
17906
17907
17908/**
17909 * @ngdoc provider
17910 * @name $sceProvider
17911 * @description
17912 *
17913 * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
17914 * - enable/disable Strict Contextual Escaping (SCE) in a module
17915 * - override the default implementation with a custom delegate
17916 *
17917 * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
17918 */
17919
17920/* jshint maxlen: false*/
17921
17922/**
17923 * @ngdoc service
17924 * @name $sce
17925 * @kind function
17926 *
17927 * @description
17928 *
17929 * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17930 *
17931 * # Strict Contextual Escaping
17932 *
17933 * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17934 * contexts to result in a value that is marked as safe to use for that context. One example of
17935 * such a context is binding arbitrary html controlled by the user via `ng-bind-html`. We refer
17936 * to these contexts as privileged or SCE contexts.
17937 *
17938 * As of version 1.2, Angular ships with SCE enabled by default.
17939 *
17940 * Note: When enabled (the default), IE<11 in quirks mode is not supported. In this mode, IE<11 allow
17941 * one to execute arbitrary javascript by the use of the expression() syntax. Refer
17942 * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17943 * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17944 * to the top of your HTML document.
17945 *
17946 * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17947 * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17948 *
17949 * Here's an example of a binding in a privileged context:
17950 *
17951 * ```
17952 * <input ng-model="userHtml" aria-label="User input">
17953 * <div ng-bind-html="userHtml"></div>
17954 * ```
17955 *
17956 * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user. With SCE
17957 * disabled, this application allows the user to render arbitrary HTML into the DIV.
17958 * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17959 * bindings. (HTML is just one example of a context where rendering user controlled input creates
17960 * security vulnerabilities.)
17961 *
17962 * For the case of HTML, you might use a library, either on the client side, or on the server side,
17963 * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17964 *
17965 * How would you ensure that every place that used these types of bindings was bound to a value that
17966 * was sanitized by your library (or returned as safe for rendering by your server?) How can you
17967 * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17968 * properties/fields and forgot to update the binding to the sanitized value?
17969 *
17970 * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17971 * determine that something explicitly says it's safe to use a value for binding in that
17972 * context. You can then audit your code (a simple grep would do) to ensure that this is only done
17973 * for those values that you can easily tell are safe - because they were received from your server,
17974 * sanitized by your library, etc. You can organize your codebase to help with this - perhaps
17975 * allowing only the files in a specific directory to do this. Ensuring that the internal API
17976 * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17977 *
17978 * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17979 * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17980 * obtain values that will be accepted by SCE / privileged contexts.
17981 *
17982 *
17983 * ## How does it work?
17984 *
17985 * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17986 * $sce.getTrusted(context, value)} rather than to the value directly. Directives use {@link
17987 * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17988 * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17989 *
17990 * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17991 * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}. Here's the actual code (slightly
17992 * simplified):
17993 *
17994 * ```
17995 * var ngBindHtmlDirective = ['$sce', function($sce) {
17996 * return function(scope, element, attr) {
17997 * scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17998 * element.html(value || '');
17999 * });
18000 * };
18001 * }];
18002 * ```
18003 *
18004 * ## Impact on loading templates
18005 *
18006 * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
18007 * `templateUrl`'s specified by {@link guide/directive directives}.
18008 *
18009 * By default, Angular only loads templates from the same domain and protocol as the application
18010 * document. This is done by calling {@link ng.$sce#getTrustedResourceUrl
18011 * $sce.getTrustedResourceUrl} on the template URL. To load templates from other domains and/or
18012 * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
18013 * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
18014 *
18015 * *Please note*:
18016 * The browser's
18017 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
18018 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
18019 * policy apply in addition to this and may further restrict whether the template is successfully
18020 * loaded. This means that without the right CORS policy, loading templates from a different domain
18021 * won't work on all browsers. Also, loading templates from `file://` URL does not work on some
18022 * browsers.
18023 *
18024 * ## This feels like too much overhead
18025 *
18026 * It's important to remember that SCE only applies to interpolation expressions.
18027 *
18028 * If your expressions are constant literals, they're automatically trusted and you don't need to
18029 * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
18030 * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
18031 *
18032 * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
18033 * through {@link ng.$sce#getTrusted $sce.getTrusted}. SCE doesn't play a role here.
18034 *
18035 * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
18036 * templates in `ng-include` from your application's domain without having to even know about SCE.
18037 * It blocks loading templates from other domains or loading templates over http from an https
18038 * served document. You can change these by setting your own custom {@link
18039 * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
18040 * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
18041 *
18042 * This significantly reduces the overhead. It is far easier to pay the small overhead and have an
18043 * application that's secure and can be audited to verify that with much more ease than bolting
18044 * security onto an application later.
18045 *
18046 * <a name="contexts"></a>
18047 * ## What trusted context types are supported?
18048 *
18049 * | Context | Notes |
18050 * |---------------------|----------------|
18051 * | `$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. |
18052 * | `$sce.CSS` | For CSS that's safe to source into the application. Currently unused. Feel free to use it in your own directives. |
18053 * | `$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. |
18054 * | `$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. |
18055 * | `$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. |
18056 *
18057 * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
18058 *
18059 * Each element in these arrays must be one of the following:
18060 *
18061 * - **'self'**
18062 * - The special **string**, `'self'`, can be used to match against all URLs of the **same
18063 * domain** as the application document using the **same protocol**.
18064 * - **String** (except the special value `'self'`)
18065 * - The string is matched against the full *normalized / absolute URL* of the resource
18066 * being tested (substring matches are not good enough.)
18067 * - There are exactly **two wildcard sequences** - `*` and `**`. All other characters
18068 * match themselves.
18069 * - `*`: matches zero or more occurrences of any character other than one of the following 6
18070 * characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'. It's a useful wildcard for use
18071 * in a whitelist.
18072 * - `**`: matches zero or more occurrences of *any* character. As such, it's not
18073 * appropriate for use in a scheme, domain, etc. as it would match too much. (e.g.
18074 * http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
18075 * not have been the intention.) Its usage at the very end of the path is ok. (e.g.
18076 * http://foo.example.com/templates/**).
18077 * - **RegExp** (*see caveat below*)
18078 * - *Caveat*: While regular expressions are powerful and offer great flexibility, their syntax
18079 * (and all the inevitable escaping) makes them *harder to maintain*. It's easy to
18080 * accidentally introduce a bug when one updates a complex expression (imho, all regexes should
18081 * have good test coverage). For instance, the use of `.` in the regex is correct only in a
18082 * small number of cases. A `.` character in the regex used when matching the scheme or a
18083 * subdomain could be matched against a `:` or literal `.` that was likely not intended. It
18084 * is highly recommended to use the string patterns and only fall back to regular expressions
18085 * as a last resort.
18086 * - The regular expression must be an instance of RegExp (i.e. not a string.) It is
18087 * matched against the **entire** *normalized / absolute URL* of the resource being tested
18088 * (even when the RegExp did not have the `^` and `$` codes.) In addition, any flags
18089 * present on the RegExp (such as multiline, global, ignoreCase) are ignored.
18090 * - If you are generating your JavaScript from some other templating engine (not
18091 * recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
18092 * remember to escape your regular expression (and be aware that you might need more than
18093 * one level of escaping depending on your templating engine and the way you interpolated
18094 * the value.) Do make use of your platform's escaping mechanism as it might be good
18095 * enough before coding your own. E.g. Ruby has
18096 * [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
18097 * and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
18098 * Javascript lacks a similar built in function for escaping. Take a look at Google
18099 * Closure library's [goog.string.regExpEscape(s)](
18100 * http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
18101 *
18102 * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
18103 *
18104 * ## Show me an example using SCE.
18105 *
18106 * <example module="mySceApp" deps="angular-sanitize.js">
18107 * <file name="index.html">
18108 * <div ng-controller="AppController as myCtrl">
18109 * <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
18110 * <b>User comments</b><br>
18111 * By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
18112 * $sanitize is available. If $sanitize isn't available, this results in an error instead of an
18113 * exploit.
18114 * <div class="well">
18115 * <div ng-repeat="userComment in myCtrl.userComments">
18116 * <b>{{userComment.name}}</b>:
18117 * <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
18118 * <br>
18119 * </div>
18120 * </div>
18121 * </div>
18122 * </file>
18123 *
18124 * <file name="script.js">
18125 * angular.module('mySceApp', ['ngSanitize'])
18126 * .controller('AppController', ['$http', '$templateCache', '$sce',
18127 * function($http, $templateCache, $sce) {
18128 * var self = this;
18129 * $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
18130 * self.userComments = userComments;
18131 * });
18132 * self.explicitlyTrustedHtml = $sce.trustAsHtml(
18133 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
18134 * 'sanitization."">Hover over this text.</span>');
18135 * }]);
18136 * </file>
18137 *
18138 * <file name="test_data.json">
18139 * [
18140 * { "name": "Alice",
18141 * "htmlComment":
18142 * "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
18143 * },
18144 * { "name": "Bob",
18145 * "htmlComment": "<i>Yes!</i> Am I the only other one?"
18146 * }
18147 * ]
18148 * </file>
18149 *
18150 * <file name="protractor.js" type="protractor">
18151 * describe('SCE doc demo', function() {
18152 * it('should sanitize untrusted values', function() {
18153 * expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
18154 * .toBe('<span>Is <i>anyone</i> reading this?</span>');
18155 * });
18156 *
18157 * it('should NOT sanitize explicitly trusted values', function() {
18158 * expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
18159 * '<span onmouseover="this.textContent="Explicitly trusted HTML bypasses ' +
18160 * 'sanitization."">Hover over this text.</span>');
18161 * });
18162 * });
18163 * </file>
18164 * </example>
18165 *
18166 *
18167 *
18168 * ## Can I disable SCE completely?
18169 *
18170 * Yes, you can. However, this is strongly discouraged. SCE gives you a lot of security benefits
18171 * for little coding overhead. It will be much harder to take an SCE disabled application and
18172 * either secure it on your own or enable SCE at a later stage. It might make sense to disable SCE
18173 * for cases where you have a lot of existing code that was written before SCE was introduced and
18174 * you're migrating them a module at a time.
18175 *
18176 * That said, here's how you can completely disable SCE:
18177 *
18178 * ```
18179 * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
18180 * // Completely disable SCE. For demonstration purposes only!
18181 * // Do not use in new projects.
18182 * $sceProvider.enabled(false);
18183 * });
18184 * ```
18185 *
18186 */
18187/* jshint maxlen: 100 */
18188
18189function $SceProvider() {
18190 var enabled = true;
18191
18192 /**
18193 * @ngdoc method
18194 * @name $sceProvider#enabled
18195 * @kind function
18196 *
18197 * @param {boolean=} value If provided, then enables/disables SCE.
18198 * @return {boolean} true if SCE is enabled, false otherwise.
18199 *
18200 * @description
18201 * Enables/disables SCE and returns the current value.
18202 */
18203 this.enabled = function(value) {
18204 if (arguments.length) {
18205 enabled = !!value;
18206 }
18207 return enabled;
18208 };
18209
18210
18211 /* Design notes on the default implementation for SCE.
18212 *
18213 * The API contract for the SCE delegate
18214 * -------------------------------------
18215 * The SCE delegate object must provide the following 3 methods:
18216 *
18217 * - trustAs(contextEnum, value)
18218 * This method is used to tell the SCE service that the provided value is OK to use in the
18219 * contexts specified by contextEnum. It must return an object that will be accepted by
18220 * getTrusted() for a compatible contextEnum and return this value.
18221 *
18222 * - valueOf(value)
18223 * For values that were not produced by trustAs(), return them as is. For values that were
18224 * produced by trustAs(), return the corresponding input value to trustAs. Basically, if
18225 * trustAs is wrapping the given values into some type, this operation unwraps it when given
18226 * such a value.
18227 *
18228 * - getTrusted(contextEnum, value)
18229 * This function should return the a value that is safe to use in the context specified by
18230 * contextEnum or throw and exception otherwise.
18231 *
18232 * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
18233 * opaque or wrapped in some holder object. That happens to be an implementation detail. For
18234 * instance, an implementation could maintain a registry of all trusted objects by context. In
18235 * such a case, trustAs() would return the same object that was passed in. getTrusted() would
18236 * return the same object passed in if it was found in the registry under a compatible context or
18237 * throw an exception otherwise. An implementation might only wrap values some of the time based
18238 * on some criteria. getTrusted() might return a value and not throw an exception for special
18239 * constants or objects even if not wrapped. All such implementations fulfill this contract.
18240 *
18241 *
18242 * A note on the inheritance model for SCE contexts
18243 * ------------------------------------------------
18244 * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types. This
18245 * is purely an implementation details.
18246 *
18247 * The contract is simply this:
18248 *
18249 * getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
18250 * will also succeed.
18251 *
18252 * Inheritance happens to capture this in a natural way. In some future, we
18253 * may not use inheritance anymore. That is OK because no code outside of
18254 * sce.js and sceSpecs.js would need to be aware of this detail.
18255 */
18256
18257 this.$get = ['$parse', '$sceDelegate', function(
18258 $parse, $sceDelegate) {
18259 // Prereq: Ensure that we're not running in IE<11 quirks mode. In that mode, IE < 11 allow
18260 // the "expression(javascript expression)" syntax which is insecure.
18261 if (enabled && msie < 8) {
18262 throw $sceMinErr('iequirks',
18263 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
18264 'mode. You can fix this by adding the text <!doctype html> to the top of your HTML ' +
18265 'document. See http://docs.angularjs.org/api/ng.$sce for more information.');
18266 }
18267
18268 var sce = shallowCopy(SCE_CONTEXTS);
18269
18270 /**
18271 * @ngdoc method
18272 * @name $sce#isEnabled
18273 * @kind function
18274 *
18275 * @return {Boolean} true if SCE is enabled, false otherwise. If you want to set the value, you
18276 * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
18277 *
18278 * @description
18279 * Returns a boolean indicating if SCE is enabled.
18280 */
18281 sce.isEnabled = function() {
18282 return enabled;
18283 };
18284 sce.trustAs = $sceDelegate.trustAs;
18285 sce.getTrusted = $sceDelegate.getTrusted;
18286 sce.valueOf = $sceDelegate.valueOf;
18287
18288 if (!enabled) {
18289 sce.trustAs = sce.getTrusted = function(type, value) { return value; };
18290 sce.valueOf = identity;
18291 }
18292
18293 /**
18294 * @ngdoc method
18295 * @name $sce#parseAs
18296 *
18297 * @description
18298 * Converts Angular {@link guide/expression expression} into a function. This is like {@link
18299 * ng.$parse $parse} and is identical when the expression is a literal constant. Otherwise, it
18300 * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
18301 * *result*)}
18302 *
18303 * @param {string} type The kind of SCE context in which this result will be used.
18304 * @param {string} expression String expression to compile.
18305 * @returns {function(context, locals)} a function which represents the compiled expression:
18306 *
18307 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18308 * are evaluated against (typically a scope object).
18309 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18310 * `context`.
18311 */
18312 sce.parseAs = function sceParseAs(type, expr) {
18313 var parsed = $parse(expr);
18314 if (parsed.literal && parsed.constant) {
18315 return parsed;
18316 } else {
18317 return $parse(expr, function(value) {
18318 return sce.getTrusted(type, value);
18319 });
18320 }
18321 };
18322
18323 /**
18324 * @ngdoc method
18325 * @name $sce#trustAs
18326 *
18327 * @description
18328 * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}. As such,
18329 * returns an object that is trusted by angular for use in specified strict contextual
18330 * escaping contexts (such as ng-bind-html, ng-include, any src attribute
18331 * interpolation, any dom event binding attribute interpolation such as for onclick, etc.)
18332 * that uses the provided value. See * {@link ng.$sce $sce} for enabling strict contextual
18333 * escaping.
18334 *
18335 * @param {string} type The kind of context in which this value is safe for use. e.g. url,
18336 * resourceUrl, html, js and css.
18337 * @param {*} value The value that that should be considered trusted/safe.
18338 * @returns {*} A value that can be used to stand in for the provided `value` in places
18339 * where Angular expects a $sce.trustAs() return value.
18340 */
18341
18342 /**
18343 * @ngdoc method
18344 * @name $sce#trustAsHtml
18345 *
18346 * @description
18347 * Shorthand method. `$sce.trustAsHtml(value)` →
18348 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
18349 *
18350 * @param {*} value The value to trustAs.
18351 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
18352 * $sce.getTrustedHtml(value)} to obtain the original value. (privileged directives
18353 * only accept expressions that are either literal constants or are the
18354 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18355 */
18356
18357 /**
18358 * @ngdoc method
18359 * @name $sce#trustAsUrl
18360 *
18361 * @description
18362 * Shorthand method. `$sce.trustAsUrl(value)` →
18363 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
18364 *
18365 * @param {*} value The value to trustAs.
18366 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
18367 * $sce.getTrustedUrl(value)} to obtain the original value. (privileged directives
18368 * only accept expressions that are either literal constants or are the
18369 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18370 */
18371
18372 /**
18373 * @ngdoc method
18374 * @name $sce#trustAsResourceUrl
18375 *
18376 * @description
18377 * Shorthand method. `$sce.trustAsResourceUrl(value)` →
18378 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
18379 *
18380 * @param {*} value The value to trustAs.
18381 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
18382 * $sce.getTrustedResourceUrl(value)} to obtain the original value. (privileged directives
18383 * only accept expressions that are either literal constants or are the return
18384 * value of {@link ng.$sce#trustAs $sce.trustAs}.)
18385 */
18386
18387 /**
18388 * @ngdoc method
18389 * @name $sce#trustAsJs
18390 *
18391 * @description
18392 * Shorthand method. `$sce.trustAsJs(value)` →
18393 * {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
18394 *
18395 * @param {*} value The value to trustAs.
18396 * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
18397 * $sce.getTrustedJs(value)} to obtain the original value. (privileged directives
18398 * only accept expressions that are either literal constants or are the
18399 * return value of {@link ng.$sce#trustAs $sce.trustAs}.)
18400 */
18401
18402 /**
18403 * @ngdoc method
18404 * @name $sce#getTrusted
18405 *
18406 * @description
18407 * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}. As such,
18408 * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
18409 * originally supplied value if the queried context type is a supertype of the created type.
18410 * If this condition isn't satisfied, throws an exception.
18411 *
18412 * @param {string} type The kind of context in which this value is to be used.
18413 * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
18414 * call.
18415 * @returns {*} The value the was originally provided to
18416 * {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
18417 * Otherwise, throws an exception.
18418 */
18419
18420 /**
18421 * @ngdoc method
18422 * @name $sce#getTrustedHtml
18423 *
18424 * @description
18425 * Shorthand method. `$sce.getTrustedHtml(value)` →
18426 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
18427 *
18428 * @param {*} value The value to pass to `$sce.getTrusted`.
18429 * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
18430 */
18431
18432 /**
18433 * @ngdoc method
18434 * @name $sce#getTrustedCss
18435 *
18436 * @description
18437 * Shorthand method. `$sce.getTrustedCss(value)` →
18438 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
18439 *
18440 * @param {*} value The value to pass to `$sce.getTrusted`.
18441 * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
18442 */
18443
18444 /**
18445 * @ngdoc method
18446 * @name $sce#getTrustedUrl
18447 *
18448 * @description
18449 * Shorthand method. `$sce.getTrustedUrl(value)` →
18450 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
18451 *
18452 * @param {*} value The value to pass to `$sce.getTrusted`.
18453 * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
18454 */
18455
18456 /**
18457 * @ngdoc method
18458 * @name $sce#getTrustedResourceUrl
18459 *
18460 * @description
18461 * Shorthand method. `$sce.getTrustedResourceUrl(value)` →
18462 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
18463 *
18464 * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
18465 * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
18466 */
18467
18468 /**
18469 * @ngdoc method
18470 * @name $sce#getTrustedJs
18471 *
18472 * @description
18473 * Shorthand method. `$sce.getTrustedJs(value)` →
18474 * {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
18475 *
18476 * @param {*} value The value to pass to `$sce.getTrusted`.
18477 * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
18478 */
18479
18480 /**
18481 * @ngdoc method
18482 * @name $sce#parseAsHtml
18483 *
18484 * @description
18485 * Shorthand method. `$sce.parseAsHtml(expression string)` →
18486 * {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
18487 *
18488 * @param {string} expression String expression to compile.
18489 * @returns {function(context, locals)} a function which represents the compiled expression:
18490 *
18491 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18492 * are evaluated against (typically a scope object).
18493 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18494 * `context`.
18495 */
18496
18497 /**
18498 * @ngdoc method
18499 * @name $sce#parseAsCss
18500 *
18501 * @description
18502 * Shorthand method. `$sce.parseAsCss(value)` →
18503 * {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
18504 *
18505 * @param {string} expression String expression to compile.
18506 * @returns {function(context, locals)} a function which represents the compiled expression:
18507 *
18508 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18509 * are evaluated against (typically a scope object).
18510 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18511 * `context`.
18512 */
18513
18514 /**
18515 * @ngdoc method
18516 * @name $sce#parseAsUrl
18517 *
18518 * @description
18519 * Shorthand method. `$sce.parseAsUrl(value)` →
18520 * {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
18521 *
18522 * @param {string} expression String expression to compile.
18523 * @returns {function(context, locals)} a function which represents the compiled expression:
18524 *
18525 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18526 * are evaluated against (typically a scope object).
18527 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18528 * `context`.
18529 */
18530
18531 /**
18532 * @ngdoc method
18533 * @name $sce#parseAsResourceUrl
18534 *
18535 * @description
18536 * Shorthand method. `$sce.parseAsResourceUrl(value)` →
18537 * {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
18538 *
18539 * @param {string} expression String expression to compile.
18540 * @returns {function(context, locals)} a function which represents the compiled expression:
18541 *
18542 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18543 * are evaluated against (typically a scope object).
18544 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18545 * `context`.
18546 */
18547
18548 /**
18549 * @ngdoc method
18550 * @name $sce#parseAsJs
18551 *
18552 * @description
18553 * Shorthand method. `$sce.parseAsJs(value)` →
18554 * {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
18555 *
18556 * @param {string} expression String expression to compile.
18557 * @returns {function(context, locals)} a function which represents the compiled expression:
18558 *
18559 * * `context` – `{object}` – an object against which any expressions embedded in the strings
18560 * are evaluated against (typically a scope object).
18561 * * `locals` – `{object=}` – local variables context object, useful for overriding values in
18562 * `context`.
18563 */
18564
18565 // Shorthand delegations.
18566 var parse = sce.parseAs,
18567 getTrusted = sce.getTrusted,
18568 trustAs = sce.trustAs;
18569
18570 forEach(SCE_CONTEXTS, function(enumValue, name) {
18571 var lName = lowercase(name);
18572 sce[camelCase("parse_as_" + lName)] = function(expr) {
18573 return parse(enumValue, expr);
18574 };
18575 sce[camelCase("get_trusted_" + lName)] = function(value) {
18576 return getTrusted(enumValue, value);
18577 };
18578 sce[camelCase("trust_as_" + lName)] = function(value) {
18579 return trustAs(enumValue, value);
18580 };
18581 });
18582
18583 return sce;
18584 }];
18585}
18586
18587/**
18588 * !!! This is an undocumented "private" service !!!
18589 *
18590 * @name $sniffer
18591 * @requires $window
18592 * @requires $document
18593 *
18594 * @property {boolean} history Does the browser support html5 history api ?
18595 * @property {boolean} transitions Does the browser support CSS transition events ?
18596 * @property {boolean} animations Does the browser support CSS animation events ?
18597 *
18598 * @description
18599 * This is very simple implementation of testing browser's features.
18600 */
18601function $SnifferProvider() {
18602 this.$get = ['$window', '$document', function($window, $document) {
18603 var eventSupport = {},
18604 // Chrome Packaged Apps are not allowed to access `history.pushState`. They can be detected by
18605 // the presence of `chrome.app.runtime` (see https://developer.chrome.com/apps/api_index)
18606 isChromePackagedApp = $window.chrome && $window.chrome.app && $window.chrome.app.runtime,
18607 hasHistoryPushState = !isChromePackagedApp && $window.history && $window.history.pushState,
18608 android =
18609 toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
18610 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
18611 document = $document[0] || {},
18612 vendorPrefix,
18613 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
18614 bodyStyle = document.body && document.body.style,
18615 transitions = false,
18616 animations = false,
18617 match;
18618
18619 if (bodyStyle) {
18620 for (var prop in bodyStyle) {
18621 if (match = vendorRegex.exec(prop)) {
18622 vendorPrefix = match[0];
18623 vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
18624 break;
18625 }
18626 }
18627
18628 if (!vendorPrefix) {
18629 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
18630 }
18631
18632 transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
18633 animations = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
18634
18635 if (android && (!transitions || !animations)) {
18636 transitions = isString(bodyStyle.webkitTransition);
18637 animations = isString(bodyStyle.webkitAnimation);
18638 }
18639 }
18640
18641
18642 return {
18643 // Android has history.pushState, but it does not update location correctly
18644 // so let's not use the history API at all.
18645 // http://code.google.com/p/android/issues/detail?id=17471
18646 // https://github.com/angular/angular.js/issues/904
18647
18648 // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
18649 // so let's not use the history API also
18650 // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
18651 // jshint -W018
18652 history: !!(hasHistoryPushState && !(android < 4) && !boxee),
18653 // jshint +W018
18654 hasEvent: function(event) {
18655 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
18656 // it. In particular the event is not fired when backspace or delete key are pressed or
18657 // when cut operation is performed.
18658 // IE10+ implements 'input' event but it erroneously fires under various situations,
18659 // e.g. when placeholder changes, or a form is focused.
18660 if (event === 'input' && msie <= 11) return false;
18661
18662 if (isUndefined(eventSupport[event])) {
18663 var divElm = document.createElement('div');
18664 eventSupport[event] = 'on' + event in divElm;
18665 }
18666
18667 return eventSupport[event];
18668 },
18669 csp: csp(),
18670 vendorPrefix: vendorPrefix,
18671 transitions: transitions,
18672 animations: animations,
18673 android: android
18674 };
18675 }];
18676}
18677
18678var $templateRequestMinErr = minErr('$compile');
18679
18680/**
18681 * @ngdoc provider
18682 * @name $templateRequestProvider
18683 * @description
18684 * Used to configure the options passed to the {@link $http} service when making a template request.
18685 *
18686 * For example, it can be used for specifying the "Accept" header that is sent to the server, when
18687 * requesting a template.
18688 */
18689function $TemplateRequestProvider() {
18690
18691 var httpOptions;
18692
18693 /**
18694 * @ngdoc method
18695 * @name $templateRequestProvider#httpOptions
18696 * @description
18697 * The options to be passed to the {@link $http} service when making the request.
18698 * You can use this to override options such as the "Accept" header for template requests.
18699 *
18700 * The {@link $templateRequest} will set the `cache` and the `transformResponse` properties of the
18701 * options if not overridden here.
18702 *
18703 * @param {string=} value new value for the {@link $http} options.
18704 * @returns {string|self} Returns the {@link $http} options when used as getter and self if used as setter.
18705 */
18706 this.httpOptions = function(val) {
18707 if (val) {
18708 httpOptions = val;
18709 return this;
18710 }
18711 return httpOptions;
18712 };
18713
18714 /**
18715 * @ngdoc service
18716 * @name $templateRequest
18717 *
18718 * @description
18719 * The `$templateRequest` service runs security checks then downloads the provided template using
18720 * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
18721 * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
18722 * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
18723 * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
18724 * when `tpl` is of type string and `$templateCache` has the matching entry.
18725 *
18726 * If you want to pass custom options to the `$http` service, such as setting the Accept header you
18727 * can configure this via {@link $templateRequestProvider#httpOptions}.
18728 *
18729 * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
18730 * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
18731 *
18732 * @return {Promise} a promise for the HTTP response data of the given URL.
18733 *
18734 * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
18735 */
18736 this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
18737
18738 function handleRequestFn(tpl, ignoreRequestError) {
18739 handleRequestFn.totalPendingRequests++;
18740
18741 // We consider the template cache holds only trusted templates, so
18742 // there's no need to go through whitelisting again for keys that already
18743 // are included in there. This also makes Angular accept any script
18744 // directive, no matter its name. However, we still need to unwrap trusted
18745 // types.
18746 if (!isString(tpl) || !$templateCache.get(tpl)) {
18747 tpl = $sce.getTrustedResourceUrl(tpl);
18748 }
18749
18750 var transformResponse = $http.defaults && $http.defaults.transformResponse;
18751
18752 if (isArray(transformResponse)) {
18753 transformResponse = transformResponse.filter(function(transformer) {
18754 return transformer !== defaultHttpResponseTransform;
18755 });
18756 } else if (transformResponse === defaultHttpResponseTransform) {
18757 transformResponse = null;
18758 }
18759
18760 return $http.get(tpl, extend({
18761 cache: $templateCache,
18762 transformResponse: transformResponse
18763 }, httpOptions))
18764 ['finally'](function() {
18765 handleRequestFn.totalPendingRequests--;
18766 })
18767 .then(function(response) {
18768 $templateCache.put(tpl, response.data);
18769 return response.data;
18770 }, handleError);
18771
18772 function handleError(resp) {
18773 if (!ignoreRequestError) {
18774 throw $templateRequestMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
18775 tpl, resp.status, resp.statusText);
18776 }
18777 return $q.reject(resp);
18778 }
18779 }
18780
18781 handleRequestFn.totalPendingRequests = 0;
18782
18783 return handleRequestFn;
18784 }];
18785}
18786
18787function $$TestabilityProvider() {
18788 this.$get = ['$rootScope', '$browser', '$location',
18789 function($rootScope, $browser, $location) {
18790
18791 /**
18792 * @name $testability
18793 *
18794 * @description
18795 * The private $$testability service provides a collection of methods for use when debugging
18796 * or by automated test and debugging tools.
18797 */
18798 var testability = {};
18799
18800 /**
18801 * @name $$testability#findBindings
18802 *
18803 * @description
18804 * Returns an array of elements that are bound (via ng-bind or {{}})
18805 * to expressions matching the input.
18806 *
18807 * @param {Element} element The element root to search from.
18808 * @param {string} expression The binding expression to match.
18809 * @param {boolean} opt_exactMatch If true, only returns exact matches
18810 * for the expression. Filters and whitespace are ignored.
18811 */
18812 testability.findBindings = function(element, expression, opt_exactMatch) {
18813 var bindings = element.getElementsByClassName('ng-binding');
18814 var matches = [];
18815 forEach(bindings, function(binding) {
18816 var dataBinding = angular.element(binding).data('$binding');
18817 if (dataBinding) {
18818 forEach(dataBinding, function(bindingName) {
18819 if (opt_exactMatch) {
18820 var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
18821 if (matcher.test(bindingName)) {
18822 matches.push(binding);
18823 }
18824 } else {
18825 if (bindingName.indexOf(expression) != -1) {
18826 matches.push(binding);
18827 }
18828 }
18829 });
18830 }
18831 });
18832 return matches;
18833 };
18834
18835 /**
18836 * @name $$testability#findModels
18837 *
18838 * @description
18839 * Returns an array of elements that are two-way found via ng-model to
18840 * expressions matching the input.
18841 *
18842 * @param {Element} element The element root to search from.
18843 * @param {string} expression The model expression to match.
18844 * @param {boolean} opt_exactMatch If true, only returns exact matches
18845 * for the expression.
18846 */
18847 testability.findModels = function(element, expression, opt_exactMatch) {
18848 var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
18849 for (var p = 0; p < prefixes.length; ++p) {
18850 var attributeEquals = opt_exactMatch ? '=' : '*=';
18851 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
18852 var elements = element.querySelectorAll(selector);
18853 if (elements.length) {
18854 return elements;
18855 }
18856 }
18857 };
18858
18859 /**
18860 * @name $$testability#getLocation
18861 *
18862 * @description
18863 * Shortcut for getting the location in a browser agnostic way. Returns
18864 * the path, search, and hash. (e.g. /path?a=b#hash)
18865 */
18866 testability.getLocation = function() {
18867 return $location.url();
18868 };
18869
18870 /**
18871 * @name $$testability#setLocation
18872 *
18873 * @description
18874 * Shortcut for navigating to a location without doing a full page reload.
18875 *
18876 * @param {string} url The location url (path, search and hash,
18877 * e.g. /path?a=b#hash) to go to.
18878 */
18879 testability.setLocation = function(url) {
18880 if (url !== $location.url()) {
18881 $location.url(url);
18882 $rootScope.$digest();
18883 }
18884 };
18885
18886 /**
18887 * @name $$testability#whenStable
18888 *
18889 * @description
18890 * Calls the callback when $timeout and $http requests are completed.
18891 *
18892 * @param {function} callback
18893 */
18894 testability.whenStable = function(callback) {
18895 $browser.notifyWhenNoOutstandingRequests(callback);
18896 };
18897
18898 return testability;
18899 }];
18900}
18901
18902function $TimeoutProvider() {
18903 this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
18904 function($rootScope, $browser, $q, $$q, $exceptionHandler) {
18905
18906 var deferreds = {};
18907
18908
18909 /**
18910 * @ngdoc service
18911 * @name $timeout
18912 *
18913 * @description
18914 * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
18915 * block and delegates any exceptions to
18916 * {@link ng.$exceptionHandler $exceptionHandler} service.
18917 *
18918 * The return value of calling `$timeout` is a promise, which will be resolved when
18919 * the delay has passed and the timeout function, if provided, is executed.
18920 *
18921 * To cancel a timeout request, call `$timeout.cancel(promise)`.
18922 *
18923 * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
18924 * synchronously flush the queue of deferred functions.
18925 *
18926 * If you only want a promise that will be resolved after some specified delay
18927 * then you can call `$timeout` without the `fn` function.
18928 *
18929 * @param {function()=} fn A function, whose execution should be delayed.
18930 * @param {number=} [delay=0] Delay in milliseconds.
18931 * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
18932 * will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
18933 * @param {...*=} Pass additional parameters to the executed function.
18934 * @returns {Promise} Promise that will be resolved when the timeout is reached. The promise
18935 * will be resolved with the return value of the `fn` function.
18936 *
18937 */
18938 function timeout(fn, delay, invokeApply) {
18939 if (!isFunction(fn)) {
18940 invokeApply = delay;
18941 delay = fn;
18942 fn = noop;
18943 }
18944
18945 var args = sliceArgs(arguments, 3),
18946 skipApply = (isDefined(invokeApply) && !invokeApply),
18947 deferred = (skipApply ? $$q : $q).defer(),
18948 promise = deferred.promise,
18949 timeoutId;
18950
18951 timeoutId = $browser.defer(function() {
18952 try {
18953 deferred.resolve(fn.apply(null, args));
18954 } catch (e) {
18955 deferred.reject(e);
18956 $exceptionHandler(e);
18957 }
18958 finally {
18959 delete deferreds[promise.$$timeoutId];
18960 }
18961
18962 if (!skipApply) $rootScope.$apply();
18963 }, delay);
18964
18965 promise.$$timeoutId = timeoutId;
18966 deferreds[timeoutId] = deferred;
18967
18968 return promise;
18969 }
18970
18971
18972 /**
18973 * @ngdoc method
18974 * @name $timeout#cancel
18975 *
18976 * @description
18977 * Cancels a task associated with the `promise`. As a result of this, the promise will be
18978 * resolved with a rejection.
18979 *
18980 * @param {Promise=} promise Promise returned by the `$timeout` function.
18981 * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18982 * canceled.
18983 */
18984 timeout.cancel = function(promise) {
18985 if (promise && promise.$$timeoutId in deferreds) {
18986 deferreds[promise.$$timeoutId].reject('canceled');
18987 delete deferreds[promise.$$timeoutId];
18988 return $browser.defer.cancel(promise.$$timeoutId);
18989 }
18990 return false;
18991 };
18992
18993 return timeout;
18994 }];
18995}
18996
18997// NOTE: The usage of window and document instead of $window and $document here is
18998// deliberate. This service depends on the specific behavior of anchor nodes created by the
18999// browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
19000// cause us to break tests. In addition, when the browser resolves a URL for XHR, it
19001// doesn't know about mocked locations and resolves URLs to the real document - which is
19002// exactly the behavior needed here. There is little value is mocking these out for this
19003// service.
19004var urlParsingNode = document.createElement("a");
19005var originUrl = urlResolve(window.location.href);
19006
19007
19008/**
19009 *
19010 * Implementation Notes for non-IE browsers
19011 * ----------------------------------------
19012 * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
19013 * results both in the normalizing and parsing of the URL. Normalizing means that a relative
19014 * URL will be resolved into an absolute URL in the context of the application document.
19015 * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
19016 * properties are all populated to reflect the normalized URL. This approach has wide
19017 * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc. See
19018 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
19019 *
19020 * Implementation Notes for IE
19021 * ---------------------------
19022 * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
19023 * browsers. However, the parsed components will not be set if the URL assigned did not specify
19024 * them. (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.) We
19025 * work around that by performing the parsing in a 2nd step by taking a previously normalized
19026 * URL (e.g. by assigning to a.href) and assigning it a.href again. This correctly populates the
19027 * properties such as protocol, hostname, port, etc.
19028 *
19029 * References:
19030 * http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
19031 * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
19032 * http://url.spec.whatwg.org/#urlutils
19033 * https://github.com/angular/angular.js/pull/2902
19034 * http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
19035 *
19036 * @kind function
19037 * @param {string} url The URL to be parsed.
19038 * @description Normalizes and parses a URL.
19039 * @returns {object} Returns the normalized URL as a dictionary.
19040 *
19041 * | member name | Description |
19042 * |---------------|----------------|
19043 * | href | A normalized version of the provided URL if it was not an absolute URL |
19044 * | protocol | The protocol including the trailing colon |
19045 * | host | The host and port (if the port is non-default) of the normalizedUrl |
19046 * | search | The search params, minus the question mark |
19047 * | hash | The hash string, minus the hash symbol
19048 * | hostname | The hostname
19049 * | port | The port, without ":"
19050 * | pathname | The pathname, beginning with "/"
19051 *
19052 */
19053function urlResolve(url) {
19054 var href = url;
19055
19056 if (msie) {
19057 // Normalize before parse. Refer Implementation Notes on why this is
19058 // done in two steps on IE.
19059 urlParsingNode.setAttribute("href", href);
19060 href = urlParsingNode.href;
19061 }
19062
19063 urlParsingNode.setAttribute('href', href);
19064
19065 // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
19066 return {
19067 href: urlParsingNode.href,
19068 protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
19069 host: urlParsingNode.host,
19070 search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
19071 hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
19072 hostname: urlParsingNode.hostname,
19073 port: urlParsingNode.port,
19074 pathname: (urlParsingNode.pathname.charAt(0) === '/')
19075 ? urlParsingNode.pathname
19076 : '/' + urlParsingNode.pathname
19077 };
19078}
19079
19080/**
19081 * Parse a request URL and determine whether this is a same-origin request as the application document.
19082 *
19083 * @param {string|object} requestUrl The url of the request as a string that will be resolved
19084 * or a parsed URL object.
19085 * @returns {boolean} Whether the request is for the same origin as the application document.
19086 */
19087function urlIsSameOrigin(requestUrl) {
19088 var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
19089 return (parsed.protocol === originUrl.protocol &&
19090 parsed.host === originUrl.host);
19091}
19092
19093/**
19094 * @ngdoc service
19095 * @name $window
19096 *
19097 * @description
19098 * A reference to the browser's `window` object. While `window`
19099 * is globally available in JavaScript, it causes testability problems, because
19100 * it is a global variable. In angular we always refer to it through the
19101 * `$window` service, so it may be overridden, removed or mocked for testing.
19102 *
19103 * Expressions, like the one defined for the `ngClick` directive in the example
19104 * below, are evaluated with respect to the current scope. Therefore, there is
19105 * no risk of inadvertently coding in a dependency on a global value in such an
19106 * expression.
19107 *
19108 * @example
19109 <example module="windowExample">
19110 <file name="index.html">
19111 <script>
19112 angular.module('windowExample', [])
19113 .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
19114 $scope.greeting = 'Hello, World!';
19115 $scope.doGreeting = function(greeting) {
19116 $window.alert(greeting);
19117 };
19118 }]);
19119 </script>
19120 <div ng-controller="ExampleController">
19121 <input type="text" ng-model="greeting" aria-label="greeting" />
19122 <button ng-click="doGreeting(greeting)">ALERT</button>
19123 </div>
19124 </file>
19125 <file name="protractor.js" type="protractor">
19126 it('should display the greeting in the input box', function() {
19127 element(by.model('greeting')).sendKeys('Hello, E2E Tests');
19128 // If we click the button it will block the test runner
19129 // element(':button').click();
19130 });
19131 </file>
19132 </example>
19133 */
19134function $WindowProvider() {
19135 this.$get = valueFn(window);
19136}
19137
19138/**
19139 * @name $$cookieReader
19140 * @requires $document
19141 *
19142 * @description
19143 * This is a private service for reading cookies used by $http and ngCookies
19144 *
19145 * @return {Object} a key/value map of the current cookies
19146 */
19147function $$CookieReader($document) {
19148 var rawDocument = $document[0] || {};
19149 var lastCookies = {};
19150 var lastCookieString = '';
19151
19152 function safeDecodeURIComponent(str) {
19153 try {
19154 return decodeURIComponent(str);
19155 } catch (e) {
19156 return str;
19157 }
19158 }
19159
19160 return function() {
19161 var cookieArray, cookie, i, index, name;
19162 var currentCookieString = rawDocument.cookie || '';
19163
19164 if (currentCookieString !== lastCookieString) {
19165 lastCookieString = currentCookieString;
19166 cookieArray = lastCookieString.split('; ');
19167 lastCookies = {};
19168
19169 for (i = 0; i < cookieArray.length; i++) {
19170 cookie = cookieArray[i];
19171 index = cookie.indexOf('=');
19172 if (index > 0) { //ignore nameless cookies
19173 name = safeDecodeURIComponent(cookie.substring(0, index));
19174 // the first value that is seen for a cookie is the most
19175 // specific one. values for the same cookie name that
19176 // follow are for less specific paths.
19177 if (isUndefined(lastCookies[name])) {
19178 lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
19179 }
19180 }
19181 }
19182 }
19183 return lastCookies;
19184 };
19185}
19186
19187$$CookieReader.$inject = ['$document'];
19188
19189function $$CookieReaderProvider() {
19190 this.$get = $$CookieReader;
19191}
19192
19193/* global currencyFilter: true,
19194 dateFilter: true,
19195 filterFilter: true,
19196 jsonFilter: true,
19197 limitToFilter: true,
19198 lowercaseFilter: true,
19199 numberFilter: true,
19200 orderByFilter: true,
19201 uppercaseFilter: true,
19202 */
19203
19204/**
19205 * @ngdoc provider
19206 * @name $filterProvider
19207 * @description
19208 *
19209 * Filters are just functions which transform input to an output. However filters need to be
19210 * Dependency Injected. To achieve this a filter definition consists of a factory function which is
19211 * annotated with dependencies and is responsible for creating a filter function.
19212 *
19213 * <div class="alert alert-warning">
19214 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
19215 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
19216 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
19217 * (`myapp_subsection_filterx`).
19218 * </div>
19219 *
19220 * ```js
19221 * // Filter registration
19222 * function MyModule($provide, $filterProvider) {
19223 * // create a service to demonstrate injection (not always needed)
19224 * $provide.value('greet', function(name){
19225 * return 'Hello ' + name + '!';
19226 * });
19227 *
19228 * // register a filter factory which uses the
19229 * // greet service to demonstrate DI.
19230 * $filterProvider.register('greet', function(greet){
19231 * // return the filter function which uses the greet service
19232 * // to generate salutation
19233 * return function(text) {
19234 * // filters need to be forgiving so check input validity
19235 * return text && greet(text) || text;
19236 * };
19237 * });
19238 * }
19239 * ```
19240 *
19241 * The filter function is registered with the `$injector` under the filter name suffix with
19242 * `Filter`.
19243 *
19244 * ```js
19245 * it('should be the same instance', inject(
19246 * function($filterProvider) {
19247 * $filterProvider.register('reverse', function(){
19248 * return ...;
19249 * });
19250 * },
19251 * function($filter, reverseFilter) {
19252 * expect($filter('reverse')).toBe(reverseFilter);
19253 * });
19254 * ```
19255 *
19256 *
19257 * For more information about how angular filters work, and how to create your own filters, see
19258 * {@link guide/filter Filters} in the Angular Developer Guide.
19259 */
19260
19261/**
19262 * @ngdoc service
19263 * @name $filter
19264 * @kind function
19265 * @description
19266 * Filters are used for formatting data displayed to the user.
19267 *
19268 * The general syntax in templates is as follows:
19269 *
19270 * {{ expression [| filter_name[:parameter_value] ... ] }}
19271 *
19272 * @param {String} name Name of the filter function to retrieve
19273 * @return {Function} the filter function
19274 * @example
19275 <example name="$filter" module="filterExample">
19276 <file name="index.html">
19277 <div ng-controller="MainCtrl">
19278 <h3>{{ originalText }}</h3>
19279 <h3>{{ filteredText }}</h3>
19280 </div>
19281 </file>
19282
19283 <file name="script.js">
19284 angular.module('filterExample', [])
19285 .controller('MainCtrl', function($scope, $filter) {
19286 $scope.originalText = 'hello';
19287 $scope.filteredText = $filter('uppercase')($scope.originalText);
19288 });
19289 </file>
19290 </example>
19291 */
19292$FilterProvider.$inject = ['$provide'];
19293function $FilterProvider($provide) {
19294 var suffix = 'Filter';
19295
19296 /**
19297 * @ngdoc method
19298 * @name $filterProvider#register
19299 * @param {string|Object} name Name of the filter function, or an object map of filters where
19300 * the keys are the filter names and the values are the filter factories.
19301 *
19302 * <div class="alert alert-warning">
19303 * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
19304 * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
19305 * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
19306 * (`myapp_subsection_filterx`).
19307 * </div>
19308 * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
19309 * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
19310 * of the registered filter instances.
19311 */
19312 function register(name, factory) {
19313 if (isObject(name)) {
19314 var filters = {};
19315 forEach(name, function(filter, key) {
19316 filters[key] = register(key, filter);
19317 });
19318 return filters;
19319 } else {
19320 return $provide.factory(name + suffix, factory);
19321 }
19322 }
19323 this.register = register;
19324
19325 this.$get = ['$injector', function($injector) {
19326 return function(name) {
19327 return $injector.get(name + suffix);
19328 };
19329 }];
19330
19331 ////////////////////////////////////////
19332
19333 /* global
19334 currencyFilter: false,
19335 dateFilter: false,
19336 filterFilter: false,
19337 jsonFilter: false,
19338 limitToFilter: false,
19339 lowercaseFilter: false,
19340 numberFilter: false,
19341 orderByFilter: false,
19342 uppercaseFilter: false,
19343 */
19344
19345 register('currency', currencyFilter);
19346 register('date', dateFilter);
19347 register('filter', filterFilter);
19348 register('json', jsonFilter);
19349 register('limitTo', limitToFilter);
19350 register('lowercase', lowercaseFilter);
19351 register('number', numberFilter);
19352 register('orderBy', orderByFilter);
19353 register('uppercase', uppercaseFilter);
19354}
19355
19356/**
19357 * @ngdoc filter
19358 * @name filter
19359 * @kind function
19360 *
19361 * @description
19362 * Selects a subset of items from `array` and returns it as a new array.
19363 *
19364 * @param {Array} array The source array.
19365 * @param {string|Object|function()} expression The predicate to be used for selecting items from
19366 * `array`.
19367 *
19368 * Can be one of:
19369 *
19370 * - `string`: The string is used for matching against the contents of the `array`. All strings or
19371 * objects with string properties in `array` that match this string will be returned. This also
19372 * applies to nested object properties.
19373 * The predicate can be negated by prefixing the string with `!`.
19374 *
19375 * - `Object`: A pattern object can be used to filter specific properties on objects contained
19376 * by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
19377 * which have property `name` containing "M" and property `phone` containing "1". A special
19378 * property name `$` can be used (as in `{$:"text"}`) to accept a match against any
19379 * property of the object or its nested object properties. That's equivalent to the simple
19380 * substring match with a `string` as described above. The predicate can be negated by prefixing
19381 * the string with `!`.
19382 * For example `{name: "!M"}` predicate will return an array of items which have property `name`
19383 * not containing "M".
19384 *
19385 * Note that a named property will match properties on the same level only, while the special
19386 * `$` property will match properties on the same level or deeper. E.g. an array item like
19387 * `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
19388 * **will** be matched by `{$: 'John'}`.
19389 *
19390 * - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
19391 * The function is called for each element of the array, with the element, its index, and
19392 * the entire array itself as arguments.
19393 *
19394 * The final result is an array of those elements that the predicate returned true for.
19395 *
19396 * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
19397 * determining if the expected value (from the filter expression) and actual value (from
19398 * the object in the array) should be considered a match.
19399 *
19400 * Can be one of:
19401 *
19402 * - `function(actual, expected)`:
19403 * The function will be given the object value and the predicate value to compare and
19404 * should return true if both values should be considered equal.
19405 *
19406 * - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
19407 * This is essentially strict comparison of expected and actual.
19408 *
19409 * - `false|undefined`: A short hand for a function which will look for a substring match in case
19410 * insensitive way.
19411 *
19412 * Primitive values are converted to strings. Objects are not compared against primitives,
19413 * unless they have a custom `toString` method (e.g. `Date` objects).
19414 *
19415 * @example
19416 <example>
19417 <file name="index.html">
19418 <div ng-init="friends = [{name:'John', phone:'555-1276'},
19419 {name:'Mary', phone:'800-BIG-MARY'},
19420 {name:'Mike', phone:'555-4321'},
19421 {name:'Adam', phone:'555-5678'},
19422 {name:'Julie', phone:'555-8765'},
19423 {name:'Juliette', phone:'555-5678'}]"></div>
19424
19425 <label>Search: <input ng-model="searchText"></label>
19426 <table id="searchTextResults">
19427 <tr><th>Name</th><th>Phone</th></tr>
19428 <tr ng-repeat="friend in friends | filter:searchText">
19429 <td>{{friend.name}}</td>
19430 <td>{{friend.phone}}</td>
19431 </tr>
19432 </table>
19433 <hr>
19434 <label>Any: <input ng-model="search.$"></label> <br>
19435 <label>Name only <input ng-model="search.name"></label><br>
19436 <label>Phone only <input ng-model="search.phone"></label><br>
19437 <label>Equality <input type="checkbox" ng-model="strict"></label><br>
19438 <table id="searchObjResults">
19439 <tr><th>Name</th><th>Phone</th></tr>
19440 <tr ng-repeat="friendObj in friends | filter:search:strict">
19441 <td>{{friendObj.name}}</td>
19442 <td>{{friendObj.phone}}</td>
19443 </tr>
19444 </table>
19445 </file>
19446 <file name="protractor.js" type="protractor">
19447 var expectFriendNames = function(expectedNames, key) {
19448 element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
19449 arr.forEach(function(wd, i) {
19450 expect(wd.getText()).toMatch(expectedNames[i]);
19451 });
19452 });
19453 };
19454
19455 it('should search across all fields when filtering with a string', function() {
19456 var searchText = element(by.model('searchText'));
19457 searchText.clear();
19458 searchText.sendKeys('m');
19459 expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
19460
19461 searchText.clear();
19462 searchText.sendKeys('76');
19463 expectFriendNames(['John', 'Julie'], 'friend');
19464 });
19465
19466 it('should search in specific fields when filtering with a predicate object', function() {
19467 var searchAny = element(by.model('search.$'));
19468 searchAny.clear();
19469 searchAny.sendKeys('i');
19470 expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
19471 });
19472 it('should use a equal comparison when comparator is true', function() {
19473 var searchName = element(by.model('search.name'));
19474 var strict = element(by.model('strict'));
19475 searchName.clear();
19476 searchName.sendKeys('Julie');
19477 strict.click();
19478 expectFriendNames(['Julie'], 'friendObj');
19479 });
19480 </file>
19481 </example>
19482 */
19483function filterFilter() {
19484 return function(array, expression, comparator) {
19485 if (!isArrayLike(array)) {
19486 if (array == null) {
19487 return array;
19488 } else {
19489 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
19490 }
19491 }
19492
19493 var expressionType = getTypeForFilter(expression);
19494 var predicateFn;
19495 var matchAgainstAnyProp;
19496
19497 switch (expressionType) {
19498 case 'function':
19499 predicateFn = expression;
19500 break;
19501 case 'boolean':
19502 case 'null':
19503 case 'number':
19504 case 'string':
19505 matchAgainstAnyProp = true;
19506 //jshint -W086
19507 case 'object':
19508 //jshint +W086
19509 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
19510 break;
19511 default:
19512 return array;
19513 }
19514
19515 return Array.prototype.filter.call(array, predicateFn);
19516 };
19517}
19518
19519// Helper functions for `filterFilter`
19520function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
19521 var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
19522 var predicateFn;
19523
19524 if (comparator === true) {
19525 comparator = equals;
19526 } else if (!isFunction(comparator)) {
19527 comparator = function(actual, expected) {
19528 if (isUndefined(actual)) {
19529 // No substring matching against `undefined`
19530 return false;
19531 }
19532 if ((actual === null) || (expected === null)) {
19533 // No substring matching against `null`; only match against `null`
19534 return actual === expected;
19535 }
19536 if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
19537 // Should not compare primitives against objects, unless they have custom `toString` method
19538 return false;
19539 }
19540
19541 actual = lowercase('' + actual);
19542 expected = lowercase('' + expected);
19543 return actual.indexOf(expected) !== -1;
19544 };
19545 }
19546
19547 predicateFn = function(item) {
19548 if (shouldMatchPrimitives && !isObject(item)) {
19549 return deepCompare(item, expression.$, comparator, false);
19550 }
19551 return deepCompare(item, expression, comparator, matchAgainstAnyProp);
19552 };
19553
19554 return predicateFn;
19555}
19556
19557function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
19558 var actualType = getTypeForFilter(actual);
19559 var expectedType = getTypeForFilter(expected);
19560
19561 if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
19562 return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
19563 } else if (isArray(actual)) {
19564 // In case `actual` is an array, consider it a match
19565 // if ANY of it's items matches `expected`
19566 return actual.some(function(item) {
19567 return deepCompare(item, expected, comparator, matchAgainstAnyProp);
19568 });
19569 }
19570
19571 switch (actualType) {
19572 case 'object':
19573 var key;
19574 if (matchAgainstAnyProp) {
19575 for (key in actual) {
19576 if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
19577 return true;
19578 }
19579 }
19580 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
19581 } else if (expectedType === 'object') {
19582 for (key in expected) {
19583 var expectedVal = expected[key];
19584 if (isFunction(expectedVal) || isUndefined(expectedVal)) {
19585 continue;
19586 }
19587
19588 var matchAnyProperty = key === '$';
19589 var actualVal = matchAnyProperty ? actual : actual[key];
19590 if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
19591 return false;
19592 }
19593 }
19594 return true;
19595 } else {
19596 return comparator(actual, expected);
19597 }
19598 break;
19599 case 'function':
19600 return false;
19601 default:
19602 return comparator(actual, expected);
19603 }
19604}
19605
19606// Used for easily differentiating between `null` and actual `object`
19607function getTypeForFilter(val) {
19608 return (val === null) ? 'null' : typeof val;
19609}
19610
19611var MAX_DIGITS = 22;
19612var DECIMAL_SEP = '.';
19613var ZERO_CHAR = '0';
19614
19615/**
19616 * @ngdoc filter
19617 * @name currency
19618 * @kind function
19619 *
19620 * @description
19621 * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
19622 * symbol for current locale is used.
19623 *
19624 * @param {number} amount Input to filter.
19625 * @param {string=} symbol Currency symbol or identifier to be displayed.
19626 * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
19627 * @returns {string} Formatted number.
19628 *
19629 *
19630 * @example
19631 <example module="currencyExample">
19632 <file name="index.html">
19633 <script>
19634 angular.module('currencyExample', [])
19635 .controller('ExampleController', ['$scope', function($scope) {
19636 $scope.amount = 1234.56;
19637 }]);
19638 </script>
19639 <div ng-controller="ExampleController">
19640 <input type="number" ng-model="amount" aria-label="amount"> <br>
19641 default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
19642 custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
19643 no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
19644 </div>
19645 </file>
19646 <file name="protractor.js" type="protractor">
19647 it('should init with 1234.56', function() {
19648 expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
19649 expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
19650 expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
19651 });
19652 it('should update', function() {
19653 if (browser.params.browser == 'safari') {
19654 // Safari does not understand the minus key. See
19655 // https://github.com/angular/protractor/issues/481
19656 return;
19657 }
19658 element(by.model('amount')).clear();
19659 element(by.model('amount')).sendKeys('-1234');
19660 expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
19661 expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
19662 expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
19663 });
19664 </file>
19665 </example>
19666 */
19667currencyFilter.$inject = ['$locale'];
19668function currencyFilter($locale) {
19669 var formats = $locale.NUMBER_FORMATS;
19670 return function(amount, currencySymbol, fractionSize) {
19671 if (isUndefined(currencySymbol)) {
19672 currencySymbol = formats.CURRENCY_SYM;
19673 }
19674
19675 if (isUndefined(fractionSize)) {
19676 fractionSize = formats.PATTERNS[1].maxFrac;
19677 }
19678
19679 // if null or undefined pass it through
19680 return (amount == null)
19681 ? amount
19682 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
19683 replace(/\u00A4/g, currencySymbol);
19684 };
19685}
19686
19687/**
19688 * @ngdoc filter
19689 * @name number
19690 * @kind function
19691 *
19692 * @description
19693 * Formats a number as text.
19694 *
19695 * If the input is null or undefined, it will just be returned.
19696 * If the input is infinite (Infinity or -Infinity), the Infinity symbol '∞' or '-∞' is returned, respectively.
19697 * If the input is not a number an empty string is returned.
19698 *
19699 *
19700 * @param {number|string} number Number to format.
19701 * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
19702 * If this is not provided then the fraction size is computed from the current locale's number
19703 * formatting pattern. In the case of the default locale, it will be 3.
19704 * @returns {string} Number rounded to fractionSize and places a “,” after each third digit.
19705 *
19706 * @example
19707 <example module="numberFilterExample">
19708 <file name="index.html">
19709 <script>
19710 angular.module('numberFilterExample', [])
19711 .controller('ExampleController', ['$scope', function($scope) {
19712 $scope.val = 1234.56789;
19713 }]);
19714 </script>
19715 <div ng-controller="ExampleController">
19716 <label>Enter number: <input ng-model='val'></label><br>
19717 Default formatting: <span id='number-default'>{{val | number}}</span><br>
19718 No fractions: <span>{{val | number:0}}</span><br>
19719 Negative number: <span>{{-val | number:4}}</span>
19720 </div>
19721 </file>
19722 <file name="protractor.js" type="protractor">
19723 it('should format numbers', function() {
19724 expect(element(by.id('number-default')).getText()).toBe('1,234.568');
19725 expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
19726 expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
19727 });
19728
19729 it('should update', function() {
19730 element(by.model('val')).clear();
19731 element(by.model('val')).sendKeys('3374.333');
19732 expect(element(by.id('number-default')).getText()).toBe('3,374.333');
19733 expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
19734 expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
19735 });
19736 </file>
19737 </example>
19738 */
19739numberFilter.$inject = ['$locale'];
19740function numberFilter($locale) {
19741 var formats = $locale.NUMBER_FORMATS;
19742 return function(number, fractionSize) {
19743
19744 // if null or undefined pass it through
19745 return (number == null)
19746 ? number
19747 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
19748 fractionSize);
19749 };
19750}
19751
19752/**
19753 * Parse a number (as a string) into three components that can be used
19754 * for formatting the number.
19755 *
19756 * (Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/)
19757 *
19758 * @param {string} numStr The number to parse
19759 * @return {object} An object describing this number, containing the following keys:
19760 * - d : an array of digits containing leading zeros as necessary
19761 * - i : the number of the digits in `d` that are to the left of the decimal point
19762 * - e : the exponent for numbers that would need more than `MAX_DIGITS` digits in `d`
19763 *
19764 */
19765function parse(numStr) {
19766 var exponent = 0, digits, numberOfIntegerDigits;
19767 var i, j, zeros;
19768
19769 // Decimal point?
19770 if ((numberOfIntegerDigits = numStr.indexOf(DECIMAL_SEP)) > -1) {
19771 numStr = numStr.replace(DECIMAL_SEP, '');
19772 }
19773
19774 // Exponential form?
19775 if ((i = numStr.search(/e/i)) > 0) {
19776 // Work out the exponent.
19777 if (numberOfIntegerDigits < 0) numberOfIntegerDigits = i;
19778 numberOfIntegerDigits += +numStr.slice(i + 1);
19779 numStr = numStr.substring(0, i);
19780 } else if (numberOfIntegerDigits < 0) {
19781 // There was no decimal point or exponent so it is an integer.
19782 numberOfIntegerDigits = numStr.length;
19783 }
19784
19785 // Count the number of leading zeros.
19786 for (i = 0; numStr.charAt(i) == ZERO_CHAR; i++) {/* jshint noempty: false */}
19787
19788 if (i == (zeros = numStr.length)) {
19789 // The digits are all zero.
19790 digits = [0];
19791 numberOfIntegerDigits = 1;
19792 } else {
19793 // Count the number of trailing zeros
19794 zeros--;
19795 while (numStr.charAt(zeros) == ZERO_CHAR) zeros--;
19796
19797 // Trailing zeros are insignificant so ignore them
19798 numberOfIntegerDigits -= i;
19799 digits = [];
19800 // Convert string to array of digits without leading/trailing zeros.
19801 for (j = 0; i <= zeros; i++, j++) {
19802 digits[j] = +numStr.charAt(i);
19803 }
19804 }
19805
19806 // If the number overflows the maximum allowed digits then use an exponent.
19807 if (numberOfIntegerDigits > MAX_DIGITS) {
19808 digits = digits.splice(0, MAX_DIGITS - 1);
19809 exponent = numberOfIntegerDigits - 1;
19810 numberOfIntegerDigits = 1;
19811 }
19812
19813 return { d: digits, e: exponent, i: numberOfIntegerDigits };
19814}
19815
19816/**
19817 * Round the parsed number to the specified number of decimal places
19818 * This function changed the parsedNumber in-place
19819 */
19820function roundNumber(parsedNumber, fractionSize, minFrac, maxFrac) {
19821 var digits = parsedNumber.d;
19822 var fractionLen = digits.length - parsedNumber.i;
19823
19824 // determine fractionSize if it is not specified; `+fractionSize` converts it to a number
19825 fractionSize = (isUndefined(fractionSize)) ? Math.min(Math.max(minFrac, fractionLen), maxFrac) : +fractionSize;
19826
19827 // The index of the digit to where rounding is to occur
19828 var roundAt = fractionSize + parsedNumber.i;
19829 var digit = digits[roundAt];
19830
19831 if (roundAt > 0) {
19832 // Drop fractional digits beyond `roundAt`
19833 digits.splice(Math.max(parsedNumber.i, roundAt));
19834
19835 // Set non-fractional digits beyond `roundAt` to 0
19836 for (var j = roundAt; j < digits.length; j++) {
19837 digits[j] = 0;
19838 }
19839 } else {
19840 // We rounded to zero so reset the parsedNumber
19841 fractionLen = Math.max(0, fractionLen);
19842 parsedNumber.i = 1;
19843 digits.length = Math.max(1, roundAt = fractionSize + 1);
19844 digits[0] = 0;
19845 for (var i = 1; i < roundAt; i++) digits[i] = 0;
19846 }
19847
19848 if (digit >= 5) {
19849 if (roundAt - 1 < 0) {
19850 for (var k = 0; k > roundAt; k--) {
19851 digits.unshift(0);
19852 parsedNumber.i++;
19853 }
19854 digits.unshift(1);
19855 parsedNumber.i++;
19856 } else {
19857 digits[roundAt - 1]++;
19858 }
19859 }
19860
19861 // Pad out with zeros to get the required fraction length
19862 for (; fractionLen < Math.max(0, fractionSize); fractionLen++) digits.push(0);
19863
19864
19865 // Do any carrying, e.g. a digit was rounded up to 10
19866 var carry = digits.reduceRight(function(carry, d, i, digits) {
19867 d = d + carry;
19868 digits[i] = d % 10;
19869 return Math.floor(d / 10);
19870 }, 0);
19871 if (carry) {
19872 digits.unshift(carry);
19873 parsedNumber.i++;
19874 }
19875}
19876
19877/**
19878 * Format a number into a string
19879 * @param {number} number The number to format
19880 * @param {{
19881 * minFrac, // the minimum number of digits required in the fraction part of the number
19882 * maxFrac, // the maximum number of digits required in the fraction part of the number
19883 * gSize, // number of digits in each group of separated digits
19884 * lgSize, // number of digits in the last group of digits before the decimal separator
19885 * negPre, // the string to go in front of a negative number (e.g. `-` or `(`))
19886 * posPre, // the string to go in front of a positive number
19887 * negSuf, // the string to go after a negative number (e.g. `)`)
19888 * posSuf // the string to go after a positive number
19889 * }} pattern
19890 * @param {string} groupSep The string to separate groups of number (e.g. `,`)
19891 * @param {string} decimalSep The string to act as the decimal separator (e.g. `.`)
19892 * @param {[type]} fractionSize The size of the fractional part of the number
19893 * @return {string} The number formatted as a string
19894 */
19895function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
19896
19897 if (!(isString(number) || isNumber(number)) || isNaN(number)) return '';
19898
19899 var isInfinity = !isFinite(number);
19900 var isZero = false;
19901 var numStr = Math.abs(number) + '',
19902 formattedText = '',
19903 parsedNumber;
19904
19905 if (isInfinity) {
19906 formattedText = '\u221e';
19907 } else {
19908 parsedNumber = parse(numStr);
19909
19910 roundNumber(parsedNumber, fractionSize, pattern.minFrac, pattern.maxFrac);
19911
19912 var digits = parsedNumber.d;
19913 var integerLen = parsedNumber.i;
19914 var exponent = parsedNumber.e;
19915 var decimals = [];
19916 isZero = digits.reduce(function(isZero, d) { return isZero && !d; }, true);
19917
19918 // pad zeros for small numbers
19919 while (integerLen < 0) {
19920 digits.unshift(0);
19921 integerLen++;
19922 }
19923
19924 // extract decimals digits
19925 if (integerLen > 0) {
19926 decimals = digits.splice(integerLen);
19927 } else {
19928 decimals = digits;
19929 digits = [0];
19930 }
19931
19932 // format the integer digits with grouping separators
19933 var groups = [];
19934 if (digits.length >= pattern.lgSize) {
19935 groups.unshift(digits.splice(-pattern.lgSize).join(''));
19936 }
19937 while (digits.length > pattern.gSize) {
19938 groups.unshift(digits.splice(-pattern.gSize).join(''));
19939 }
19940 if (digits.length) {
19941 groups.unshift(digits.join(''));
19942 }
19943 formattedText = groups.join(groupSep);
19944
19945 // append the decimal digits
19946 if (decimals.length) {
19947 formattedText += decimalSep + decimals.join('');
19948 }
19949
19950 if (exponent) {
19951 formattedText += 'e+' + exponent;
19952 }
19953 }
19954 if (number < 0 && !isZero) {
19955 return pattern.negPre + formattedText + pattern.negSuf;
19956 } else {
19957 return pattern.posPre + formattedText + pattern.posSuf;
19958 }
19959}
19960
19961function padNumber(num, digits, trim, negWrap) {
19962 var neg = '';
19963 if (num < 0 || (negWrap && num <= 0)) {
19964 if (negWrap) {
19965 num = -num + 1;
19966 } else {
19967 num = -num;
19968 neg = '-';
19969 }
19970 }
19971 num = '' + num;
19972 while (num.length < digits) num = ZERO_CHAR + num;
19973 if (trim) {
19974 num = num.substr(num.length - digits);
19975 }
19976 return neg + num;
19977}
19978
19979
19980function dateGetter(name, size, offset, trim, negWrap) {
19981 offset = offset || 0;
19982 return function(date) {
19983 var value = date['get' + name]();
19984 if (offset > 0 || value > -offset) {
19985 value += offset;
19986 }
19987 if (value === 0 && offset == -12) value = 12;
19988 return padNumber(value, size, trim, negWrap);
19989 };
19990}
19991
19992function dateStrGetter(name, shortForm, standAlone) {
19993 return function(date, formats) {
19994 var value = date['get' + name]();
19995 var propPrefix = (standAlone ? 'STANDALONE' : '') + (shortForm ? 'SHORT' : '');
19996 var get = uppercase(propPrefix + name);
19997
19998 return formats[get][value];
19999 };
20000}
20001
20002function timeZoneGetter(date, formats, offset) {
20003 var zone = -1 * offset;
20004 var paddedZone = (zone >= 0) ? "+" : "";
20005
20006 paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
20007 padNumber(Math.abs(zone % 60), 2);
20008
20009 return paddedZone;
20010}
20011
20012function getFirstThursdayOfYear(year) {
20013 // 0 = index of January
20014 var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
20015 // 4 = index of Thursday (+1 to account for 1st = 5)
20016 // 11 = index of *next* Thursday (+1 account for 1st = 12)
20017 return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
20018}
20019
20020function getThursdayThisWeek(datetime) {
20021 return new Date(datetime.getFullYear(), datetime.getMonth(),
20022 // 4 = index of Thursday
20023 datetime.getDate() + (4 - datetime.getDay()));
20024}
20025
20026function weekGetter(size) {
20027 return function(date) {
20028 var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
20029 thisThurs = getThursdayThisWeek(date);
20030
20031 var diff = +thisThurs - +firstThurs,
20032 result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
20033
20034 return padNumber(result, size);
20035 };
20036}
20037
20038function ampmGetter(date, formats) {
20039 return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
20040}
20041
20042function eraGetter(date, formats) {
20043 return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
20044}
20045
20046function longEraGetter(date, formats) {
20047 return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
20048}
20049
20050var DATE_FORMATS = {
20051 yyyy: dateGetter('FullYear', 4, 0, false, true),
20052 yy: dateGetter('FullYear', 2, 0, true, true),
20053 y: dateGetter('FullYear', 1, 0, false, true),
20054 MMMM: dateStrGetter('Month'),
20055 MMM: dateStrGetter('Month', true),
20056 MM: dateGetter('Month', 2, 1),
20057 M: dateGetter('Month', 1, 1),
20058 LLLL: dateStrGetter('Month', false, true),
20059 dd: dateGetter('Date', 2),
20060 d: dateGetter('Date', 1),
20061 HH: dateGetter('Hours', 2),
20062 H: dateGetter('Hours', 1),
20063 hh: dateGetter('Hours', 2, -12),
20064 h: dateGetter('Hours', 1, -12),
20065 mm: dateGetter('Minutes', 2),
20066 m: dateGetter('Minutes', 1),
20067 ss: dateGetter('Seconds', 2),
20068 s: dateGetter('Seconds', 1),
20069 // while ISO 8601 requires fractions to be prefixed with `.` or `,`
20070 // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
20071 sss: dateGetter('Milliseconds', 3),
20072 EEEE: dateStrGetter('Day'),
20073 EEE: dateStrGetter('Day', true),
20074 a: ampmGetter,
20075 Z: timeZoneGetter,
20076 ww: weekGetter(2),
20077 w: weekGetter(1),
20078 G: eraGetter,
20079 GG: eraGetter,
20080 GGG: eraGetter,
20081 GGGG: longEraGetter
20082};
20083
20084var DATE_FORMATS_SPLIT = /((?:[^yMLdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|L+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
20085 NUMBER_STRING = /^\-?\d+$/;
20086
20087/**
20088 * @ngdoc filter
20089 * @name date
20090 * @kind function
20091 *
20092 * @description
20093 * Formats `date` to a string based on the requested `format`.
20094 *
20095 * `format` string can be composed of the following elements:
20096 *
20097 * * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
20098 * * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
20099 * * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
20100 * * `'MMMM'`: Month in year (January-December)
20101 * * `'MMM'`: Month in year (Jan-Dec)
20102 * * `'MM'`: Month in year, padded (01-12)
20103 * * `'M'`: Month in year (1-12)
20104 * * `'LLLL'`: Stand-alone month in year (January-December)
20105 * * `'dd'`: Day in month, padded (01-31)
20106 * * `'d'`: Day in month (1-31)
20107 * * `'EEEE'`: Day in Week,(Sunday-Saturday)
20108 * * `'EEE'`: Day in Week, (Sun-Sat)
20109 * * `'HH'`: Hour in day, padded (00-23)
20110 * * `'H'`: Hour in day (0-23)
20111 * * `'hh'`: Hour in AM/PM, padded (01-12)
20112 * * `'h'`: Hour in AM/PM, (1-12)
20113 * * `'mm'`: Minute in hour, padded (00-59)
20114 * * `'m'`: Minute in hour (0-59)
20115 * * `'ss'`: Second in minute, padded (00-59)
20116 * * `'s'`: Second in minute (0-59)
20117 * * `'sss'`: Millisecond in second, padded (000-999)
20118 * * `'a'`: AM/PM marker
20119 * * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
20120 * * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
20121 * * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
20122 * * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
20123 * * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
20124 *
20125 * `format` string can also be one of the following predefined
20126 * {@link guide/i18n localizable formats}:
20127 *
20128 * * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
20129 * (e.g. Sep 3, 2010 12:05:08 PM)
20130 * * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US locale (e.g. 9/3/10 12:05 PM)
20131 * * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US locale
20132 * (e.g. Friday, September 3, 2010)
20133 * * `'longDate'`: equivalent to `'MMMM d, y'` for en_US locale (e.g. September 3, 2010)
20134 * * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US locale (e.g. Sep 3, 2010)
20135 * * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
20136 * * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
20137 * * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
20138 *
20139 * `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
20140 * `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
20141 * (e.g. `"h 'o''clock'"`).
20142 *
20143 * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
20144 * number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
20145 * shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
20146 * specified in the string input, the time is considered to be in the local timezone.
20147 * @param {string=} format Formatting rules (see Description). If not specified,
20148 * `mediumDate` is used.
20149 * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
20150 * continental US time zone abbreviations, but for general use, use a time zone offset, for
20151 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
20152 * If not specified, the timezone of the browser will be used.
20153 * @returns {string} Formatted string or the input if input is not recognized as date/millis.
20154 *
20155 * @example
20156 <example>
20157 <file name="index.html">
20158 <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
20159 <span>{{1288323623006 | date:'medium'}}</span><br>
20160 <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
20161 <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
20162 <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
20163 <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
20164 <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
20165 <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
20166 </file>
20167 <file name="protractor.js" type="protractor">
20168 it('should format date', function() {
20169 expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
20170 toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
20171 expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
20172 toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
20173 expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
20174 toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
20175 expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
20176 toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
20177 });
20178 </file>
20179 </example>
20180 */
20181dateFilter.$inject = ['$locale'];
20182function dateFilter($locale) {
20183
20184
20185 var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
20186 // 1 2 3 4 5 6 7 8 9 10 11
20187 function jsonStringToDate(string) {
20188 var match;
20189 if (match = string.match(R_ISO8601_STR)) {
20190 var date = new Date(0),
20191 tzHour = 0,
20192 tzMin = 0,
20193 dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
20194 timeSetter = match[8] ? date.setUTCHours : date.setHours;
20195
20196 if (match[9]) {
20197 tzHour = toInt(match[9] + match[10]);
20198 tzMin = toInt(match[9] + match[11]);
20199 }
20200 dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
20201 var h = toInt(match[4] || 0) - tzHour;
20202 var m = toInt(match[5] || 0) - tzMin;
20203 var s = toInt(match[6] || 0);
20204 var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
20205 timeSetter.call(date, h, m, s, ms);
20206 return date;
20207 }
20208 return string;
20209 }
20210
20211
20212 return function(date, format, timezone) {
20213 var text = '',
20214 parts = [],
20215 fn, match;
20216
20217 format = format || 'mediumDate';
20218 format = $locale.DATETIME_FORMATS[format] || format;
20219 if (isString(date)) {
20220 date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
20221 }
20222
20223 if (isNumber(date)) {
20224 date = new Date(date);
20225 }
20226
20227 if (!isDate(date) || !isFinite(date.getTime())) {
20228 return date;
20229 }
20230
20231 while (format) {
20232 match = DATE_FORMATS_SPLIT.exec(format);
20233 if (match) {
20234 parts = concat(parts, match, 1);
20235 format = parts.pop();
20236 } else {
20237 parts.push(format);
20238 format = null;
20239 }
20240 }
20241
20242 var dateTimezoneOffset = date.getTimezoneOffset();
20243 if (timezone) {
20244 dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
20245 date = convertTimezoneToLocal(date, timezone, true);
20246 }
20247 forEach(parts, function(value) {
20248 fn = DATE_FORMATS[value];
20249 text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
20250 : value === "''" ? "'" : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
20251 });
20252
20253 return text;
20254 };
20255}
20256
20257
20258/**
20259 * @ngdoc filter
20260 * @name json
20261 * @kind function
20262 *
20263 * @description
20264 * Allows you to convert a JavaScript object into JSON string.
20265 *
20266 * This filter is mostly useful for debugging. When using the double curly {{value}} notation
20267 * the binding is automatically converted to JSON.
20268 *
20269 * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
20270 * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
20271 * @returns {string} JSON string.
20272 *
20273 *
20274 * @example
20275 <example>
20276 <file name="index.html">
20277 <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
20278 <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
20279 </file>
20280 <file name="protractor.js" type="protractor">
20281 it('should jsonify filtered objects', function() {
20282 expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
20283 expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n "name": ?"value"\n}/);
20284 });
20285 </file>
20286 </example>
20287 *
20288 */
20289function jsonFilter() {
20290 return function(object, spacing) {
20291 if (isUndefined(spacing)) {
20292 spacing = 2;
20293 }
20294 return toJson(object, spacing);
20295 };
20296}
20297
20298
20299/**
20300 * @ngdoc filter
20301 * @name lowercase
20302 * @kind function
20303 * @description
20304 * Converts string to lowercase.
20305 * @see angular.lowercase
20306 */
20307var lowercaseFilter = valueFn(lowercase);
20308
20309
20310/**
20311 * @ngdoc filter
20312 * @name uppercase
20313 * @kind function
20314 * @description
20315 * Converts string to uppercase.
20316 * @see angular.uppercase
20317 */
20318var uppercaseFilter = valueFn(uppercase);
20319
20320/**
20321 * @ngdoc filter
20322 * @name limitTo
20323 * @kind function
20324 *
20325 * @description
20326 * Creates a new array or string containing only a specified number of elements. The elements
20327 * are taken from either the beginning or the end of the source array, string or number, as specified by
20328 * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
20329 * converted to a string.
20330 *
20331 * @param {Array|string|number} input Source array, string or number to be limited.
20332 * @param {string|number} limit The length of the returned array or string. If the `limit` number
20333 * is positive, `limit` number of items from the beginning of the source array/string are copied.
20334 * If the number is negative, `limit` number of items from the end of the source array/string
20335 * are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
20336 * the input will be returned unchanged.
20337 * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
20338 * indicates an offset from the end of `input`. Defaults to `0`.
20339 * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
20340 * had less than `limit` elements.
20341 *
20342 * @example
20343 <example module="limitToExample">
20344 <file name="index.html">
20345 <script>
20346 angular.module('limitToExample', [])
20347 .controller('ExampleController', ['$scope', function($scope) {
20348 $scope.numbers = [1,2,3,4,5,6,7,8,9];
20349 $scope.letters = "abcdefghi";
20350 $scope.longNumber = 2345432342;
20351 $scope.numLimit = 3;
20352 $scope.letterLimit = 3;
20353 $scope.longNumberLimit = 3;
20354 }]);
20355 </script>
20356 <div ng-controller="ExampleController">
20357 <label>
20358 Limit {{numbers}} to:
20359 <input type="number" step="1" ng-model="numLimit">
20360 </label>
20361 <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
20362 <label>
20363 Limit {{letters}} to:
20364 <input type="number" step="1" ng-model="letterLimit">
20365 </label>
20366 <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
20367 <label>
20368 Limit {{longNumber}} to:
20369 <input type="number" step="1" ng-model="longNumberLimit">
20370 </label>
20371 <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
20372 </div>
20373 </file>
20374 <file name="protractor.js" type="protractor">
20375 var numLimitInput = element(by.model('numLimit'));
20376 var letterLimitInput = element(by.model('letterLimit'));
20377 var longNumberLimitInput = element(by.model('longNumberLimit'));
20378 var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
20379 var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
20380 var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
20381
20382 it('should limit the number array to first three items', function() {
20383 expect(numLimitInput.getAttribute('value')).toBe('3');
20384 expect(letterLimitInput.getAttribute('value')).toBe('3');
20385 expect(longNumberLimitInput.getAttribute('value')).toBe('3');
20386 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
20387 expect(limitedLetters.getText()).toEqual('Output letters: abc');
20388 expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
20389 });
20390
20391 // There is a bug in safari and protractor that doesn't like the minus key
20392 // it('should update the output when -3 is entered', function() {
20393 // numLimitInput.clear();
20394 // numLimitInput.sendKeys('-3');
20395 // letterLimitInput.clear();
20396 // letterLimitInput.sendKeys('-3');
20397 // longNumberLimitInput.clear();
20398 // longNumberLimitInput.sendKeys('-3');
20399 // expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
20400 // expect(limitedLetters.getText()).toEqual('Output letters: ghi');
20401 // expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
20402 // });
20403
20404 it('should not exceed the maximum size of input array', function() {
20405 numLimitInput.clear();
20406 numLimitInput.sendKeys('100');
20407 letterLimitInput.clear();
20408 letterLimitInput.sendKeys('100');
20409 longNumberLimitInput.clear();
20410 longNumberLimitInput.sendKeys('100');
20411 expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
20412 expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
20413 expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
20414 });
20415 </file>
20416 </example>
20417*/
20418function limitToFilter() {
20419 return function(input, limit, begin) {
20420 if (Math.abs(Number(limit)) === Infinity) {
20421 limit = Number(limit);
20422 } else {
20423 limit = toInt(limit);
20424 }
20425 if (isNaN(limit)) return input;
20426
20427 if (isNumber(input)) input = input.toString();
20428 if (!isArray(input) && !isString(input)) return input;
20429
20430 begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
20431 begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
20432
20433 if (limit >= 0) {
20434 return input.slice(begin, begin + limit);
20435 } else {
20436 if (begin === 0) {
20437 return input.slice(limit, input.length);
20438 } else {
20439 return input.slice(Math.max(0, begin + limit), begin);
20440 }
20441 }
20442 };
20443}
20444
20445/**
20446 * @ngdoc filter
20447 * @name orderBy
20448 * @kind function
20449 *
20450 * @description
20451 * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
20452 * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
20453 * as expected, make sure they are actually being saved as numbers and not strings.
20454 * Array-like values (e.g. NodeLists, jQuery objects, TypedArrays, Strings, etc) are also supported.
20455 *
20456 * @param {Array} array The array (or array-like object) to sort.
20457 * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
20458 * used by the comparator to determine the order of elements.
20459 *
20460 * Can be one of:
20461 *
20462 * - `function`: Getter function. The result of this function will be sorted using the
20463 * `<`, `===`, `>` operator.
20464 * - `string`: An Angular expression. The result of this expression is used to compare elements
20465 * (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
20466 * 3 first characters of a property called `name`). The result of a constant expression
20467 * is interpreted as a property name to be used in comparisons (for example `"special name"`
20468 * to sort object by the value of their `special name` property). An expression can be
20469 * optionally prefixed with `+` or `-` to control ascending or descending sort order
20470 * (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
20471 * element itself is used to compare where sorting.
20472 * - `Array`: An array of function or string predicates. The first predicate in the array
20473 * is used for sorting, but when two items are equivalent, the next predicate is used.
20474 *
20475 * If the predicate is missing or empty then it defaults to `'+'`.
20476 *
20477 * @param {boolean=} reverse Reverse the order of the array.
20478 * @returns {Array} Sorted copy of the source array.
20479 *
20480 *
20481 * @example
20482 * The example below demonstrates a simple ngRepeat, where the data is sorted
20483 * by age in descending order (predicate is set to `'-age'`).
20484 * `reverse` is not set, which means it defaults to `false`.
20485 <example module="orderByExample">
20486 <file name="index.html">
20487 <div ng-controller="ExampleController">
20488 <table class="friend">
20489 <tr>
20490 <th>Name</th>
20491 <th>Phone Number</th>
20492 <th>Age</th>
20493 </tr>
20494 <tr ng-repeat="friend in friends | orderBy:'-age'">
20495 <td>{{friend.name}}</td>
20496 <td>{{friend.phone}}</td>
20497 <td>{{friend.age}}</td>
20498 </tr>
20499 </table>
20500 </div>
20501 </file>
20502 <file name="script.js">
20503 angular.module('orderByExample', [])
20504 .controller('ExampleController', ['$scope', function($scope) {
20505 $scope.friends =
20506 [{name:'John', phone:'555-1212', age:10},
20507 {name:'Mary', phone:'555-9876', age:19},
20508 {name:'Mike', phone:'555-4321', age:21},
20509 {name:'Adam', phone:'555-5678', age:35},
20510 {name:'Julie', phone:'555-8765', age:29}];
20511 }]);
20512 </file>
20513 </example>
20514 *
20515 * The predicate and reverse parameters can be controlled dynamically through scope properties,
20516 * as shown in the next example.
20517 * @example
20518 <example module="orderByExample">
20519 <file name="index.html">
20520 <div ng-controller="ExampleController">
20521 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
20522 <hr/>
20523 <button ng-click="predicate=''">Set to unsorted</button>
20524 <table class="friend">
20525 <tr>
20526 <th>
20527 <button ng-click="order('name')">Name</button>
20528 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20529 </th>
20530 <th>
20531 <button ng-click="order('phone')">Phone Number</button>
20532 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20533 </th>
20534 <th>
20535 <button ng-click="order('age')">Age</button>
20536 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20537 </th>
20538 </tr>
20539 <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
20540 <td>{{friend.name}}</td>
20541 <td>{{friend.phone}}</td>
20542 <td>{{friend.age}}</td>
20543 </tr>
20544 </table>
20545 </div>
20546 </file>
20547 <file name="script.js">
20548 angular.module('orderByExample', [])
20549 .controller('ExampleController', ['$scope', function($scope) {
20550 $scope.friends =
20551 [{name:'John', phone:'555-1212', age:10},
20552 {name:'Mary', phone:'555-9876', age:19},
20553 {name:'Mike', phone:'555-4321', age:21},
20554 {name:'Adam', phone:'555-5678', age:35},
20555 {name:'Julie', phone:'555-8765', age:29}];
20556 $scope.predicate = 'age';
20557 $scope.reverse = true;
20558 $scope.order = function(predicate) {
20559 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20560 $scope.predicate = predicate;
20561 };
20562 }]);
20563 </file>
20564 <file name="style.css">
20565 .sortorder:after {
20566 content: '\25b2';
20567 }
20568 .sortorder.reverse:after {
20569 content: '\25bc';
20570 }
20571 </file>
20572 </example>
20573 *
20574 * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
20575 * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
20576 * desired parameters.
20577 *
20578 * Example:
20579 *
20580 * @example
20581 <example module="orderByExample">
20582 <file name="index.html">
20583 <div ng-controller="ExampleController">
20584 <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
20585 <table class="friend">
20586 <tr>
20587 <th>
20588 <button ng-click="order('name')">Name</button>
20589 <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
20590 </th>
20591 <th>
20592 <button ng-click="order('phone')">Phone Number</button>
20593 <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
20594 </th>
20595 <th>
20596 <button ng-click="order('age')">Age</button>
20597 <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
20598 </th>
20599 </tr>
20600 <tr ng-repeat="friend in friends">
20601 <td>{{friend.name}}</td>
20602 <td>{{friend.phone}}</td>
20603 <td>{{friend.age}}</td>
20604 </tr>
20605 </table>
20606 </div>
20607 </file>
20608
20609 <file name="script.js">
20610 angular.module('orderByExample', [])
20611 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
20612 var orderBy = $filter('orderBy');
20613 $scope.friends = [
20614 { name: 'John', phone: '555-1212', age: 10 },
20615 { name: 'Mary', phone: '555-9876', age: 19 },
20616 { name: 'Mike', phone: '555-4321', age: 21 },
20617 { name: 'Adam', phone: '555-5678', age: 35 },
20618 { name: 'Julie', phone: '555-8765', age: 29 }
20619 ];
20620 $scope.order = function(predicate) {
20621 $scope.predicate = predicate;
20622 $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
20623 $scope.friends = orderBy($scope.friends, predicate, $scope.reverse);
20624 };
20625 $scope.order('age', true);
20626 }]);
20627 </file>
20628
20629 <file name="style.css">
20630 .sortorder:after {
20631 content: '\25b2';
20632 }
20633 .sortorder.reverse:after {
20634 content: '\25bc';
20635 }
20636 </file>
20637</example>
20638 */
20639orderByFilter.$inject = ['$parse'];
20640function orderByFilter($parse) {
20641 return function(array, sortPredicate, reverseOrder) {
20642
20643 if (array == null) return array;
20644 if (!isArrayLike(array)) {
20645 throw minErr('orderBy')('notarray', 'Expected array but received: {0}', array);
20646 }
20647
20648 if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
20649 if (sortPredicate.length === 0) { sortPredicate = ['+']; }
20650
20651 var predicates = processPredicates(sortPredicate, reverseOrder);
20652 // Add a predicate at the end that evaluates to the element index. This makes the
20653 // sort stable as it works as a tie-breaker when all the input predicates cannot
20654 // distinguish between two elements.
20655 predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
20656
20657 // The next three lines are a version of a Swartzian Transform idiom from Perl
20658 // (sometimes called the Decorate-Sort-Undecorate idiom)
20659 // See https://en.wikipedia.org/wiki/Schwartzian_transform
20660 var compareValues = Array.prototype.map.call(array, getComparisonObject);
20661 compareValues.sort(doComparison);
20662 array = compareValues.map(function(item) { return item.value; });
20663
20664 return array;
20665
20666 function getComparisonObject(value, index) {
20667 return {
20668 value: value,
20669 predicateValues: predicates.map(function(predicate) {
20670 return getPredicateValue(predicate.get(value), index);
20671 })
20672 };
20673 }
20674
20675 function doComparison(v1, v2) {
20676 var result = 0;
20677 for (var index=0, length = predicates.length; index < length; ++index) {
20678 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
20679 if (result) break;
20680 }
20681 return result;
20682 }
20683 };
20684
20685 function processPredicates(sortPredicate, reverseOrder) {
20686 reverseOrder = reverseOrder ? -1 : 1;
20687 return sortPredicate.map(function(predicate) {
20688 var descending = 1, get = identity;
20689
20690 if (isFunction(predicate)) {
20691 get = predicate;
20692 } else if (isString(predicate)) {
20693 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
20694 descending = predicate.charAt(0) == '-' ? -1 : 1;
20695 predicate = predicate.substring(1);
20696 }
20697 if (predicate !== '') {
20698 get = $parse(predicate);
20699 if (get.constant) {
20700 var key = get();
20701 get = function(value) { return value[key]; };
20702 }
20703 }
20704 }
20705 return { get: get, descending: descending * reverseOrder };
20706 });
20707 }
20708
20709 function isPrimitive(value) {
20710 switch (typeof value) {
20711 case 'number': /* falls through */
20712 case 'boolean': /* falls through */
20713 case 'string':
20714 return true;
20715 default:
20716 return false;
20717 }
20718 }
20719
20720 function objectValue(value, index) {
20721 // If `valueOf` is a valid function use that
20722 if (typeof value.valueOf === 'function') {
20723 value = value.valueOf();
20724 if (isPrimitive(value)) return value;
20725 }
20726 // If `toString` is a valid function and not the one from `Object.prototype` use that
20727 if (hasCustomToString(value)) {
20728 value = value.toString();
20729 if (isPrimitive(value)) return value;
20730 }
20731 // We have a basic object so we use the position of the object in the collection
20732 return index;
20733 }
20734
20735 function getPredicateValue(value, index) {
20736 var type = typeof value;
20737 if (value === null) {
20738 type = 'string';
20739 value = 'null';
20740 } else if (type === 'string') {
20741 value = value.toLowerCase();
20742 } else if (type === 'object') {
20743 value = objectValue(value, index);
20744 }
20745 return { value: value, type: type };
20746 }
20747
20748 function compare(v1, v2) {
20749 var result = 0;
20750 if (v1.type === v2.type) {
20751 if (v1.value !== v2.value) {
20752 result = v1.value < v2.value ? -1 : 1;
20753 }
20754 } else {
20755 result = v1.type < v2.type ? -1 : 1;
20756 }
20757 return result;
20758 }
20759}
20760
20761function ngDirective(directive) {
20762 if (isFunction(directive)) {
20763 directive = {
20764 link: directive
20765 };
20766 }
20767 directive.restrict = directive.restrict || 'AC';
20768 return valueFn(directive);
20769}
20770
20771/**
20772 * @ngdoc directive
20773 * @name a
20774 * @restrict E
20775 *
20776 * @description
20777 * Modifies the default behavior of the html A tag so that the default action is prevented when
20778 * the href attribute is empty.
20779 *
20780 * This change permits the easy creation of action links with the `ngClick` directive
20781 * without changing the location or causing page reloads, e.g.:
20782 * `<a href="" ng-click="list.addItem()">Add Item</a>`
20783 */
20784var htmlAnchorDirective = valueFn({
20785 restrict: 'E',
20786 compile: function(element, attr) {
20787 if (!attr.href && !attr.xlinkHref) {
20788 return function(scope, element) {
20789 // If the linked element is not an anchor tag anymore, do nothing
20790 if (element[0].nodeName.toLowerCase() !== 'a') return;
20791
20792 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
20793 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
20794 'xlink:href' : 'href';
20795 element.on('click', function(event) {
20796 // if we have no href url, then don't navigate anywhere.
20797 if (!element.attr(href)) {
20798 event.preventDefault();
20799 }
20800 });
20801 };
20802 }
20803 }
20804});
20805
20806/**
20807 * @ngdoc directive
20808 * @name ngHref
20809 * @restrict A
20810 * @priority 99
20811 *
20812 * @description
20813 * Using Angular markup like `{{hash}}` in an href attribute will
20814 * make the link go to the wrong URL if the user clicks it before
20815 * Angular has a chance to replace the `{{hash}}` markup with its
20816 * value. Until Angular replaces the markup the link will be broken
20817 * and will most likely return a 404 error. The `ngHref` directive
20818 * solves this problem.
20819 *
20820 * The wrong way to write it:
20821 * ```html
20822 * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20823 * ```
20824 *
20825 * The correct way to write it:
20826 * ```html
20827 * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
20828 * ```
20829 *
20830 * @element A
20831 * @param {template} ngHref any string which can contain `{{}}` markup.
20832 *
20833 * @example
20834 * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
20835 * in links and their different behaviors:
20836 <example>
20837 <file name="index.html">
20838 <input ng-model="value" /><br />
20839 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
20840 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
20841 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
20842 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
20843 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
20844 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
20845 </file>
20846 <file name="protractor.js" type="protractor">
20847 it('should execute ng-click but not reload when href without value', function() {
20848 element(by.id('link-1')).click();
20849 expect(element(by.model('value')).getAttribute('value')).toEqual('1');
20850 expect(element(by.id('link-1')).getAttribute('href')).toBe('');
20851 });
20852
20853 it('should execute ng-click but not reload when href empty string', function() {
20854 element(by.id('link-2')).click();
20855 expect(element(by.model('value')).getAttribute('value')).toEqual('2');
20856 expect(element(by.id('link-2')).getAttribute('href')).toBe('');
20857 });
20858
20859 it('should execute ng-click and change url when ng-href specified', function() {
20860 expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
20861
20862 element(by.id('link-3')).click();
20863
20864 // At this point, we navigate away from an Angular page, so we need
20865 // to use browser.driver to get the base webdriver.
20866
20867 browser.wait(function() {
20868 return browser.driver.getCurrentUrl().then(function(url) {
20869 return url.match(/\/123$/);
20870 });
20871 }, 5000, 'page should navigate to /123');
20872 });
20873
20874 it('should execute ng-click but not reload when href empty string and name specified', function() {
20875 element(by.id('link-4')).click();
20876 expect(element(by.model('value')).getAttribute('value')).toEqual('4');
20877 expect(element(by.id('link-4')).getAttribute('href')).toBe('');
20878 });
20879
20880 it('should execute ng-click but not reload when no href but name specified', function() {
20881 element(by.id('link-5')).click();
20882 expect(element(by.model('value')).getAttribute('value')).toEqual('5');
20883 expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
20884 });
20885
20886 it('should only change url when only ng-href', function() {
20887 element(by.model('value')).clear();
20888 element(by.model('value')).sendKeys('6');
20889 expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
20890
20891 element(by.id('link-6')).click();
20892
20893 // At this point, we navigate away from an Angular page, so we need
20894 // to use browser.driver to get the base webdriver.
20895 browser.wait(function() {
20896 return browser.driver.getCurrentUrl().then(function(url) {
20897 return url.match(/\/6$/);
20898 });
20899 }, 5000, 'page should navigate to /6');
20900 });
20901 </file>
20902 </example>
20903 */
20904
20905/**
20906 * @ngdoc directive
20907 * @name ngSrc
20908 * @restrict A
20909 * @priority 99
20910 *
20911 * @description
20912 * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
20913 * work right: The browser will fetch from the URL with the literal
20914 * text `{{hash}}` until Angular replaces the expression inside
20915 * `{{hash}}`. The `ngSrc` directive solves this problem.
20916 *
20917 * The buggy way to write it:
20918 * ```html
20919 * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
20920 * ```
20921 *
20922 * The correct way to write it:
20923 * ```html
20924 * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
20925 * ```
20926 *
20927 * @element IMG
20928 * @param {template} ngSrc any string which can contain `{{}}` markup.
20929 */
20930
20931/**
20932 * @ngdoc directive
20933 * @name ngSrcset
20934 * @restrict A
20935 * @priority 99
20936 *
20937 * @description
20938 * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
20939 * work right: The browser will fetch from the URL with the literal
20940 * text `{{hash}}` until Angular replaces the expression inside
20941 * `{{hash}}`. The `ngSrcset` directive solves this problem.
20942 *
20943 * The buggy way to write it:
20944 * ```html
20945 * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
20946 * ```
20947 *
20948 * The correct way to write it:
20949 * ```html
20950 * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
20951 * ```
20952 *
20953 * @element IMG
20954 * @param {template} ngSrcset any string which can contain `{{}}` markup.
20955 */
20956
20957/**
20958 * @ngdoc directive
20959 * @name ngDisabled
20960 * @restrict A
20961 * @priority 100
20962 *
20963 * @description
20964 *
20965 * This directive sets the `disabled` attribute on the element if the
20966 * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
20967 *
20968 * A special directive is necessary because we cannot use interpolation inside the `disabled`
20969 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
20970 *
20971 * @example
20972 <example>
20973 <file name="index.html">
20974 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
20975 <button ng-model="button" ng-disabled="checked">Button</button>
20976 </file>
20977 <file name="protractor.js" type="protractor">
20978 it('should toggle button', function() {
20979 expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
20980 element(by.model('checked')).click();
20981 expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
20982 });
20983 </file>
20984 </example>
20985 *
20986 * @element INPUT
20987 * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
20988 * then the `disabled` attribute will be set on the element
20989 */
20990
20991
20992/**
20993 * @ngdoc directive
20994 * @name ngChecked
20995 * @restrict A
20996 * @priority 100
20997 *
20998 * @description
20999 * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
21000 *
21001 * Note that this directive should not be used together with {@link ngModel `ngModel`},
21002 * as this can lead to unexpected behavior.
21003 *
21004 * A special directive is necessary because we cannot use interpolation inside the `checked`
21005 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21006 *
21007 * @example
21008 <example>
21009 <file name="index.html">
21010 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
21011 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
21012 </file>
21013 <file name="protractor.js" type="protractor">
21014 it('should check both checkBoxes', function() {
21015 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
21016 element(by.model('master')).click();
21017 expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
21018 });
21019 </file>
21020 </example>
21021 *
21022 * @element INPUT
21023 * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
21024 * then the `checked` attribute will be set on the element
21025 */
21026
21027
21028/**
21029 * @ngdoc directive
21030 * @name ngReadonly
21031 * @restrict A
21032 * @priority 100
21033 *
21034 * @description
21035 *
21036 * Sets the `readOnly` attribute on the element, if the expression inside `ngReadonly` is truthy.
21037 *
21038 * A special directive is necessary because we cannot use interpolation inside the `readOnly`
21039 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21040 *
21041 * @example
21042 <example>
21043 <file name="index.html">
21044 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
21045 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
21046 </file>
21047 <file name="protractor.js" type="protractor">
21048 it('should toggle readonly attr', function() {
21049 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
21050 element(by.model('checked')).click();
21051 expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
21052 });
21053 </file>
21054 </example>
21055 *
21056 * @element INPUT
21057 * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
21058 * then special attribute "readonly" will be set on the element
21059 */
21060
21061
21062/**
21063 * @ngdoc directive
21064 * @name ngSelected
21065 * @restrict A
21066 * @priority 100
21067 *
21068 * @description
21069 *
21070 * Sets the `selected` attribute on the element, if the expression inside `ngSelected` is truthy.
21071 *
21072 * A special directive is necessary because we cannot use interpolation inside the `selected`
21073 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21074 *
21075 * @example
21076 <example>
21077 <file name="index.html">
21078 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
21079 <select aria-label="ngSelected demo">
21080 <option>Hello!</option>
21081 <option id="greet" ng-selected="selected">Greetings!</option>
21082 </select>
21083 </file>
21084 <file name="protractor.js" type="protractor">
21085 it('should select Greetings!', function() {
21086 expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
21087 element(by.model('selected')).click();
21088 expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
21089 });
21090 </file>
21091 </example>
21092 *
21093 * @element OPTION
21094 * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
21095 * then special attribute "selected" will be set on the element
21096 */
21097
21098/**
21099 * @ngdoc directive
21100 * @name ngOpen
21101 * @restrict A
21102 * @priority 100
21103 *
21104 * @description
21105 *
21106 * Sets the `open` attribute on the element, if the expression inside `ngOpen` is truthy.
21107 *
21108 * A special directive is necessary because we cannot use interpolation inside the `open`
21109 * attribute. See the {@link guide/interpolation interpolation guide} for more info.
21110 *
21111 * @example
21112 <example>
21113 <file name="index.html">
21114 <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
21115 <details id="details" ng-open="open">
21116 <summary>Show/Hide me</summary>
21117 </details>
21118 </file>
21119 <file name="protractor.js" type="protractor">
21120 it('should toggle open', function() {
21121 expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
21122 element(by.model('open')).click();
21123 expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
21124 });
21125 </file>
21126 </example>
21127 *
21128 * @element DETAILS
21129 * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
21130 * then special attribute "open" will be set on the element
21131 */
21132
21133var ngAttributeAliasDirectives = {};
21134
21135// boolean attrs are evaluated
21136forEach(BOOLEAN_ATTR, function(propName, attrName) {
21137 // binding to multiple is not supported
21138 if (propName == "multiple") return;
21139
21140 function defaultLinkFn(scope, element, attr) {
21141 scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
21142 attr.$set(attrName, !!value);
21143 });
21144 }
21145
21146 var normalized = directiveNormalize('ng-' + attrName);
21147 var linkFn = defaultLinkFn;
21148
21149 if (propName === 'checked') {
21150 linkFn = function(scope, element, attr) {
21151 // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
21152 if (attr.ngModel !== attr[normalized]) {
21153 defaultLinkFn(scope, element, attr);
21154 }
21155 };
21156 }
21157
21158 ngAttributeAliasDirectives[normalized] = function() {
21159 return {
21160 restrict: 'A',
21161 priority: 100,
21162 link: linkFn
21163 };
21164 };
21165});
21166
21167// aliased input attrs are evaluated
21168forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
21169 ngAttributeAliasDirectives[ngAttr] = function() {
21170 return {
21171 priority: 100,
21172 link: function(scope, element, attr) {
21173 //special case ngPattern when a literal regular expression value
21174 //is used as the expression (this way we don't have to watch anything).
21175 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
21176 var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
21177 if (match) {
21178 attr.$set("ngPattern", new RegExp(match[1], match[2]));
21179 return;
21180 }
21181 }
21182
21183 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
21184 attr.$set(ngAttr, value);
21185 });
21186 }
21187 };
21188 };
21189});
21190
21191// ng-src, ng-srcset, ng-href are interpolated
21192forEach(['src', 'srcset', 'href'], function(attrName) {
21193 var normalized = directiveNormalize('ng-' + attrName);
21194 ngAttributeAliasDirectives[normalized] = function() {
21195 return {
21196 priority: 99, // it needs to run after the attributes are interpolated
21197 link: function(scope, element, attr) {
21198 var propName = attrName,
21199 name = attrName;
21200
21201 if (attrName === 'href' &&
21202 toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
21203 name = 'xlinkHref';
21204 attr.$attr[name] = 'xlink:href';
21205 propName = null;
21206 }
21207
21208 attr.$observe(normalized, function(value) {
21209 if (!value) {
21210 if (attrName === 'href') {
21211 attr.$set(name, null);
21212 }
21213 return;
21214 }
21215
21216 attr.$set(name, value);
21217
21218 // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
21219 // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
21220 // to set the property as well to achieve the desired effect.
21221 // we use attr[attrName] value since $set can sanitize the url.
21222 if (msie && propName) element.prop(propName, attr[name]);
21223 });
21224 }
21225 };
21226 };
21227});
21228
21229/* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
21230 */
21231var nullFormCtrl = {
21232 $addControl: noop,
21233 $$renameControl: nullFormRenameControl,
21234 $removeControl: noop,
21235 $setValidity: noop,
21236 $setDirty: noop,
21237 $setPristine: noop,
21238 $setSubmitted: noop
21239},
21240SUBMITTED_CLASS = 'ng-submitted';
21241
21242function nullFormRenameControl(control, name) {
21243 control.$name = name;
21244}
21245
21246/**
21247 * @ngdoc type
21248 * @name form.FormController
21249 *
21250 * @property {boolean} $pristine True if user has not interacted with the form yet.
21251 * @property {boolean} $dirty True if user has already interacted with the form.
21252 * @property {boolean} $valid True if all of the containing forms and controls are valid.
21253 * @property {boolean} $invalid True if at least one containing control or form is invalid.
21254 * @property {boolean} $pending True if at least one containing control or form is pending.
21255 * @property {boolean} $submitted True if user has submitted the form even if its invalid.
21256 *
21257 * @property {Object} $error Is an object hash, containing references to controls or
21258 * forms with failing validators, where:
21259 *
21260 * - keys are validation tokens (error names),
21261 * - values are arrays of controls or forms that have a failing validator for given error name.
21262 *
21263 * Built-in validation tokens:
21264 *
21265 * - `email`
21266 * - `max`
21267 * - `maxlength`
21268 * - `min`
21269 * - `minlength`
21270 * - `number`
21271 * - `pattern`
21272 * - `required`
21273 * - `url`
21274 * - `date`
21275 * - `datetimelocal`
21276 * - `time`
21277 * - `week`
21278 * - `month`
21279 *
21280 * @description
21281 * `FormController` keeps track of all its controls and nested forms as well as the state of them,
21282 * such as being valid/invalid or dirty/pristine.
21283 *
21284 * Each {@link ng.directive:form form} directive creates an instance
21285 * of `FormController`.
21286 *
21287 */
21288//asks for $scope to fool the BC controller module
21289FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
21290function FormController(element, attrs, $scope, $animate, $interpolate) {
21291 var form = this,
21292 controls = [];
21293
21294 // init state
21295 form.$error = {};
21296 form.$$success = {};
21297 form.$pending = undefined;
21298 form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
21299 form.$dirty = false;
21300 form.$pristine = true;
21301 form.$valid = true;
21302 form.$invalid = false;
21303 form.$submitted = false;
21304 form.$$parentForm = nullFormCtrl;
21305
21306 /**
21307 * @ngdoc method
21308 * @name form.FormController#$rollbackViewValue
21309 *
21310 * @description
21311 * Rollback all form controls pending updates to the `$modelValue`.
21312 *
21313 * Updates may be pending by a debounced event or because the input is waiting for a some future
21314 * event defined in `ng-model-options`. This method is typically needed by the reset button of
21315 * a form that uses `ng-model-options` to pend updates.
21316 */
21317 form.$rollbackViewValue = function() {
21318 forEach(controls, function(control) {
21319 control.$rollbackViewValue();
21320 });
21321 };
21322
21323 /**
21324 * @ngdoc method
21325 * @name form.FormController#$commitViewValue
21326 *
21327 * @description
21328 * Commit all form controls pending updates to the `$modelValue`.
21329 *
21330 * Updates may be pending by a debounced event or because the input is waiting for a some future
21331 * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
21332 * usually handles calling this in response to input events.
21333 */
21334 form.$commitViewValue = function() {
21335 forEach(controls, function(control) {
21336 control.$commitViewValue();
21337 });
21338 };
21339
21340 /**
21341 * @ngdoc method
21342 * @name form.FormController#$addControl
21343 * @param {object} control control object, either a {@link form.FormController} or an
21344 * {@link ngModel.NgModelController}
21345 *
21346 * @description
21347 * Register a control with the form. Input elements using ngModelController do this automatically
21348 * when they are linked.
21349 *
21350 * Note that the current state of the control will not be reflected on the new parent form. This
21351 * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
21352 * state.
21353 *
21354 * However, if the method is used programmatically, for example by adding dynamically created controls,
21355 * or controls that have been previously removed without destroying their corresponding DOM element,
21356 * it's the developers responsibility to make sure the current state propagates to the parent form.
21357 *
21358 * For example, if an input control is added that is already `$dirty` and has `$error` properties,
21359 * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
21360 */
21361 form.$addControl = function(control) {
21362 // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
21363 // and not added to the scope. Now we throw an error.
21364 assertNotHasOwnProperty(control.$name, 'input');
21365 controls.push(control);
21366
21367 if (control.$name) {
21368 form[control.$name] = control;
21369 }
21370
21371 control.$$parentForm = form;
21372 };
21373
21374 // Private API: rename a form control
21375 form.$$renameControl = function(control, newName) {
21376 var oldName = control.$name;
21377
21378 if (form[oldName] === control) {
21379 delete form[oldName];
21380 }
21381 form[newName] = control;
21382 control.$name = newName;
21383 };
21384
21385 /**
21386 * @ngdoc method
21387 * @name form.FormController#$removeControl
21388 * @param {object} control control object, either a {@link form.FormController} or an
21389 * {@link ngModel.NgModelController}
21390 *
21391 * @description
21392 * Deregister a control from the form.
21393 *
21394 * Input elements using ngModelController do this automatically when they are destroyed.
21395 *
21396 * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
21397 * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
21398 * different from case to case. For example, removing the only `$dirty` control from a form may or
21399 * may not mean that the form is still `$dirty`.
21400 */
21401 form.$removeControl = function(control) {
21402 if (control.$name && form[control.$name] === control) {
21403 delete form[control.$name];
21404 }
21405 forEach(form.$pending, function(value, name) {
21406 form.$setValidity(name, null, control);
21407 });
21408 forEach(form.$error, function(value, name) {
21409 form.$setValidity(name, null, control);
21410 });
21411 forEach(form.$$success, function(value, name) {
21412 form.$setValidity(name, null, control);
21413 });
21414
21415 arrayRemove(controls, control);
21416 control.$$parentForm = nullFormCtrl;
21417 };
21418
21419
21420 /**
21421 * @ngdoc method
21422 * @name form.FormController#$setValidity
21423 *
21424 * @description
21425 * Sets the validity of a form control.
21426 *
21427 * This method will also propagate to parent forms.
21428 */
21429 addSetValidityMethod({
21430 ctrl: this,
21431 $element: element,
21432 set: function(object, property, controller) {
21433 var list = object[property];
21434 if (!list) {
21435 object[property] = [controller];
21436 } else {
21437 var index = list.indexOf(controller);
21438 if (index === -1) {
21439 list.push(controller);
21440 }
21441 }
21442 },
21443 unset: function(object, property, controller) {
21444 var list = object[property];
21445 if (!list) {
21446 return;
21447 }
21448 arrayRemove(list, controller);
21449 if (list.length === 0) {
21450 delete object[property];
21451 }
21452 },
21453 $animate: $animate
21454 });
21455
21456 /**
21457 * @ngdoc method
21458 * @name form.FormController#$setDirty
21459 *
21460 * @description
21461 * Sets the form to a dirty state.
21462 *
21463 * This method can be called to add the 'ng-dirty' class and set the form to a dirty
21464 * state (ng-dirty class). This method will also propagate to parent forms.
21465 */
21466 form.$setDirty = function() {
21467 $animate.removeClass(element, PRISTINE_CLASS);
21468 $animate.addClass(element, DIRTY_CLASS);
21469 form.$dirty = true;
21470 form.$pristine = false;
21471 form.$$parentForm.$setDirty();
21472 };
21473
21474 /**
21475 * @ngdoc method
21476 * @name form.FormController#$setPristine
21477 *
21478 * @description
21479 * Sets the form to its pristine state.
21480 *
21481 * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
21482 * state (ng-pristine class). This method will also propagate to all the controls contained
21483 * in this form.
21484 *
21485 * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
21486 * saving or resetting it.
21487 */
21488 form.$setPristine = function() {
21489 $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
21490 form.$dirty = false;
21491 form.$pristine = true;
21492 form.$submitted = false;
21493 forEach(controls, function(control) {
21494 control.$setPristine();
21495 });
21496 };
21497
21498 /**
21499 * @ngdoc method
21500 * @name form.FormController#$setUntouched
21501 *
21502 * @description
21503 * Sets the form to its untouched state.
21504 *
21505 * This method can be called to remove the 'ng-touched' class and set the form controls to their
21506 * untouched state (ng-untouched class).
21507 *
21508 * Setting a form controls back to their untouched state is often useful when setting the form
21509 * back to its pristine state.
21510 */
21511 form.$setUntouched = function() {
21512 forEach(controls, function(control) {
21513 control.$setUntouched();
21514 });
21515 };
21516
21517 /**
21518 * @ngdoc method
21519 * @name form.FormController#$setSubmitted
21520 *
21521 * @description
21522 * Sets the form to its submitted state.
21523 */
21524 form.$setSubmitted = function() {
21525 $animate.addClass(element, SUBMITTED_CLASS);
21526 form.$submitted = true;
21527 form.$$parentForm.$setSubmitted();
21528 };
21529}
21530
21531/**
21532 * @ngdoc directive
21533 * @name ngForm
21534 * @restrict EAC
21535 *
21536 * @description
21537 * Nestable alias of {@link ng.directive:form `form`} directive. HTML
21538 * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
21539 * sub-group of controls needs to be determined.
21540 *
21541 * Note: the purpose of `ngForm` is to group controls,
21542 * but not to be a replacement for the `<form>` tag with all of its capabilities
21543 * (e.g. posting to the server, ...).
21544 *
21545 * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
21546 * related scope, under this name.
21547 *
21548 */
21549
21550 /**
21551 * @ngdoc directive
21552 * @name form
21553 * @restrict E
21554 *
21555 * @description
21556 * Directive that instantiates
21557 * {@link form.FormController FormController}.
21558 *
21559 * If the `name` attribute is specified, the form controller is published onto the current scope under
21560 * this name.
21561 *
21562 * # Alias: {@link ng.directive:ngForm `ngForm`}
21563 *
21564 * In Angular, forms can be nested. This means that the outer form is valid when all of the child
21565 * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
21566 * Angular provides the {@link ng.directive:ngForm `ngForm`} directive, which behaves identically to
21567 * `form` but can be nested. Nested forms can be useful, for example, if the validity of a sub-group
21568 * of controls needs to be determined.
21569 *
21570 * # CSS classes
21571 * - `ng-valid` is set if the form is valid.
21572 * - `ng-invalid` is set if the form is invalid.
21573 * - `ng-pending` is set if the form is pending.
21574 * - `ng-pristine` is set if the form is pristine.
21575 * - `ng-dirty` is set if the form is dirty.
21576 * - `ng-submitted` is set if the form was submitted.
21577 *
21578 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
21579 *
21580 *
21581 * # Submitting a form and preventing the default action
21582 *
21583 * Since the role of forms in client-side Angular applications is different than in classical
21584 * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
21585 * page reload that sends the data to the server. Instead some javascript logic should be triggered
21586 * to handle the form submission in an application-specific way.
21587 *
21588 * For this reason, Angular prevents the default action (form submission to the server) unless the
21589 * `<form>` element has an `action` attribute specified.
21590 *
21591 * You can use one of the following two ways to specify what javascript method should be called when
21592 * a form is submitted:
21593 *
21594 * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
21595 * - {@link ng.directive:ngClick ngClick} directive on the first
21596 * button or input field of type submit (input[type=submit])
21597 *
21598 * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
21599 * or {@link ng.directive:ngClick ngClick} directives.
21600 * This is because of the following form submission rules in the HTML specification:
21601 *
21602 * - If a form has only one input field then hitting enter in this field triggers form submit
21603 * (`ngSubmit`)
21604 * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
21605 * doesn't trigger submit
21606 * - if a form has one or more input fields and one or more buttons or input[type=submit] then
21607 * hitting enter in any of the input fields will trigger the click handler on the *first* button or
21608 * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
21609 *
21610 * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
21611 * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
21612 * to have access to the updated model.
21613 *
21614 * ## Animation Hooks
21615 *
21616 * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
21617 * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
21618 * other validations that are performed within the form. Animations in ngForm are similar to how
21619 * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
21620 * as JS animations.
21621 *
21622 * The following example shows a simple way to utilize CSS transitions to style a form element
21623 * that has been rendered as invalid after it has been validated:
21624 *
21625 * <pre>
21626 * //be sure to include ngAnimate as a module to hook into more
21627 * //advanced animations
21628 * .my-form {
21629 * transition:0.5s linear all;
21630 * background: white;
21631 * }
21632 * .my-form.ng-invalid {
21633 * background: red;
21634 * color:white;
21635 * }
21636 * </pre>
21637 *
21638 * @example
21639 <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
21640 <file name="index.html">
21641 <script>
21642 angular.module('formExample', [])
21643 .controller('FormController', ['$scope', function($scope) {
21644 $scope.userType = 'guest';
21645 }]);
21646 </script>
21647 <style>
21648 .my-form {
21649 transition:all linear 0.5s;
21650 background: transparent;
21651 }
21652 .my-form.ng-invalid {
21653 background: red;
21654 }
21655 </style>
21656 <form name="myForm" ng-controller="FormController" class="my-form">
21657 userType: <input name="input" ng-model="userType" required>
21658 <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
21659 <code>userType = {{userType}}</code><br>
21660 <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
21661 <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
21662 <code>myForm.$valid = {{myForm.$valid}}</code><br>
21663 <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
21664 </form>
21665 </file>
21666 <file name="protractor.js" type="protractor">
21667 it('should initialize to model', function() {
21668 var userType = element(by.binding('userType'));
21669 var valid = element(by.binding('myForm.input.$valid'));
21670
21671 expect(userType.getText()).toContain('guest');
21672 expect(valid.getText()).toContain('true');
21673 });
21674
21675 it('should be invalid if empty', function() {
21676 var userType = element(by.binding('userType'));
21677 var valid = element(by.binding('myForm.input.$valid'));
21678 var userInput = element(by.model('userType'));
21679
21680 userInput.clear();
21681 userInput.sendKeys('');
21682
21683 expect(userType.getText()).toEqual('userType =');
21684 expect(valid.getText()).toContain('false');
21685 });
21686 </file>
21687 </example>
21688 *
21689 * @param {string=} name Name of the form. If specified, the form controller will be published into
21690 * related scope, under this name.
21691 */
21692var formDirectiveFactory = function(isNgForm) {
21693 return ['$timeout', '$parse', function($timeout, $parse) {
21694 var formDirective = {
21695 name: 'form',
21696 restrict: isNgForm ? 'EAC' : 'E',
21697 require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
21698 controller: FormController,
21699 compile: function ngFormCompile(formElement, attr) {
21700 // Setup initial state of the control
21701 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
21702
21703 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
21704
21705 return {
21706 pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
21707 var controller = ctrls[0];
21708
21709 // if `action` attr is not present on the form, prevent the default action (submission)
21710 if (!('action' in attr)) {
21711 // we can't use jq events because if a form is destroyed during submission the default
21712 // action is not prevented. see #1238
21713 //
21714 // IE 9 is not affected because it doesn't fire a submit event and try to do a full
21715 // page reload if the form was destroyed by submission of the form via a click handler
21716 // on a button in the form. Looks like an IE9 specific bug.
21717 var handleFormSubmission = function(event) {
21718 scope.$apply(function() {
21719 controller.$commitViewValue();
21720 controller.$setSubmitted();
21721 });
21722
21723 event.preventDefault();
21724 };
21725
21726 addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
21727
21728 // unregister the preventDefault listener so that we don't not leak memory but in a
21729 // way that will achieve the prevention of the default action.
21730 formElement.on('$destroy', function() {
21731 $timeout(function() {
21732 removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
21733 }, 0, false);
21734 });
21735 }
21736
21737 var parentFormCtrl = ctrls[1] || controller.$$parentForm;
21738 parentFormCtrl.$addControl(controller);
21739
21740 var setter = nameAttr ? getSetter(controller.$name) : noop;
21741
21742 if (nameAttr) {
21743 setter(scope, controller);
21744 attr.$observe(nameAttr, function(newValue) {
21745 if (controller.$name === newValue) return;
21746 setter(scope, undefined);
21747 controller.$$parentForm.$$renameControl(controller, newValue);
21748 setter = getSetter(controller.$name);
21749 setter(scope, controller);
21750 });
21751 }
21752 formElement.on('$destroy', function() {
21753 controller.$$parentForm.$removeControl(controller);
21754 setter(scope, undefined);
21755 extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
21756 });
21757 }
21758 };
21759 }
21760 };
21761
21762 return formDirective;
21763
21764 function getSetter(expression) {
21765 if (expression === '') {
21766 //create an assignable expression, so forms with an empty name can be renamed later
21767 return $parse('this[""]').assign;
21768 }
21769 return $parse(expression).assign || noop;
21770 }
21771 }];
21772};
21773
21774var formDirective = formDirectiveFactory();
21775var ngFormDirective = formDirectiveFactory(true);
21776
21777/* global VALID_CLASS: false,
21778 INVALID_CLASS: false,
21779 PRISTINE_CLASS: false,
21780 DIRTY_CLASS: false,
21781 UNTOUCHED_CLASS: false,
21782 TOUCHED_CLASS: false,
21783 ngModelMinErr: false,
21784*/
21785
21786// Regex code was initially obtained from SO prior to modification: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
21787var 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)$/;
21788// See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
21789// Note: We are being more lenient, because browsers are too.
21790// 1. Scheme
21791// 2. Slashes
21792// 3. Username
21793// 4. Password
21794// 5. Hostname
21795// 6. Port
21796// 7. Path
21797// 8. Query
21798// 9. Fragment
21799// 1111111111111111 222 333333 44444 555555555555555555555555 666 77777777 8888888 999
21800var URL_REGEXP = /^[a-z][a-z\d.+-]*:\/*(?:[^:@]+(?::[^@]+)?@)?(?:[^\s:/?#]+|\[[a-f\d:]+\])(?::\d+)?(?:\/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$/i;
21801var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
21802var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
21803var DATE_REGEXP = /^(\d{4,})-(\d{2})-(\d{2})$/;
21804var DATETIMELOCAL_REGEXP = /^(\d{4,})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21805var WEEK_REGEXP = /^(\d{4,})-W(\d\d)$/;
21806var MONTH_REGEXP = /^(\d{4,})-(\d\d)$/;
21807var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
21808
21809var PARTIAL_VALIDATION_EVENTS = 'keydown wheel mousedown';
21810var PARTIAL_VALIDATION_TYPES = createMap();
21811forEach('date,datetime-local,month,time,week'.split(','), function(type) {
21812 PARTIAL_VALIDATION_TYPES[type] = true;
21813});
21814
21815var inputType = {
21816
21817 /**
21818 * @ngdoc input
21819 * @name input[text]
21820 *
21821 * @description
21822 * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
21823 *
21824 *
21825 * @param {string} ngModel Assignable angular expression to data-bind to.
21826 * @param {string=} name Property name of the form under which the control is published.
21827 * @param {string=} required Adds `required` validation error key if the value is not entered.
21828 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21829 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21830 * `required` when you want to data-bind to the `required` attribute.
21831 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21832 * minlength.
21833 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21834 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21835 * any length.
21836 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21837 * that contains the regular expression body that will be converted to a regular expression
21838 * as in the ngPattern directive.
21839 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
21840 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
21841 * If the expression evaluates to a RegExp object, then this is used directly.
21842 * If the expression evaluates to a string, then it will be converted to a RegExp
21843 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21844 * `new RegExp('^abc$')`.<br />
21845 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21846 * start at the index of the last search's match, thus not taking the whole input value into
21847 * account.
21848 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21849 * interaction with the input element.
21850 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
21851 * This parameter is ignored for input[type=password] controls, which will never trim the
21852 * input.
21853 *
21854 * @example
21855 <example name="text-input-directive" module="textInputExample">
21856 <file name="index.html">
21857 <script>
21858 angular.module('textInputExample', [])
21859 .controller('ExampleController', ['$scope', function($scope) {
21860 $scope.example = {
21861 text: 'guest',
21862 word: /^\s*\w*\s*$/
21863 };
21864 }]);
21865 </script>
21866 <form name="myForm" ng-controller="ExampleController">
21867 <label>Single word:
21868 <input type="text" name="input" ng-model="example.text"
21869 ng-pattern="example.word" required ng-trim="false">
21870 </label>
21871 <div role="alert">
21872 <span class="error" ng-show="myForm.input.$error.required">
21873 Required!</span>
21874 <span class="error" ng-show="myForm.input.$error.pattern">
21875 Single word only!</span>
21876 </div>
21877 <tt>text = {{example.text}}</tt><br/>
21878 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21879 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21880 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21881 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21882 </form>
21883 </file>
21884 <file name="protractor.js" type="protractor">
21885 var text = element(by.binding('example.text'));
21886 var valid = element(by.binding('myForm.input.$valid'));
21887 var input = element(by.model('example.text'));
21888
21889 it('should initialize to model', function() {
21890 expect(text.getText()).toContain('guest');
21891 expect(valid.getText()).toContain('true');
21892 });
21893
21894 it('should be invalid if empty', function() {
21895 input.clear();
21896 input.sendKeys('');
21897
21898 expect(text.getText()).toEqual('text =');
21899 expect(valid.getText()).toContain('false');
21900 });
21901
21902 it('should be invalid if multi word', function() {
21903 input.clear();
21904 input.sendKeys('hello world');
21905
21906 expect(valid.getText()).toContain('false');
21907 });
21908 </file>
21909 </example>
21910 */
21911 'text': textInputType,
21912
21913 /**
21914 * @ngdoc input
21915 * @name input[date]
21916 *
21917 * @description
21918 * Input with date validation and transformation. In browsers that do not yet support
21919 * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
21920 * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
21921 * modern browsers do not yet support this input type, it is important to provide cues to users on the
21922 * expected input format via a placeholder or label.
21923 *
21924 * The model must always be a Date object, otherwise Angular will throw an error.
21925 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21926 *
21927 * The timezone to be used to read/write the `Date` instance in the model can be defined using
21928 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21929 *
21930 * @param {string} ngModel Assignable angular expression to data-bind to.
21931 * @param {string=} name Property name of the form under which the control is published.
21932 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
21933 * valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21934 * (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
21935 * constraint validation.
21936 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
21937 * a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
21938 * (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
21939 * constraint validation.
21940 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
21941 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21942 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
21943 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21944 * @param {string=} required Sets `required` validation error key if the value is not entered.
21945 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21946 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21947 * `required` when you want to data-bind to the `required` attribute.
21948 * @param {string=} ngChange Angular expression to be executed when input changes due to user
21949 * interaction with the input element.
21950 *
21951 * @example
21952 <example name="date-input-directive" module="dateInputExample">
21953 <file name="index.html">
21954 <script>
21955 angular.module('dateInputExample', [])
21956 .controller('DateController', ['$scope', function($scope) {
21957 $scope.example = {
21958 value: new Date(2013, 9, 22)
21959 };
21960 }]);
21961 </script>
21962 <form name="myForm" ng-controller="DateController as dateCtrl">
21963 <label for="exampleInput">Pick a date in 2013:</label>
21964 <input type="date" id="exampleInput" name="input" ng-model="example.value"
21965 placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
21966 <div role="alert">
21967 <span class="error" ng-show="myForm.input.$error.required">
21968 Required!</span>
21969 <span class="error" ng-show="myForm.input.$error.date">
21970 Not a valid date!</span>
21971 </div>
21972 <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
21973 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21974 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21975 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21976 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21977 </form>
21978 </file>
21979 <file name="protractor.js" type="protractor">
21980 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
21981 var valid = element(by.binding('myForm.input.$valid'));
21982 var input = element(by.model('example.value'));
21983
21984 // currently protractor/webdriver does not support
21985 // sending keys to all known HTML5 input controls
21986 // for various browsers (see https://github.com/angular/protractor/issues/562).
21987 function setInput(val) {
21988 // set the value of the element and force validation.
21989 var scr = "var ipt = document.getElementById('exampleInput'); " +
21990 "ipt.value = '" + val + "';" +
21991 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21992 browser.executeScript(scr);
21993 }
21994
21995 it('should initialize to model', function() {
21996 expect(value.getText()).toContain('2013-10-22');
21997 expect(valid.getText()).toContain('myForm.input.$valid = true');
21998 });
21999
22000 it('should be invalid if empty', function() {
22001 setInput('');
22002 expect(value.getText()).toEqual('value =');
22003 expect(valid.getText()).toContain('myForm.input.$valid = false');
22004 });
22005
22006 it('should be invalid if over max', function() {
22007 setInput('2015-01-01');
22008 expect(value.getText()).toContain('');
22009 expect(valid.getText()).toContain('myForm.input.$valid = false');
22010 });
22011 </file>
22012 </example>
22013 */
22014 'date': createDateInputType('date', DATE_REGEXP,
22015 createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
22016 'yyyy-MM-dd'),
22017
22018 /**
22019 * @ngdoc input
22020 * @name input[datetime-local]
22021 *
22022 * @description
22023 * Input with datetime validation and transformation. In browsers that do not yet support
22024 * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22025 * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
22026 *
22027 * The model must always be a Date object, otherwise Angular will throw an error.
22028 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22029 *
22030 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22031 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22032 *
22033 * @param {string} ngModel Assignable angular expression to data-bind to.
22034 * @param {string=} name Property name of the form under which the control is published.
22035 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22036 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
22037 * inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
22038 * Note that `min` will also add native HTML5 constraint validation.
22039 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22040 * This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
22041 * inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
22042 * Note that `max` will also add native HTML5 constraint validation.
22043 * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
22044 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22045 * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
22046 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22047 * @param {string=} required Sets `required` validation error key if the value is not entered.
22048 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22049 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22050 * `required` when you want to data-bind to the `required` attribute.
22051 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22052 * interaction with the input element.
22053 *
22054 * @example
22055 <example name="datetimelocal-input-directive" module="dateExample">
22056 <file name="index.html">
22057 <script>
22058 angular.module('dateExample', [])
22059 .controller('DateController', ['$scope', function($scope) {
22060 $scope.example = {
22061 value: new Date(2010, 11, 28, 14, 57)
22062 };
22063 }]);
22064 </script>
22065 <form name="myForm" ng-controller="DateController as dateCtrl">
22066 <label for="exampleInput">Pick a date between in 2013:</label>
22067 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
22068 placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
22069 <div role="alert">
22070 <span class="error" ng-show="myForm.input.$error.required">
22071 Required!</span>
22072 <span class="error" ng-show="myForm.input.$error.datetimelocal">
22073 Not a valid date!</span>
22074 </div>
22075 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
22076 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22077 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22078 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22079 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22080 </form>
22081 </file>
22082 <file name="protractor.js" type="protractor">
22083 var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
22084 var valid = element(by.binding('myForm.input.$valid'));
22085 var input = element(by.model('example.value'));
22086
22087 // currently protractor/webdriver does not support
22088 // sending keys to all known HTML5 input controls
22089 // for various browsers (https://github.com/angular/protractor/issues/562).
22090 function setInput(val) {
22091 // set the value of the element and force validation.
22092 var scr = "var ipt = document.getElementById('exampleInput'); " +
22093 "ipt.value = '" + val + "';" +
22094 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22095 browser.executeScript(scr);
22096 }
22097
22098 it('should initialize to model', function() {
22099 expect(value.getText()).toContain('2010-12-28T14:57:00');
22100 expect(valid.getText()).toContain('myForm.input.$valid = true');
22101 });
22102
22103 it('should be invalid if empty', function() {
22104 setInput('');
22105 expect(value.getText()).toEqual('value =');
22106 expect(valid.getText()).toContain('myForm.input.$valid = false');
22107 });
22108
22109 it('should be invalid if over max', function() {
22110 setInput('2015-01-01T23:59:00');
22111 expect(value.getText()).toContain('');
22112 expect(valid.getText()).toContain('myForm.input.$valid = false');
22113 });
22114 </file>
22115 </example>
22116 */
22117 'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
22118 createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
22119 'yyyy-MM-ddTHH:mm:ss.sss'),
22120
22121 /**
22122 * @ngdoc input
22123 * @name input[time]
22124 *
22125 * @description
22126 * Input with time validation and transformation. In browsers that do not yet support
22127 * the HTML5 time input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22128 * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
22129 * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
22130 *
22131 * The model must always be a Date object, otherwise Angular will throw an error.
22132 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22133 *
22134 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22135 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22136 *
22137 * @param {string} ngModel Assignable angular expression to data-bind to.
22138 * @param {string=} name Property name of the form under which the control is published.
22139 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22140 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
22141 * attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
22142 * native HTML5 constraint validation.
22143 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22144 * This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
22145 * attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
22146 * native HTML5 constraint validation.
22147 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
22148 * `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22149 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
22150 * `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22151 * @param {string=} required Sets `required` validation error key if the value is not entered.
22152 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22153 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22154 * `required` when you want to data-bind to the `required` attribute.
22155 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22156 * interaction with the input element.
22157 *
22158 * @example
22159 <example name="time-input-directive" module="timeExample">
22160 <file name="index.html">
22161 <script>
22162 angular.module('timeExample', [])
22163 .controller('DateController', ['$scope', function($scope) {
22164 $scope.example = {
22165 value: new Date(1970, 0, 1, 14, 57, 0)
22166 };
22167 }]);
22168 </script>
22169 <form name="myForm" ng-controller="DateController as dateCtrl">
22170 <label for="exampleInput">Pick a time between 8am and 5pm:</label>
22171 <input type="time" id="exampleInput" name="input" ng-model="example.value"
22172 placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
22173 <div role="alert">
22174 <span class="error" ng-show="myForm.input.$error.required">
22175 Required!</span>
22176 <span class="error" ng-show="myForm.input.$error.time">
22177 Not a valid date!</span>
22178 </div>
22179 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
22180 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22181 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22182 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22183 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22184 </form>
22185 </file>
22186 <file name="protractor.js" type="protractor">
22187 var value = element(by.binding('example.value | date: "HH:mm:ss"'));
22188 var valid = element(by.binding('myForm.input.$valid'));
22189 var input = element(by.model('example.value'));
22190
22191 // currently protractor/webdriver does not support
22192 // sending keys to all known HTML5 input controls
22193 // for various browsers (https://github.com/angular/protractor/issues/562).
22194 function setInput(val) {
22195 // set the value of the element and force validation.
22196 var scr = "var ipt = document.getElementById('exampleInput'); " +
22197 "ipt.value = '" + val + "';" +
22198 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22199 browser.executeScript(scr);
22200 }
22201
22202 it('should initialize to model', function() {
22203 expect(value.getText()).toContain('14:57:00');
22204 expect(valid.getText()).toContain('myForm.input.$valid = true');
22205 });
22206
22207 it('should be invalid if empty', function() {
22208 setInput('');
22209 expect(value.getText()).toEqual('value =');
22210 expect(valid.getText()).toContain('myForm.input.$valid = false');
22211 });
22212
22213 it('should be invalid if over max', function() {
22214 setInput('23:59:00');
22215 expect(value.getText()).toContain('');
22216 expect(valid.getText()).toContain('myForm.input.$valid = false');
22217 });
22218 </file>
22219 </example>
22220 */
22221 'time': createDateInputType('time', TIME_REGEXP,
22222 createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
22223 'HH:mm:ss.sss'),
22224
22225 /**
22226 * @ngdoc input
22227 * @name input[week]
22228 *
22229 * @description
22230 * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
22231 * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22232 * week format (yyyy-W##), for example: `2013-W02`.
22233 *
22234 * The model must always be a Date object, otherwise Angular will throw an error.
22235 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22236 *
22237 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22238 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22239 *
22240 * @param {string} ngModel Assignable angular expression to data-bind to.
22241 * @param {string=} name Property name of the form under which the control is published.
22242 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22243 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
22244 * attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
22245 * native HTML5 constraint validation.
22246 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22247 * This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
22248 * attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
22249 * native HTML5 constraint validation.
22250 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
22251 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22252 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
22253 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22254 * @param {string=} required Sets `required` validation error key if the value is not entered.
22255 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22256 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22257 * `required` when you want to data-bind to the `required` attribute.
22258 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22259 * interaction with the input element.
22260 *
22261 * @example
22262 <example name="week-input-directive" module="weekExample">
22263 <file name="index.html">
22264 <script>
22265 angular.module('weekExample', [])
22266 .controller('DateController', ['$scope', function($scope) {
22267 $scope.example = {
22268 value: new Date(2013, 0, 3)
22269 };
22270 }]);
22271 </script>
22272 <form name="myForm" ng-controller="DateController as dateCtrl">
22273 <label>Pick a date between in 2013:
22274 <input id="exampleInput" type="week" name="input" ng-model="example.value"
22275 placeholder="YYYY-W##" min="2012-W32"
22276 max="2013-W52" required />
22277 </label>
22278 <div role="alert">
22279 <span class="error" ng-show="myForm.input.$error.required">
22280 Required!</span>
22281 <span class="error" ng-show="myForm.input.$error.week">
22282 Not a valid date!</span>
22283 </div>
22284 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
22285 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22286 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22287 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22288 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22289 </form>
22290 </file>
22291 <file name="protractor.js" type="protractor">
22292 var value = element(by.binding('example.value | date: "yyyy-Www"'));
22293 var valid = element(by.binding('myForm.input.$valid'));
22294 var input = element(by.model('example.value'));
22295
22296 // currently protractor/webdriver does not support
22297 // sending keys to all known HTML5 input controls
22298 // for various browsers (https://github.com/angular/protractor/issues/562).
22299 function setInput(val) {
22300 // set the value of the element and force validation.
22301 var scr = "var ipt = document.getElementById('exampleInput'); " +
22302 "ipt.value = '" + val + "';" +
22303 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22304 browser.executeScript(scr);
22305 }
22306
22307 it('should initialize to model', function() {
22308 expect(value.getText()).toContain('2013-W01');
22309 expect(valid.getText()).toContain('myForm.input.$valid = true');
22310 });
22311
22312 it('should be invalid if empty', function() {
22313 setInput('');
22314 expect(value.getText()).toEqual('value =');
22315 expect(valid.getText()).toContain('myForm.input.$valid = false');
22316 });
22317
22318 it('should be invalid if over max', function() {
22319 setInput('2015-W01');
22320 expect(value.getText()).toContain('');
22321 expect(valid.getText()).toContain('myForm.input.$valid = false');
22322 });
22323 </file>
22324 </example>
22325 */
22326 'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
22327
22328 /**
22329 * @ngdoc input
22330 * @name input[month]
22331 *
22332 * @description
22333 * Input with month validation and transformation. In browsers that do not yet support
22334 * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
22335 * month format (yyyy-MM), for example: `2009-01`.
22336 *
22337 * The model must always be a Date object, otherwise Angular will throw an error.
22338 * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
22339 * If the model is not set to the first of the month, the next view to model update will set it
22340 * to the first of the month.
22341 *
22342 * The timezone to be used to read/write the `Date` instance in the model can be defined using
22343 * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
22344 *
22345 * @param {string} ngModel Assignable angular expression to data-bind to.
22346 * @param {string=} name Property name of the form under which the control is published.
22347 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22348 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
22349 * attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
22350 * native HTML5 constraint validation.
22351 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22352 * This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
22353 * attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
22354 * native HTML5 constraint validation.
22355 * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
22356 * the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
22357 * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
22358 * the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
22359
22360 * @param {string=} required Sets `required` validation error key if the value is not entered.
22361 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22362 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22363 * `required` when you want to data-bind to the `required` attribute.
22364 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22365 * interaction with the input element.
22366 *
22367 * @example
22368 <example name="month-input-directive" module="monthExample">
22369 <file name="index.html">
22370 <script>
22371 angular.module('monthExample', [])
22372 .controller('DateController', ['$scope', function($scope) {
22373 $scope.example = {
22374 value: new Date(2013, 9, 1)
22375 };
22376 }]);
22377 </script>
22378 <form name="myForm" ng-controller="DateController as dateCtrl">
22379 <label for="exampleInput">Pick a month in 2013:</label>
22380 <input id="exampleInput" type="month" name="input" ng-model="example.value"
22381 placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
22382 <div role="alert">
22383 <span class="error" ng-show="myForm.input.$error.required">
22384 Required!</span>
22385 <span class="error" ng-show="myForm.input.$error.month">
22386 Not a valid month!</span>
22387 </div>
22388 <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
22389 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22390 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22391 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22392 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22393 </form>
22394 </file>
22395 <file name="protractor.js" type="protractor">
22396 var value = element(by.binding('example.value | date: "yyyy-MM"'));
22397 var valid = element(by.binding('myForm.input.$valid'));
22398 var input = element(by.model('example.value'));
22399
22400 // currently protractor/webdriver does not support
22401 // sending keys to all known HTML5 input controls
22402 // for various browsers (https://github.com/angular/protractor/issues/562).
22403 function setInput(val) {
22404 // set the value of the element and force validation.
22405 var scr = "var ipt = document.getElementById('exampleInput'); " +
22406 "ipt.value = '" + val + "';" +
22407 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
22408 browser.executeScript(scr);
22409 }
22410
22411 it('should initialize to model', function() {
22412 expect(value.getText()).toContain('2013-10');
22413 expect(valid.getText()).toContain('myForm.input.$valid = true');
22414 });
22415
22416 it('should be invalid if empty', function() {
22417 setInput('');
22418 expect(value.getText()).toEqual('value =');
22419 expect(valid.getText()).toContain('myForm.input.$valid = false');
22420 });
22421
22422 it('should be invalid if over max', function() {
22423 setInput('2015-01');
22424 expect(value.getText()).toContain('');
22425 expect(valid.getText()).toContain('myForm.input.$valid = false');
22426 });
22427 </file>
22428 </example>
22429 */
22430 'month': createDateInputType('month', MONTH_REGEXP,
22431 createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
22432 'yyyy-MM'),
22433
22434 /**
22435 * @ngdoc input
22436 * @name input[number]
22437 *
22438 * @description
22439 * Text input with number validation and transformation. Sets the `number` validation
22440 * error if not a valid number.
22441 *
22442 * <div class="alert alert-warning">
22443 * The model must always be of type `number` otherwise Angular will throw an error.
22444 * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
22445 * error docs for more information and an example of how to convert your model if necessary.
22446 * </div>
22447 *
22448 * ## Issues with HTML5 constraint validation
22449 *
22450 * In browsers that follow the
22451 * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
22452 * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
22453 * If a non-number is entered in the input, the browser will report the value as an empty string,
22454 * which means the view / model values in `ngModel` and subsequently the scope value
22455 * will also be an empty string.
22456 *
22457 *
22458 * @param {string} ngModel Assignable angular expression to data-bind to.
22459 * @param {string=} name Property name of the form under which the control is published.
22460 * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
22461 * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
22462 * @param {string=} required Sets `required` validation error key if the value is not entered.
22463 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22464 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22465 * `required` when you want to data-bind to the `required` attribute.
22466 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22467 * minlength.
22468 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22469 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22470 * any length.
22471 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22472 * that contains the regular expression body that will be converted to a regular expression
22473 * as in the ngPattern directive.
22474 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22475 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22476 * If the expression evaluates to a RegExp object, then this is used directly.
22477 * If the expression evaluates to a string, then it will be converted to a RegExp
22478 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22479 * `new RegExp('^abc$')`.<br />
22480 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22481 * start at the index of the last search's match, thus not taking the whole input value into
22482 * account.
22483 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22484 * interaction with the input element.
22485 *
22486 * @example
22487 <example name="number-input-directive" module="numberExample">
22488 <file name="index.html">
22489 <script>
22490 angular.module('numberExample', [])
22491 .controller('ExampleController', ['$scope', function($scope) {
22492 $scope.example = {
22493 value: 12
22494 };
22495 }]);
22496 </script>
22497 <form name="myForm" ng-controller="ExampleController">
22498 <label>Number:
22499 <input type="number" name="input" ng-model="example.value"
22500 min="0" max="99" required>
22501 </label>
22502 <div role="alert">
22503 <span class="error" ng-show="myForm.input.$error.required">
22504 Required!</span>
22505 <span class="error" ng-show="myForm.input.$error.number">
22506 Not valid number!</span>
22507 </div>
22508 <tt>value = {{example.value}}</tt><br/>
22509 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22510 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22511 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22512 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22513 </form>
22514 </file>
22515 <file name="protractor.js" type="protractor">
22516 var value = element(by.binding('example.value'));
22517 var valid = element(by.binding('myForm.input.$valid'));
22518 var input = element(by.model('example.value'));
22519
22520 it('should initialize to model', function() {
22521 expect(value.getText()).toContain('12');
22522 expect(valid.getText()).toContain('true');
22523 });
22524
22525 it('should be invalid if empty', function() {
22526 input.clear();
22527 input.sendKeys('');
22528 expect(value.getText()).toEqual('value =');
22529 expect(valid.getText()).toContain('false');
22530 });
22531
22532 it('should be invalid if over max', function() {
22533 input.clear();
22534 input.sendKeys('123');
22535 expect(value.getText()).toEqual('value =');
22536 expect(valid.getText()).toContain('false');
22537 });
22538 </file>
22539 </example>
22540 */
22541 'number': numberInputType,
22542
22543
22544 /**
22545 * @ngdoc input
22546 * @name input[url]
22547 *
22548 * @description
22549 * Text input with URL validation. Sets the `url` validation error key if the content is not a
22550 * valid URL.
22551 *
22552 * <div class="alert alert-warning">
22553 * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
22554 * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
22555 * the built-in validators (see the {@link guide/forms Forms guide})
22556 * </div>
22557 *
22558 * @param {string} ngModel Assignable angular expression to data-bind to.
22559 * @param {string=} name Property name of the form under which the control is published.
22560 * @param {string=} required Sets `required` validation error key if the value is not entered.
22561 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22562 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22563 * `required` when you want to data-bind to the `required` attribute.
22564 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22565 * minlength.
22566 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22567 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22568 * any length.
22569 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22570 * that contains the regular expression body that will be converted to a regular expression
22571 * as in the ngPattern directive.
22572 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22573 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22574 * If the expression evaluates to a RegExp object, then this is used directly.
22575 * If the expression evaluates to a string, then it will be converted to a RegExp
22576 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22577 * `new RegExp('^abc$')`.<br />
22578 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22579 * start at the index of the last search's match, thus not taking the whole input value into
22580 * account.
22581 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22582 * interaction with the input element.
22583 *
22584 * @example
22585 <example name="url-input-directive" module="urlExample">
22586 <file name="index.html">
22587 <script>
22588 angular.module('urlExample', [])
22589 .controller('ExampleController', ['$scope', function($scope) {
22590 $scope.url = {
22591 text: 'http://google.com'
22592 };
22593 }]);
22594 </script>
22595 <form name="myForm" ng-controller="ExampleController">
22596 <label>URL:
22597 <input type="url" name="input" ng-model="url.text" required>
22598 <label>
22599 <div role="alert">
22600 <span class="error" ng-show="myForm.input.$error.required">
22601 Required!</span>
22602 <span class="error" ng-show="myForm.input.$error.url">
22603 Not valid url!</span>
22604 </div>
22605 <tt>text = {{url.text}}</tt><br/>
22606 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22607 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22608 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22609 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22610 <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
22611 </form>
22612 </file>
22613 <file name="protractor.js" type="protractor">
22614 var text = element(by.binding('url.text'));
22615 var valid = element(by.binding('myForm.input.$valid'));
22616 var input = element(by.model('url.text'));
22617
22618 it('should initialize to model', function() {
22619 expect(text.getText()).toContain('http://google.com');
22620 expect(valid.getText()).toContain('true');
22621 });
22622
22623 it('should be invalid if empty', function() {
22624 input.clear();
22625 input.sendKeys('');
22626
22627 expect(text.getText()).toEqual('text =');
22628 expect(valid.getText()).toContain('false');
22629 });
22630
22631 it('should be invalid if not url', function() {
22632 input.clear();
22633 input.sendKeys('box');
22634
22635 expect(valid.getText()).toContain('false');
22636 });
22637 </file>
22638 </example>
22639 */
22640 'url': urlInputType,
22641
22642
22643 /**
22644 * @ngdoc input
22645 * @name input[email]
22646 *
22647 * @description
22648 * Text input with email validation. Sets the `email` validation error key if not a valid email
22649 * address.
22650 *
22651 * <div class="alert alert-warning">
22652 * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
22653 * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
22654 * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
22655 * </div>
22656 *
22657 * @param {string} ngModel Assignable angular expression to data-bind to.
22658 * @param {string=} name Property name of the form under which the control is published.
22659 * @param {string=} required Sets `required` validation error key if the value is not entered.
22660 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22661 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22662 * `required` when you want to data-bind to the `required` attribute.
22663 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22664 * minlength.
22665 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22666 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
22667 * any length.
22668 * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
22669 * that contains the regular expression body that will be converted to a regular expression
22670 * as in the ngPattern directive.
22671 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
22672 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
22673 * If the expression evaluates to a RegExp object, then this is used directly.
22674 * If the expression evaluates to a string, then it will be converted to a RegExp
22675 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22676 * `new RegExp('^abc$')`.<br />
22677 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22678 * start at the index of the last search's match, thus not taking the whole input value into
22679 * account.
22680 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22681 * interaction with the input element.
22682 *
22683 * @example
22684 <example name="email-input-directive" module="emailExample">
22685 <file name="index.html">
22686 <script>
22687 angular.module('emailExample', [])
22688 .controller('ExampleController', ['$scope', function($scope) {
22689 $scope.email = {
22690 text: 'me@example.com'
22691 };
22692 }]);
22693 </script>
22694 <form name="myForm" ng-controller="ExampleController">
22695 <label>Email:
22696 <input type="email" name="input" ng-model="email.text" required>
22697 </label>
22698 <div role="alert">
22699 <span class="error" ng-show="myForm.input.$error.required">
22700 Required!</span>
22701 <span class="error" ng-show="myForm.input.$error.email">
22702 Not valid email!</span>
22703 </div>
22704 <tt>text = {{email.text}}</tt><br/>
22705 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
22706 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
22707 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22708 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22709 <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
22710 </form>
22711 </file>
22712 <file name="protractor.js" type="protractor">
22713 var text = element(by.binding('email.text'));
22714 var valid = element(by.binding('myForm.input.$valid'));
22715 var input = element(by.model('email.text'));
22716
22717 it('should initialize to model', function() {
22718 expect(text.getText()).toContain('me@example.com');
22719 expect(valid.getText()).toContain('true');
22720 });
22721
22722 it('should be invalid if empty', function() {
22723 input.clear();
22724 input.sendKeys('');
22725 expect(text.getText()).toEqual('text =');
22726 expect(valid.getText()).toContain('false');
22727 });
22728
22729 it('should be invalid if not email', function() {
22730 input.clear();
22731 input.sendKeys('xxx');
22732
22733 expect(valid.getText()).toContain('false');
22734 });
22735 </file>
22736 </example>
22737 */
22738 'email': emailInputType,
22739
22740
22741 /**
22742 * @ngdoc input
22743 * @name input[radio]
22744 *
22745 * @description
22746 * HTML radio button.
22747 *
22748 * @param {string} ngModel Assignable angular expression to data-bind to.
22749 * @param {string} value The value to which the `ngModel` expression should be set when selected.
22750 * Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
22751 * too. Use `ngValue` if you need complex models (`number`, `object`, ...).
22752 * @param {string=} name Property name of the form under which the control is published.
22753 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22754 * interaction with the input element.
22755 * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
22756 * is selected. Should be used instead of the `value` attribute if you need
22757 * a non-string `ngModel` (`boolean`, `array`, ...).
22758 *
22759 * @example
22760 <example name="radio-input-directive" module="radioExample">
22761 <file name="index.html">
22762 <script>
22763 angular.module('radioExample', [])
22764 .controller('ExampleController', ['$scope', function($scope) {
22765 $scope.color = {
22766 name: 'blue'
22767 };
22768 $scope.specialValue = {
22769 "id": "12345",
22770 "value": "green"
22771 };
22772 }]);
22773 </script>
22774 <form name="myForm" ng-controller="ExampleController">
22775 <label>
22776 <input type="radio" ng-model="color.name" value="red">
22777 Red
22778 </label><br/>
22779 <label>
22780 <input type="radio" ng-model="color.name" ng-value="specialValue">
22781 Green
22782 </label><br/>
22783 <label>
22784 <input type="radio" ng-model="color.name" value="blue">
22785 Blue
22786 </label><br/>
22787 <tt>color = {{color.name | json}}</tt><br/>
22788 </form>
22789 Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
22790 </file>
22791 <file name="protractor.js" type="protractor">
22792 it('should change state', function() {
22793 var color = element(by.binding('color.name'));
22794
22795 expect(color.getText()).toContain('blue');
22796
22797 element.all(by.model('color.name')).get(0).click();
22798
22799 expect(color.getText()).toContain('red');
22800 });
22801 </file>
22802 </example>
22803 */
22804 'radio': radioInputType,
22805
22806
22807 /**
22808 * @ngdoc input
22809 * @name input[checkbox]
22810 *
22811 * @description
22812 * HTML checkbox.
22813 *
22814 * @param {string} ngModel Assignable angular expression to data-bind to.
22815 * @param {string=} name Property name of the form under which the control is published.
22816 * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
22817 * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
22818 * @param {string=} ngChange Angular expression to be executed when input changes due to user
22819 * interaction with the input element.
22820 *
22821 * @example
22822 <example name="checkbox-input-directive" module="checkboxExample">
22823 <file name="index.html">
22824 <script>
22825 angular.module('checkboxExample', [])
22826 .controller('ExampleController', ['$scope', function($scope) {
22827 $scope.checkboxModel = {
22828 value1 : true,
22829 value2 : 'YES'
22830 };
22831 }]);
22832 </script>
22833 <form name="myForm" ng-controller="ExampleController">
22834 <label>Value1:
22835 <input type="checkbox" ng-model="checkboxModel.value1">
22836 </label><br/>
22837 <label>Value2:
22838 <input type="checkbox" ng-model="checkboxModel.value2"
22839 ng-true-value="'YES'" ng-false-value="'NO'">
22840 </label><br/>
22841 <tt>value1 = {{checkboxModel.value1}}</tt><br/>
22842 <tt>value2 = {{checkboxModel.value2}}</tt><br/>
22843 </form>
22844 </file>
22845 <file name="protractor.js" type="protractor">
22846 it('should change state', function() {
22847 var value1 = element(by.binding('checkboxModel.value1'));
22848 var value2 = element(by.binding('checkboxModel.value2'));
22849
22850 expect(value1.getText()).toContain('true');
22851 expect(value2.getText()).toContain('YES');
22852
22853 element(by.model('checkboxModel.value1')).click();
22854 element(by.model('checkboxModel.value2')).click();
22855
22856 expect(value1.getText()).toContain('false');
22857 expect(value2.getText()).toContain('NO');
22858 });
22859 </file>
22860 </example>
22861 */
22862 'checkbox': checkboxInputType,
22863
22864 'hidden': noop,
22865 'button': noop,
22866 'submit': noop,
22867 'reset': noop,
22868 'file': noop
22869};
22870
22871function stringBasedInputType(ctrl) {
22872 ctrl.$formatters.push(function(value) {
22873 return ctrl.$isEmpty(value) ? value : value.toString();
22874 });
22875}
22876
22877function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22878 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22879 stringBasedInputType(ctrl);
22880}
22881
22882function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22883 var type = lowercase(element[0].type);
22884
22885 // In composition mode, users are still inputing intermediate text buffer,
22886 // hold the listener until composition is done.
22887 // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
22888 if (!$sniffer.android) {
22889 var composing = false;
22890
22891 element.on('compositionstart', function() {
22892 composing = true;
22893 });
22894
22895 element.on('compositionend', function() {
22896 composing = false;
22897 listener();
22898 });
22899 }
22900
22901 var timeout;
22902
22903 var listener = function(ev) {
22904 if (timeout) {
22905 $browser.defer.cancel(timeout);
22906 timeout = null;
22907 }
22908 if (composing) return;
22909 var value = element.val(),
22910 event = ev && ev.type;
22911
22912 // By default we will trim the value
22913 // If the attribute ng-trim exists we will avoid trimming
22914 // If input type is 'password', the value is never trimmed
22915 if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
22916 value = trim(value);
22917 }
22918
22919 // If a control is suffering from bad input (due to native validators), browsers discard its
22920 // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
22921 // control's value is the same empty value twice in a row.
22922 if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
22923 ctrl.$setViewValue(value, event);
22924 }
22925 };
22926
22927 // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
22928 // input event on backspace, delete or cut
22929 if ($sniffer.hasEvent('input')) {
22930 element.on('input', listener);
22931 } else {
22932 var deferListener = function(ev, input, origValue) {
22933 if (!timeout) {
22934 timeout = $browser.defer(function() {
22935 timeout = null;
22936 if (!input || input.value !== origValue) {
22937 listener(ev);
22938 }
22939 });
22940 }
22941 };
22942
22943 element.on('keydown', function(event) {
22944 var key = event.keyCode;
22945
22946 // ignore
22947 // command modifiers arrows
22948 if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
22949
22950 deferListener(event, this, this.value);
22951 });
22952
22953 // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
22954 if ($sniffer.hasEvent('paste')) {
22955 element.on('paste cut', deferListener);
22956 }
22957 }
22958
22959 // if user paste into input using mouse on older browser
22960 // or form autocomplete on newer browser, we need "change" event to catch it
22961 element.on('change', listener);
22962
22963 // Some native input types (date-family) have the ability to change validity without
22964 // firing any input/change events.
22965 // For these event types, when native validators are present and the browser supports the type,
22966 // check for validity changes on various DOM events.
22967 if (PARTIAL_VALIDATION_TYPES[type] && ctrl.$$hasNativeValidators && type === attr.type) {
22968 element.on(PARTIAL_VALIDATION_EVENTS, function(ev) {
22969 if (!timeout) {
22970 var validity = this[VALIDITY_STATE_PROPERTY];
22971 var origBadInput = validity.badInput;
22972 var origTypeMismatch = validity.typeMismatch;
22973 timeout = $browser.defer(function() {
22974 timeout = null;
22975 if (validity.badInput !== origBadInput || validity.typeMismatch !== origTypeMismatch) {
22976 listener(ev);
22977 }
22978 });
22979 }
22980 });
22981 }
22982
22983 ctrl.$render = function() {
22984 // Workaround for Firefox validation #12102.
22985 var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
22986 if (element.val() !== value) {
22987 element.val(value);
22988 }
22989 };
22990}
22991
22992function weekParser(isoWeek, existingDate) {
22993 if (isDate(isoWeek)) {
22994 return isoWeek;
22995 }
22996
22997 if (isString(isoWeek)) {
22998 WEEK_REGEXP.lastIndex = 0;
22999 var parts = WEEK_REGEXP.exec(isoWeek);
23000 if (parts) {
23001 var year = +parts[1],
23002 week = +parts[2],
23003 hours = 0,
23004 minutes = 0,
23005 seconds = 0,
23006 milliseconds = 0,
23007 firstThurs = getFirstThursdayOfYear(year),
23008 addDays = (week - 1) * 7;
23009
23010 if (existingDate) {
23011 hours = existingDate.getHours();
23012 minutes = existingDate.getMinutes();
23013 seconds = existingDate.getSeconds();
23014 milliseconds = existingDate.getMilliseconds();
23015 }
23016
23017 return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
23018 }
23019 }
23020
23021 return NaN;
23022}
23023
23024function createDateParser(regexp, mapping) {
23025 return function(iso, date) {
23026 var parts, map;
23027
23028 if (isDate(iso)) {
23029 return iso;
23030 }
23031
23032 if (isString(iso)) {
23033 // When a date is JSON'ified to wraps itself inside of an extra
23034 // set of double quotes. This makes the date parsing code unable
23035 // to match the date string and parse it as a date.
23036 if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
23037 iso = iso.substring(1, iso.length - 1);
23038 }
23039 if (ISO_DATE_REGEXP.test(iso)) {
23040 return new Date(iso);
23041 }
23042 regexp.lastIndex = 0;
23043 parts = regexp.exec(iso);
23044
23045 if (parts) {
23046 parts.shift();
23047 if (date) {
23048 map = {
23049 yyyy: date.getFullYear(),
23050 MM: date.getMonth() + 1,
23051 dd: date.getDate(),
23052 HH: date.getHours(),
23053 mm: date.getMinutes(),
23054 ss: date.getSeconds(),
23055 sss: date.getMilliseconds() / 1000
23056 };
23057 } else {
23058 map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
23059 }
23060
23061 forEach(parts, function(part, index) {
23062 if (index < mapping.length) {
23063 map[mapping[index]] = +part;
23064 }
23065 });
23066 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
23067 }
23068 }
23069
23070 return NaN;
23071 };
23072}
23073
23074function createDateInputType(type, regexp, parseDate, format) {
23075 return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
23076 badInputChecker(scope, element, attr, ctrl);
23077 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23078 var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
23079 var previousDate;
23080
23081 ctrl.$$parserName = type;
23082 ctrl.$parsers.push(function(value) {
23083 if (ctrl.$isEmpty(value)) return null;
23084 if (regexp.test(value)) {
23085 // Note: We cannot read ctrl.$modelValue, as there might be a different
23086 // parser/formatter in the processing chain so that the model
23087 // contains some different data format!
23088 var parsedDate = parseDate(value, previousDate);
23089 if (timezone) {
23090 parsedDate = convertTimezoneToLocal(parsedDate, timezone);
23091 }
23092 return parsedDate;
23093 }
23094 return undefined;
23095 });
23096
23097 ctrl.$formatters.push(function(value) {
23098 if (value && !isDate(value)) {
23099 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
23100 }
23101 if (isValidDate(value)) {
23102 previousDate = value;
23103 if (previousDate && timezone) {
23104 previousDate = convertTimezoneToLocal(previousDate, timezone, true);
23105 }
23106 return $filter('date')(value, format, timezone);
23107 } else {
23108 previousDate = null;
23109 return '';
23110 }
23111 });
23112
23113 if (isDefined(attr.min) || attr.ngMin) {
23114 var minVal;
23115 ctrl.$validators.min = function(value) {
23116 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
23117 };
23118 attr.$observe('min', function(val) {
23119 minVal = parseObservedDateValue(val);
23120 ctrl.$validate();
23121 });
23122 }
23123
23124 if (isDefined(attr.max) || attr.ngMax) {
23125 var maxVal;
23126 ctrl.$validators.max = function(value) {
23127 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
23128 };
23129 attr.$observe('max', function(val) {
23130 maxVal = parseObservedDateValue(val);
23131 ctrl.$validate();
23132 });
23133 }
23134
23135 function isValidDate(value) {
23136 // Invalid Date: getTime() returns NaN
23137 return value && !(value.getTime && value.getTime() !== value.getTime());
23138 }
23139
23140 function parseObservedDateValue(val) {
23141 return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
23142 }
23143 };
23144}
23145
23146function badInputChecker(scope, element, attr, ctrl) {
23147 var node = element[0];
23148 var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
23149 if (nativeValidation) {
23150 ctrl.$parsers.push(function(value) {
23151 var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
23152 return validity.badInput || validity.typeMismatch ? undefined : value;
23153 });
23154 }
23155}
23156
23157function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23158 badInputChecker(scope, element, attr, ctrl);
23159 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23160
23161 ctrl.$$parserName = 'number';
23162 ctrl.$parsers.push(function(value) {
23163 if (ctrl.$isEmpty(value)) return null;
23164 if (NUMBER_REGEXP.test(value)) return parseFloat(value);
23165 return undefined;
23166 });
23167
23168 ctrl.$formatters.push(function(value) {
23169 if (!ctrl.$isEmpty(value)) {
23170 if (!isNumber(value)) {
23171 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
23172 }
23173 value = value.toString();
23174 }
23175 return value;
23176 });
23177
23178 if (isDefined(attr.min) || attr.ngMin) {
23179 var minVal;
23180 ctrl.$validators.min = function(value) {
23181 return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
23182 };
23183
23184 attr.$observe('min', function(val) {
23185 if (isDefined(val) && !isNumber(val)) {
23186 val = parseFloat(val, 10);
23187 }
23188 minVal = isNumber(val) && !isNaN(val) ? val : undefined;
23189 // TODO(matsko): implement validateLater to reduce number of validations
23190 ctrl.$validate();
23191 });
23192 }
23193
23194 if (isDefined(attr.max) || attr.ngMax) {
23195 var maxVal;
23196 ctrl.$validators.max = function(value) {
23197 return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
23198 };
23199
23200 attr.$observe('max', function(val) {
23201 if (isDefined(val) && !isNumber(val)) {
23202 val = parseFloat(val, 10);
23203 }
23204 maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
23205 // TODO(matsko): implement validateLater to reduce number of validations
23206 ctrl.$validate();
23207 });
23208 }
23209}
23210
23211function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23212 // Note: no badInputChecker here by purpose as `url` is only a validation
23213 // in browsers, i.e. we can always read out input.value even if it is not valid!
23214 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23215 stringBasedInputType(ctrl);
23216
23217 ctrl.$$parserName = 'url';
23218 ctrl.$validators.url = function(modelValue, viewValue) {
23219 var value = modelValue || viewValue;
23220 return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
23221 };
23222}
23223
23224function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
23225 // Note: no badInputChecker here by purpose as `url` is only a validation
23226 // in browsers, i.e. we can always read out input.value even if it is not valid!
23227 baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
23228 stringBasedInputType(ctrl);
23229
23230 ctrl.$$parserName = 'email';
23231 ctrl.$validators.email = function(modelValue, viewValue) {
23232 var value = modelValue || viewValue;
23233 return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
23234 };
23235}
23236
23237function radioInputType(scope, element, attr, ctrl) {
23238 // make the name unique, if not defined
23239 if (isUndefined(attr.name)) {
23240 element.attr('name', nextUid());
23241 }
23242
23243 var listener = function(ev) {
23244 if (element[0].checked) {
23245 ctrl.$setViewValue(attr.value, ev && ev.type);
23246 }
23247 };
23248
23249 element.on('click', listener);
23250
23251 ctrl.$render = function() {
23252 var value = attr.value;
23253 element[0].checked = (value == ctrl.$viewValue);
23254 };
23255
23256 attr.$observe('value', ctrl.$render);
23257}
23258
23259function parseConstantExpr($parse, context, name, expression, fallback) {
23260 var parseFn;
23261 if (isDefined(expression)) {
23262 parseFn = $parse(expression);
23263 if (!parseFn.constant) {
23264 throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
23265 '`{1}`.', name, expression);
23266 }
23267 return parseFn(context);
23268 }
23269 return fallback;
23270}
23271
23272function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
23273 var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
23274 var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
23275
23276 var listener = function(ev) {
23277 ctrl.$setViewValue(element[0].checked, ev && ev.type);
23278 };
23279
23280 element.on('click', listener);
23281
23282 ctrl.$render = function() {
23283 element[0].checked = ctrl.$viewValue;
23284 };
23285
23286 // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
23287 // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
23288 // it to a boolean.
23289 ctrl.$isEmpty = function(value) {
23290 return value === false;
23291 };
23292
23293 ctrl.$formatters.push(function(value) {
23294 return equals(value, trueValue);
23295 });
23296
23297 ctrl.$parsers.push(function(value) {
23298 return value ? trueValue : falseValue;
23299 });
23300}
23301
23302
23303/**
23304 * @ngdoc directive
23305 * @name textarea
23306 * @restrict E
23307 *
23308 * @description
23309 * HTML textarea element control with angular data-binding. The data-binding and validation
23310 * properties of this element are exactly the same as those of the
23311 * {@link ng.directive:input input element}.
23312 *
23313 * @param {string} ngModel Assignable angular expression to data-bind to.
23314 * @param {string=} name Property name of the form under which the control is published.
23315 * @param {string=} required Sets `required` validation error key if the value is not entered.
23316 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
23317 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
23318 * `required` when you want to data-bind to the `required` attribute.
23319 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23320 * minlength.
23321 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23322 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
23323 * length.
23324 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23325 * does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23326 * If the expression evaluates to a RegExp object, then this is used directly.
23327 * If the expression evaluates to a string, then it will be converted to a RegExp
23328 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23329 * `new RegExp('^abc$')`.<br />
23330 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23331 * start at the index of the last search's match, thus not taking the whole input value into
23332 * account.
23333 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23334 * interaction with the input element.
23335 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
23336 */
23337
23338
23339/**
23340 * @ngdoc directive
23341 * @name input
23342 * @restrict E
23343 *
23344 * @description
23345 * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
23346 * input state control, and validation.
23347 * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
23348 *
23349 * <div class="alert alert-warning">
23350 * **Note:** Not every feature offered is available for all input types.
23351 * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
23352 * </div>
23353 *
23354 * @param {string} ngModel Assignable angular expression to data-bind to.
23355 * @param {string=} name Property name of the form under which the control is published.
23356 * @param {string=} required Sets `required` validation error key if the value is not entered.
23357 * @param {boolean=} ngRequired Sets `required` attribute if set to true
23358 * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
23359 * minlength.
23360 * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
23361 * maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
23362 * length.
23363 * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel {@link ngModel.NgModelController#$viewValue $viewValue}
23364 * value does not match a RegExp found by evaluating the Angular expression given in the attribute value.
23365 * If the expression evaluates to a RegExp object, then this is used directly.
23366 * If the expression evaluates to a string, then it will be converted to a RegExp
23367 * after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
23368 * `new RegExp('^abc$')`.<br />
23369 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
23370 * start at the index of the last search's match, thus not taking the whole input value into
23371 * account.
23372 * @param {string=} ngChange Angular expression to be executed when input changes due to user
23373 * interaction with the input element.
23374 * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
23375 * This parameter is ignored for input[type=password] controls, which will never trim the
23376 * input.
23377 *
23378 * @example
23379 <example name="input-directive" module="inputExample">
23380 <file name="index.html">
23381 <script>
23382 angular.module('inputExample', [])
23383 .controller('ExampleController', ['$scope', function($scope) {
23384 $scope.user = {name: 'guest', last: 'visitor'};
23385 }]);
23386 </script>
23387 <div ng-controller="ExampleController">
23388 <form name="myForm">
23389 <label>
23390 User name:
23391 <input type="text" name="userName" ng-model="user.name" required>
23392 </label>
23393 <div role="alert">
23394 <span class="error" ng-show="myForm.userName.$error.required">
23395 Required!</span>
23396 </div>
23397 <label>
23398 Last name:
23399 <input type="text" name="lastName" ng-model="user.last"
23400 ng-minlength="3" ng-maxlength="10">
23401 </label>
23402 <div role="alert">
23403 <span class="error" ng-show="myForm.lastName.$error.minlength">
23404 Too short!</span>
23405 <span class="error" ng-show="myForm.lastName.$error.maxlength">
23406 Too long!</span>
23407 </div>
23408 </form>
23409 <hr>
23410 <tt>user = {{user}}</tt><br/>
23411 <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
23412 <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
23413 <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
23414 <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
23415 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
23416 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
23417 <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
23418 <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
23419 </div>
23420 </file>
23421 <file name="protractor.js" type="protractor">
23422 var user = element(by.exactBinding('user'));
23423 var userNameValid = element(by.binding('myForm.userName.$valid'));
23424 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
23425 var lastNameError = element(by.binding('myForm.lastName.$error'));
23426 var formValid = element(by.binding('myForm.$valid'));
23427 var userNameInput = element(by.model('user.name'));
23428 var userLastInput = element(by.model('user.last'));
23429
23430 it('should initialize to model', function() {
23431 expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
23432 expect(userNameValid.getText()).toContain('true');
23433 expect(formValid.getText()).toContain('true');
23434 });
23435
23436 it('should be invalid if empty when required', function() {
23437 userNameInput.clear();
23438 userNameInput.sendKeys('');
23439
23440 expect(user.getText()).toContain('{"last":"visitor"}');
23441 expect(userNameValid.getText()).toContain('false');
23442 expect(formValid.getText()).toContain('false');
23443 });
23444
23445 it('should be valid if empty when min length is set', function() {
23446 userLastInput.clear();
23447 userLastInput.sendKeys('');
23448
23449 expect(user.getText()).toContain('{"name":"guest","last":""}');
23450 expect(lastNameValid.getText()).toContain('true');
23451 expect(formValid.getText()).toContain('true');
23452 });
23453
23454 it('should be invalid if less than required min length', function() {
23455 userLastInput.clear();
23456 userLastInput.sendKeys('xx');
23457
23458 expect(user.getText()).toContain('{"name":"guest"}');
23459 expect(lastNameValid.getText()).toContain('false');
23460 expect(lastNameError.getText()).toContain('minlength');
23461 expect(formValid.getText()).toContain('false');
23462 });
23463
23464 it('should be invalid if longer than max length', function() {
23465 userLastInput.clear();
23466 userLastInput.sendKeys('some ridiculously long name');
23467
23468 expect(user.getText()).toContain('{"name":"guest"}');
23469 expect(lastNameValid.getText()).toContain('false');
23470 expect(lastNameError.getText()).toContain('maxlength');
23471 expect(formValid.getText()).toContain('false');
23472 });
23473 </file>
23474 </example>
23475 */
23476var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
23477 function($browser, $sniffer, $filter, $parse) {
23478 return {
23479 restrict: 'E',
23480 require: ['?ngModel'],
23481 link: {
23482 pre: function(scope, element, attr, ctrls) {
23483 if (ctrls[0]) {
23484 (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
23485 $browser, $filter, $parse);
23486 }
23487 }
23488 }
23489 };
23490}];
23491
23492
23493
23494var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
23495/**
23496 * @ngdoc directive
23497 * @name ngValue
23498 *
23499 * @description
23500 * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
23501 * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
23502 * the bound value.
23503 *
23504 * `ngValue` is useful when dynamically generating lists of radio buttons using
23505 * {@link ngRepeat `ngRepeat`}, as shown below.
23506 *
23507 * Likewise, `ngValue` can be used to generate `<option>` elements for
23508 * the {@link select `select`} element. In that case however, only strings are supported
23509 * for the `value `attribute, so the resulting `ngModel` will always be a string.
23510 * Support for `select` models with non-string values is available via `ngOptions`.
23511 *
23512 * @element input
23513 * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
23514 * of the `input` element
23515 *
23516 * @example
23517 <example name="ngValue-directive" module="valueExample">
23518 <file name="index.html">
23519 <script>
23520 angular.module('valueExample', [])
23521 .controller('ExampleController', ['$scope', function($scope) {
23522 $scope.names = ['pizza', 'unicorns', 'robots'];
23523 $scope.my = { favorite: 'unicorns' };
23524 }]);
23525 </script>
23526 <form ng-controller="ExampleController">
23527 <h2>Which is your favorite?</h2>
23528 <label ng-repeat="name in names" for="{{name}}">
23529 {{name}}
23530 <input type="radio"
23531 ng-model="my.favorite"
23532 ng-value="name"
23533 id="{{name}}"
23534 name="favorite">
23535 </label>
23536 <div>You chose {{my.favorite}}</div>
23537 </form>
23538 </file>
23539 <file name="protractor.js" type="protractor">
23540 var favorite = element(by.binding('my.favorite'));
23541
23542 it('should initialize to model', function() {
23543 expect(favorite.getText()).toContain('unicorns');
23544 });
23545 it('should bind the values to the inputs', function() {
23546 element.all(by.model('my.favorite')).get(0).click();
23547 expect(favorite.getText()).toContain('pizza');
23548 });
23549 </file>
23550 </example>
23551 */
23552var ngValueDirective = function() {
23553 return {
23554 restrict: 'A',
23555 priority: 100,
23556 compile: function(tpl, tplAttr) {
23557 if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
23558 return function ngValueConstantLink(scope, elm, attr) {
23559 attr.$set('value', scope.$eval(attr.ngValue));
23560 };
23561 } else {
23562 return function ngValueLink(scope, elm, attr) {
23563 scope.$watch(attr.ngValue, function valueWatchAction(value) {
23564 attr.$set('value', value);
23565 });
23566 };
23567 }
23568 }
23569 };
23570};
23571
23572/**
23573 * @ngdoc directive
23574 * @name ngBind
23575 * @restrict AC
23576 *
23577 * @description
23578 * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
23579 * with the value of a given expression, and to update the text content when the value of that
23580 * expression changes.
23581 *
23582 * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
23583 * `{{ expression }}` which is similar but less verbose.
23584 *
23585 * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
23586 * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
23587 * element attribute, it makes the bindings invisible to the user while the page is loading.
23588 *
23589 * An alternative solution to this problem would be using the
23590 * {@link ng.directive:ngCloak ngCloak} directive.
23591 *
23592 *
23593 * @element ANY
23594 * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
23595 *
23596 * @example
23597 * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
23598 <example module="bindExample">
23599 <file name="index.html">
23600 <script>
23601 angular.module('bindExample', [])
23602 .controller('ExampleController', ['$scope', function($scope) {
23603 $scope.name = 'Whirled';
23604 }]);
23605 </script>
23606 <div ng-controller="ExampleController">
23607 <label>Enter name: <input type="text" ng-model="name"></label><br>
23608 Hello <span ng-bind="name"></span>!
23609 </div>
23610 </file>
23611 <file name="protractor.js" type="protractor">
23612 it('should check ng-bind', function() {
23613 var nameInput = element(by.model('name'));
23614
23615 expect(element(by.binding('name')).getText()).toBe('Whirled');
23616 nameInput.clear();
23617 nameInput.sendKeys('world');
23618 expect(element(by.binding('name')).getText()).toBe('world');
23619 });
23620 </file>
23621 </example>
23622 */
23623var ngBindDirective = ['$compile', function($compile) {
23624 return {
23625 restrict: 'AC',
23626 compile: function ngBindCompile(templateElement) {
23627 $compile.$$addBindingClass(templateElement);
23628 return function ngBindLink(scope, element, attr) {
23629 $compile.$$addBindingInfo(element, attr.ngBind);
23630 element = element[0];
23631 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
23632 element.textContent = isUndefined(value) ? '' : value;
23633 });
23634 };
23635 }
23636 };
23637}];
23638
23639
23640/**
23641 * @ngdoc directive
23642 * @name ngBindTemplate
23643 *
23644 * @description
23645 * The `ngBindTemplate` directive specifies that the element
23646 * text content should be replaced with the interpolation of the template
23647 * in the `ngBindTemplate` attribute.
23648 * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
23649 * expressions. This directive is needed since some HTML elements
23650 * (such as TITLE and OPTION) cannot contain SPAN elements.
23651 *
23652 * @element ANY
23653 * @param {string} ngBindTemplate template of form
23654 * <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
23655 *
23656 * @example
23657 * Try it here: enter text in text box and watch the greeting change.
23658 <example module="bindExample">
23659 <file name="index.html">
23660 <script>
23661 angular.module('bindExample', [])
23662 .controller('ExampleController', ['$scope', function($scope) {
23663 $scope.salutation = 'Hello';
23664 $scope.name = 'World';
23665 }]);
23666 </script>
23667 <div ng-controller="ExampleController">
23668 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
23669 <label>Name: <input type="text" ng-model="name"></label><br>
23670 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
23671 </div>
23672 </file>
23673 <file name="protractor.js" type="protractor">
23674 it('should check ng-bind', function() {
23675 var salutationElem = element(by.binding('salutation'));
23676 var salutationInput = element(by.model('salutation'));
23677 var nameInput = element(by.model('name'));
23678
23679 expect(salutationElem.getText()).toBe('Hello World!');
23680
23681 salutationInput.clear();
23682 salutationInput.sendKeys('Greetings');
23683 nameInput.clear();
23684 nameInput.sendKeys('user');
23685
23686 expect(salutationElem.getText()).toBe('Greetings user!');
23687 });
23688 </file>
23689 </example>
23690 */
23691var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
23692 return {
23693 compile: function ngBindTemplateCompile(templateElement) {
23694 $compile.$$addBindingClass(templateElement);
23695 return function ngBindTemplateLink(scope, element, attr) {
23696 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
23697 $compile.$$addBindingInfo(element, interpolateFn.expressions);
23698 element = element[0];
23699 attr.$observe('ngBindTemplate', function(value) {
23700 element.textContent = isUndefined(value) ? '' : value;
23701 });
23702 };
23703 }
23704 };
23705}];
23706
23707
23708/**
23709 * @ngdoc directive
23710 * @name ngBindHtml
23711 *
23712 * @description
23713 * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
23714 * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
23715 * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
23716 * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
23717 * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
23718 *
23719 * You may also bypass sanitization for values you know are safe. To do so, bind to
23720 * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}. See the example
23721 * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
23722 *
23723 * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
23724 * will have an exception (instead of an exploit.)
23725 *
23726 * @element ANY
23727 * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
23728 *
23729 * @example
23730
23731 <example module="bindHtmlExample" deps="angular-sanitize.js">
23732 <file name="index.html">
23733 <div ng-controller="ExampleController">
23734 <p ng-bind-html="myHTML"></p>
23735 </div>
23736 </file>
23737
23738 <file name="script.js">
23739 angular.module('bindHtmlExample', ['ngSanitize'])
23740 .controller('ExampleController', ['$scope', function($scope) {
23741 $scope.myHTML =
23742 'I am an <code>HTML</code>string with ' +
23743 '<a href="#">links!</a> and other <em>stuff</em>';
23744 }]);
23745 </file>
23746
23747 <file name="protractor.js" type="protractor">
23748 it('should check ng-bind-html', function() {
23749 expect(element(by.binding('myHTML')).getText()).toBe(
23750 'I am an HTMLstring with links! and other stuff');
23751 });
23752 </file>
23753 </example>
23754 */
23755var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
23756 return {
23757 restrict: 'A',
23758 compile: function ngBindHtmlCompile(tElement, tAttrs) {
23759 var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
23760 var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
23761 return (value || '').toString();
23762 });
23763 $compile.$$addBindingClass(tElement);
23764
23765 return function ngBindHtmlLink(scope, element, attr) {
23766 $compile.$$addBindingInfo(element, attr.ngBindHtml);
23767
23768 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
23769 // we re-evaluate the expr because we want a TrustedValueHolderType
23770 // for $sce, not a string
23771 element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
23772 });
23773 };
23774 }
23775 };
23776}];
23777
23778/**
23779 * @ngdoc directive
23780 * @name ngChange
23781 *
23782 * @description
23783 * Evaluate the given expression when the user changes the input.
23784 * The expression is evaluated immediately, unlike the JavaScript onchange event
23785 * which only triggers at the end of a change (usually, when the user leaves the
23786 * form element or presses the return key).
23787 *
23788 * The `ngChange` expression is only evaluated when a change in the input value causes
23789 * a new value to be committed to the model.
23790 *
23791 * It will not be evaluated:
23792 * * if the value returned from the `$parsers` transformation pipeline has not changed
23793 * * if the input has continued to be invalid since the model will stay `null`
23794 * * if the model is changed programmatically and not by a change to the input value
23795 *
23796 *
23797 * Note, this directive requires `ngModel` to be present.
23798 *
23799 * @element input
23800 * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
23801 * in input value.
23802 *
23803 * @example
23804 * <example name="ngChange-directive" module="changeExample">
23805 * <file name="index.html">
23806 * <script>
23807 * angular.module('changeExample', [])
23808 * .controller('ExampleController', ['$scope', function($scope) {
23809 * $scope.counter = 0;
23810 * $scope.change = function() {
23811 * $scope.counter++;
23812 * };
23813 * }]);
23814 * </script>
23815 * <div ng-controller="ExampleController">
23816 * <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
23817 * <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
23818 * <label for="ng-change-example2">Confirmed</label><br />
23819 * <tt>debug = {{confirmed}}</tt><br/>
23820 * <tt>counter = {{counter}}</tt><br/>
23821 * </div>
23822 * </file>
23823 * <file name="protractor.js" type="protractor">
23824 * var counter = element(by.binding('counter'));
23825 * var debug = element(by.binding('confirmed'));
23826 *
23827 * it('should evaluate the expression if changing from view', function() {
23828 * expect(counter.getText()).toContain('0');
23829 *
23830 * element(by.id('ng-change-example1')).click();
23831 *
23832 * expect(counter.getText()).toContain('1');
23833 * expect(debug.getText()).toContain('true');
23834 * });
23835 *
23836 * it('should not evaluate the expression if changing from model', function() {
23837 * element(by.id('ng-change-example2')).click();
23838
23839 * expect(counter.getText()).toContain('0');
23840 * expect(debug.getText()).toContain('true');
23841 * });
23842 * </file>
23843 * </example>
23844 */
23845var ngChangeDirective = valueFn({
23846 restrict: 'A',
23847 require: 'ngModel',
23848 link: function(scope, element, attr, ctrl) {
23849 ctrl.$viewChangeListeners.push(function() {
23850 scope.$eval(attr.ngChange);
23851 });
23852 }
23853});
23854
23855function classDirective(name, selector) {
23856 name = 'ngClass' + name;
23857 return ['$animate', function($animate) {
23858 return {
23859 restrict: 'AC',
23860 link: function(scope, element, attr) {
23861 var oldVal;
23862
23863 scope.$watch(attr[name], ngClassWatchAction, true);
23864
23865 attr.$observe('class', function(value) {
23866 ngClassWatchAction(scope.$eval(attr[name]));
23867 });
23868
23869
23870 if (name !== 'ngClass') {
23871 scope.$watch('$index', function($index, old$index) {
23872 // jshint bitwise: false
23873 var mod = $index & 1;
23874 if (mod !== (old$index & 1)) {
23875 var classes = arrayClasses(scope.$eval(attr[name]));
23876 mod === selector ?
23877 addClasses(classes) :
23878 removeClasses(classes);
23879 }
23880 });
23881 }
23882
23883 function addClasses(classes) {
23884 var newClasses = digestClassCounts(classes, 1);
23885 attr.$addClass(newClasses);
23886 }
23887
23888 function removeClasses(classes) {
23889 var newClasses = digestClassCounts(classes, -1);
23890 attr.$removeClass(newClasses);
23891 }
23892
23893 function digestClassCounts(classes, count) {
23894 // Use createMap() to prevent class assumptions involving property
23895 // names in Object.prototype
23896 var classCounts = element.data('$classCounts') || createMap();
23897 var classesToUpdate = [];
23898 forEach(classes, function(className) {
23899 if (count > 0 || classCounts[className]) {
23900 classCounts[className] = (classCounts[className] || 0) + count;
23901 if (classCounts[className] === +(count > 0)) {
23902 classesToUpdate.push(className);
23903 }
23904 }
23905 });
23906 element.data('$classCounts', classCounts);
23907 return classesToUpdate.join(' ');
23908 }
23909
23910 function updateClasses(oldClasses, newClasses) {
23911 var toAdd = arrayDifference(newClasses, oldClasses);
23912 var toRemove = arrayDifference(oldClasses, newClasses);
23913 toAdd = digestClassCounts(toAdd, 1);
23914 toRemove = digestClassCounts(toRemove, -1);
23915 if (toAdd && toAdd.length) {
23916 $animate.addClass(element, toAdd);
23917 }
23918 if (toRemove && toRemove.length) {
23919 $animate.removeClass(element, toRemove);
23920 }
23921 }
23922
23923 function ngClassWatchAction(newVal) {
23924 if (selector === true || scope.$index % 2 === selector) {
23925 var newClasses = arrayClasses(newVal || []);
23926 if (!oldVal) {
23927 addClasses(newClasses);
23928 } else if (!equals(newVal,oldVal)) {
23929 var oldClasses = arrayClasses(oldVal);
23930 updateClasses(oldClasses, newClasses);
23931 }
23932 }
23933 oldVal = shallowCopy(newVal);
23934 }
23935 }
23936 };
23937
23938 function arrayDifference(tokens1, tokens2) {
23939 var values = [];
23940
23941 outer:
23942 for (var i = 0; i < tokens1.length; i++) {
23943 var token = tokens1[i];
23944 for (var j = 0; j < tokens2.length; j++) {
23945 if (token == tokens2[j]) continue outer;
23946 }
23947 values.push(token);
23948 }
23949 return values;
23950 }
23951
23952 function arrayClasses(classVal) {
23953 var classes = [];
23954 if (isArray(classVal)) {
23955 forEach(classVal, function(v) {
23956 classes = classes.concat(arrayClasses(v));
23957 });
23958 return classes;
23959 } else if (isString(classVal)) {
23960 return classVal.split(' ');
23961 } else if (isObject(classVal)) {
23962 forEach(classVal, function(v, k) {
23963 if (v) {
23964 classes = classes.concat(k.split(' '));
23965 }
23966 });
23967 return classes;
23968 }
23969 return classVal;
23970 }
23971 }];
23972}
23973
23974/**
23975 * @ngdoc directive
23976 * @name ngClass
23977 * @restrict AC
23978 *
23979 * @description
23980 * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
23981 * an expression that represents all classes to be added.
23982 *
23983 * The directive operates in three different ways, depending on which of three types the expression
23984 * evaluates to:
23985 *
23986 * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
23987 * names.
23988 *
23989 * 2. If the expression evaluates to an object, then for each key-value pair of the
23990 * object with a truthy value the corresponding key is used as a class name.
23991 *
23992 * 3. If the expression evaluates to an array, each element of the array should either be a string as in
23993 * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
23994 * to give you more control over what CSS classes appear. See the code below for an example of this.
23995 *
23996 *
23997 * The directive won't add duplicate classes if a particular class was already set.
23998 *
23999 * When the expression changes, the previously added classes are removed and only then are the
24000 * new classes added.
24001 *
24002 * @animations
24003 * | Animation | Occurs |
24004 * |----------------------------------|-------------------------------------|
24005 * | {@link ng.$animate#addClass addClass} | just before the class is applied to the element |
24006 * | {@link ng.$animate#removeClass removeClass} | just before the class is removed from the element |
24007 *
24008 * @element ANY
24009 * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
24010 * of the evaluation can be a string representing space delimited class
24011 * names, an array, or a map of class names to boolean values. In the case of a map, the
24012 * names of the properties whose values are truthy will be added as css classes to the
24013 * element.
24014 *
24015 * @example Example that demonstrates basic bindings via ngClass directive.
24016 <example>
24017 <file name="index.html">
24018 <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
24019 <label>
24020 <input type="checkbox" ng-model="deleted">
24021 deleted (apply "strike" class)
24022 </label><br>
24023 <label>
24024 <input type="checkbox" ng-model="important">
24025 important (apply "bold" class)
24026 </label><br>
24027 <label>
24028 <input type="checkbox" ng-model="error">
24029 error (apply "has-error" class)
24030 </label>
24031 <hr>
24032 <p ng-class="style">Using String Syntax</p>
24033 <input type="text" ng-model="style"
24034 placeholder="Type: bold strike red" aria-label="Type: bold strike red">
24035 <hr>
24036 <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
24037 <input ng-model="style1"
24038 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
24039 <input ng-model="style2"
24040 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
24041 <input ng-model="style3"
24042 placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
24043 <hr>
24044 <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
24045 <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
24046 <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
24047 </file>
24048 <file name="style.css">
24049 .strike {
24050 text-decoration: line-through;
24051 }
24052 .bold {
24053 font-weight: bold;
24054 }
24055 .red {
24056 color: red;
24057 }
24058 .has-error {
24059 color: red;
24060 background-color: yellow;
24061 }
24062 .orange {
24063 color: orange;
24064 }
24065 </file>
24066 <file name="protractor.js" type="protractor">
24067 var ps = element.all(by.css('p'));
24068
24069 it('should let you toggle the class', function() {
24070
24071 expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
24072 expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
24073
24074 element(by.model('important')).click();
24075 expect(ps.first().getAttribute('class')).toMatch(/bold/);
24076
24077 element(by.model('error')).click();
24078 expect(ps.first().getAttribute('class')).toMatch(/has-error/);
24079 });
24080
24081 it('should let you toggle string example', function() {
24082 expect(ps.get(1).getAttribute('class')).toBe('');
24083 element(by.model('style')).clear();
24084 element(by.model('style')).sendKeys('red');
24085 expect(ps.get(1).getAttribute('class')).toBe('red');
24086 });
24087
24088 it('array example should have 3 classes', function() {
24089 expect(ps.get(2).getAttribute('class')).toBe('');
24090 element(by.model('style1')).sendKeys('bold');
24091 element(by.model('style2')).sendKeys('strike');
24092 element(by.model('style3')).sendKeys('red');
24093 expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
24094 });
24095
24096 it('array with map example should have 2 classes', function() {
24097 expect(ps.last().getAttribute('class')).toBe('');
24098 element(by.model('style4')).sendKeys('bold');
24099 element(by.model('warning')).click();
24100 expect(ps.last().getAttribute('class')).toBe('bold orange');
24101 });
24102 </file>
24103 </example>
24104
24105 ## Animations
24106
24107 The example below demonstrates how to perform animations using ngClass.
24108
24109 <example module="ngAnimate" deps="angular-animate.js" animations="true">
24110 <file name="index.html">
24111 <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
24112 <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
24113 <br>
24114 <span class="base-class" ng-class="myVar">Sample Text</span>
24115 </file>
24116 <file name="style.css">
24117 .base-class {
24118 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24119 }
24120
24121 .base-class.my-class {
24122 color: red;
24123 font-size:3em;
24124 }
24125 </file>
24126 <file name="protractor.js" type="protractor">
24127 it('should check ng-class', function() {
24128 expect(element(by.css('.base-class')).getAttribute('class')).not.
24129 toMatch(/my-class/);
24130
24131 element(by.id('setbtn')).click();
24132
24133 expect(element(by.css('.base-class')).getAttribute('class')).
24134 toMatch(/my-class/);
24135
24136 element(by.id('clearbtn')).click();
24137
24138 expect(element(by.css('.base-class')).getAttribute('class')).not.
24139 toMatch(/my-class/);
24140 });
24141 </file>
24142 </example>
24143
24144
24145 ## ngClass and pre-existing CSS3 Transitions/Animations
24146 The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
24147 Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
24148 any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
24149 to view the step by step details of {@link $animate#addClass $animate.addClass} and
24150 {@link $animate#removeClass $animate.removeClass}.
24151 */
24152var ngClassDirective = classDirective('', true);
24153
24154/**
24155 * @ngdoc directive
24156 * @name ngClassOdd
24157 * @restrict AC
24158 *
24159 * @description
24160 * The `ngClassOdd` and `ngClassEven` directives work exactly as
24161 * {@link ng.directive:ngClass ngClass}, except they work in
24162 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
24163 *
24164 * This directive can be applied only within the scope of an
24165 * {@link ng.directive:ngRepeat ngRepeat}.
24166 *
24167 * @element ANY
24168 * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
24169 * of the evaluation can be a string representing space delimited class names or an array.
24170 *
24171 * @example
24172 <example>
24173 <file name="index.html">
24174 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
24175 <li ng-repeat="name in names">
24176 <span ng-class-odd="'odd'" ng-class-even="'even'">
24177 {{name}}
24178 </span>
24179 </li>
24180 </ol>
24181 </file>
24182 <file name="style.css">
24183 .odd {
24184 color: red;
24185 }
24186 .even {
24187 color: blue;
24188 }
24189 </file>
24190 <file name="protractor.js" type="protractor">
24191 it('should check ng-class-odd and ng-class-even', function() {
24192 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
24193 toMatch(/odd/);
24194 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
24195 toMatch(/even/);
24196 });
24197 </file>
24198 </example>
24199 */
24200var ngClassOddDirective = classDirective('Odd', 0);
24201
24202/**
24203 * @ngdoc directive
24204 * @name ngClassEven
24205 * @restrict AC
24206 *
24207 * @description
24208 * The `ngClassOdd` and `ngClassEven` directives work exactly as
24209 * {@link ng.directive:ngClass ngClass}, except they work in
24210 * conjunction with `ngRepeat` and take effect only on odd (even) rows.
24211 *
24212 * This directive can be applied only within the scope of an
24213 * {@link ng.directive:ngRepeat ngRepeat}.
24214 *
24215 * @element ANY
24216 * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
24217 * result of the evaluation can be a string representing space delimited class names or an array.
24218 *
24219 * @example
24220 <example>
24221 <file name="index.html">
24222 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
24223 <li ng-repeat="name in names">
24224 <span ng-class-odd="'odd'" ng-class-even="'even'">
24225 {{name}}
24226 </span>
24227 </li>
24228 </ol>
24229 </file>
24230 <file name="style.css">
24231 .odd {
24232 color: red;
24233 }
24234 .even {
24235 color: blue;
24236 }
24237 </file>
24238 <file name="protractor.js" type="protractor">
24239 it('should check ng-class-odd and ng-class-even', function() {
24240 expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
24241 toMatch(/odd/);
24242 expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
24243 toMatch(/even/);
24244 });
24245 </file>
24246 </example>
24247 */
24248var ngClassEvenDirective = classDirective('Even', 1);
24249
24250/**
24251 * @ngdoc directive
24252 * @name ngCloak
24253 * @restrict AC
24254 *
24255 * @description
24256 * The `ngCloak` directive is used to prevent the Angular html template from being briefly
24257 * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
24258 * directive to avoid the undesirable flicker effect caused by the html template display.
24259 *
24260 * The directive can be applied to the `<body>` element, but the preferred usage is to apply
24261 * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
24262 * of the browser view.
24263 *
24264 * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
24265 * `angular.min.js`.
24266 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
24267 *
24268 * ```css
24269 * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
24270 * display: none !important;
24271 * }
24272 * ```
24273 *
24274 * When this css rule is loaded by the browser, all html elements (including their children) that
24275 * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
24276 * during the compilation of the template it deletes the `ngCloak` element attribute, making
24277 * the compiled element visible.
24278 *
24279 * For the best result, the `angular.js` script must be loaded in the head section of the html
24280 * document; alternatively, the css rule above must be included in the external stylesheet of the
24281 * application.
24282 *
24283 * @element ANY
24284 *
24285 * @example
24286 <example>
24287 <file name="index.html">
24288 <div id="template1" ng-cloak>{{ 'hello' }}</div>
24289 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
24290 </file>
24291 <file name="protractor.js" type="protractor">
24292 it('should remove the template directive and css class', function() {
24293 expect($('#template1').getAttribute('ng-cloak')).
24294 toBeNull();
24295 expect($('#template2').getAttribute('ng-cloak')).
24296 toBeNull();
24297 });
24298 </file>
24299 </example>
24300 *
24301 */
24302var ngCloakDirective = ngDirective({
24303 compile: function(element, attr) {
24304 attr.$set('ngCloak', undefined);
24305 element.removeClass('ng-cloak');
24306 }
24307});
24308
24309/**
24310 * @ngdoc directive
24311 * @name ngController
24312 *
24313 * @description
24314 * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
24315 * supports the principles behind the Model-View-Controller design pattern.
24316 *
24317 * MVC components in angular:
24318 *
24319 * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
24320 * are accessed through bindings.
24321 * * View — The template (HTML with data bindings) that is rendered into the View.
24322 * * Controller — The `ngController` directive specifies a Controller class; the class contains business
24323 * logic behind the application to decorate the scope with functions and values
24324 *
24325 * Note that you can also attach controllers to the DOM by declaring it in a route definition
24326 * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
24327 * again using `ng-controller` in the template itself. This will cause the controller to be attached
24328 * and executed twice.
24329 *
24330 * @element ANY
24331 * @scope
24332 * @priority 500
24333 * @param {expression} ngController Name of a constructor function registered with the current
24334 * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
24335 * that on the current scope evaluates to a constructor function.
24336 *
24337 * The controller instance can be published into a scope property by specifying
24338 * `ng-controller="as propertyName"`.
24339 *
24340 * If the current `$controllerProvider` is configured to use globals (via
24341 * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
24342 * also be the name of a globally accessible constructor function (not recommended).
24343 *
24344 * @example
24345 * Here is a simple form for editing user contact information. Adding, removing, clearing, and
24346 * greeting are methods declared on the controller (see source tab). These methods can
24347 * easily be called from the angular markup. Any changes to the data are automatically reflected
24348 * in the View without the need for a manual update.
24349 *
24350 * Two different declaration styles are included below:
24351 *
24352 * * one binds methods and properties directly onto the controller using `this`:
24353 * `ng-controller="SettingsController1 as settings"`
24354 * * one injects `$scope` into the controller:
24355 * `ng-controller="SettingsController2"`
24356 *
24357 * The second option is more common in the Angular community, and is generally used in boilerplates
24358 * and in this guide. However, there are advantages to binding properties directly to the controller
24359 * and avoiding scope.
24360 *
24361 * * Using `controller as` makes it obvious which controller you are accessing in the template when
24362 * multiple controllers apply to an element.
24363 * * If you are writing your controllers as classes you have easier access to the properties and
24364 * methods, which will appear on the scope, from inside the controller code.
24365 * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
24366 * inheritance masking primitives.
24367 *
24368 * This example demonstrates the `controller as` syntax.
24369 *
24370 * <example name="ngControllerAs" module="controllerAsExample">
24371 * <file name="index.html">
24372 * <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
24373 * <label>Name: <input type="text" ng-model="settings.name"/></label>
24374 * <button ng-click="settings.greet()">greet</button><br/>
24375 * Contact:
24376 * <ul>
24377 * <li ng-repeat="contact in settings.contacts">
24378 * <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
24379 * <option>phone</option>
24380 * <option>email</option>
24381 * </select>
24382 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
24383 * <button ng-click="settings.clearContact(contact)">clear</button>
24384 * <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
24385 * </li>
24386 * <li><button ng-click="settings.addContact()">add</button></li>
24387 * </ul>
24388 * </div>
24389 * </file>
24390 * <file name="app.js">
24391 * angular.module('controllerAsExample', [])
24392 * .controller('SettingsController1', SettingsController1);
24393 *
24394 * function SettingsController1() {
24395 * this.name = "John Smith";
24396 * this.contacts = [
24397 * {type: 'phone', value: '408 555 1212'},
24398 * {type: 'email', value: 'john.smith@example.org'} ];
24399 * }
24400 *
24401 * SettingsController1.prototype.greet = function() {
24402 * alert(this.name);
24403 * };
24404 *
24405 * SettingsController1.prototype.addContact = function() {
24406 * this.contacts.push({type: 'email', value: 'yourname@example.org'});
24407 * };
24408 *
24409 * SettingsController1.prototype.removeContact = function(contactToRemove) {
24410 * var index = this.contacts.indexOf(contactToRemove);
24411 * this.contacts.splice(index, 1);
24412 * };
24413 *
24414 * SettingsController1.prototype.clearContact = function(contact) {
24415 * contact.type = 'phone';
24416 * contact.value = '';
24417 * };
24418 * </file>
24419 * <file name="protractor.js" type="protractor">
24420 * it('should check controller as', function() {
24421 * var container = element(by.id('ctrl-as-exmpl'));
24422 * expect(container.element(by.model('settings.name'))
24423 * .getAttribute('value')).toBe('John Smith');
24424 *
24425 * var firstRepeat =
24426 * container.element(by.repeater('contact in settings.contacts').row(0));
24427 * var secondRepeat =
24428 * container.element(by.repeater('contact in settings.contacts').row(1));
24429 *
24430 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24431 * .toBe('408 555 1212');
24432 *
24433 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
24434 * .toBe('john.smith@example.org');
24435 *
24436 * firstRepeat.element(by.buttonText('clear')).click();
24437 *
24438 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24439 * .toBe('');
24440 *
24441 * container.element(by.buttonText('add')).click();
24442 *
24443 * expect(container.element(by.repeater('contact in settings.contacts').row(2))
24444 * .element(by.model('contact.value'))
24445 * .getAttribute('value'))
24446 * .toBe('yourname@example.org');
24447 * });
24448 * </file>
24449 * </example>
24450 *
24451 * This example demonstrates the "attach to `$scope`" style of controller.
24452 *
24453 * <example name="ngController" module="controllerExample">
24454 * <file name="index.html">
24455 * <div id="ctrl-exmpl" ng-controller="SettingsController2">
24456 * <label>Name: <input type="text" ng-model="name"/></label>
24457 * <button ng-click="greet()">greet</button><br/>
24458 * Contact:
24459 * <ul>
24460 * <li ng-repeat="contact in contacts">
24461 * <select ng-model="contact.type" id="select_{{$index}}">
24462 * <option>phone</option>
24463 * <option>email</option>
24464 * </select>
24465 * <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
24466 * <button ng-click="clearContact(contact)">clear</button>
24467 * <button ng-click="removeContact(contact)">X</button>
24468 * </li>
24469 * <li>[ <button ng-click="addContact()">add</button> ]</li>
24470 * </ul>
24471 * </div>
24472 * </file>
24473 * <file name="app.js">
24474 * angular.module('controllerExample', [])
24475 * .controller('SettingsController2', ['$scope', SettingsController2]);
24476 *
24477 * function SettingsController2($scope) {
24478 * $scope.name = "John Smith";
24479 * $scope.contacts = [
24480 * {type:'phone', value:'408 555 1212'},
24481 * {type:'email', value:'john.smith@example.org'} ];
24482 *
24483 * $scope.greet = function() {
24484 * alert($scope.name);
24485 * };
24486 *
24487 * $scope.addContact = function() {
24488 * $scope.contacts.push({type:'email', value:'yourname@example.org'});
24489 * };
24490 *
24491 * $scope.removeContact = function(contactToRemove) {
24492 * var index = $scope.contacts.indexOf(contactToRemove);
24493 * $scope.contacts.splice(index, 1);
24494 * };
24495 *
24496 * $scope.clearContact = function(contact) {
24497 * contact.type = 'phone';
24498 * contact.value = '';
24499 * };
24500 * }
24501 * </file>
24502 * <file name="protractor.js" type="protractor">
24503 * it('should check controller', function() {
24504 * var container = element(by.id('ctrl-exmpl'));
24505 *
24506 * expect(container.element(by.model('name'))
24507 * .getAttribute('value')).toBe('John Smith');
24508 *
24509 * var firstRepeat =
24510 * container.element(by.repeater('contact in contacts').row(0));
24511 * var secondRepeat =
24512 * container.element(by.repeater('contact in contacts').row(1));
24513 *
24514 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24515 * .toBe('408 555 1212');
24516 * expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
24517 * .toBe('john.smith@example.org');
24518 *
24519 * firstRepeat.element(by.buttonText('clear')).click();
24520 *
24521 * expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
24522 * .toBe('');
24523 *
24524 * container.element(by.buttonText('add')).click();
24525 *
24526 * expect(container.element(by.repeater('contact in contacts').row(2))
24527 * .element(by.model('contact.value'))
24528 * .getAttribute('value'))
24529 * .toBe('yourname@example.org');
24530 * });
24531 * </file>
24532 *</example>
24533
24534 */
24535var ngControllerDirective = [function() {
24536 return {
24537 restrict: 'A',
24538 scope: true,
24539 controller: '@',
24540 priority: 500
24541 };
24542}];
24543
24544/**
24545 * @ngdoc directive
24546 * @name ngCsp
24547 *
24548 * @element html
24549 * @description
24550 *
24551 * Angular has some features that can break certain
24552 * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
24553 *
24554 * If you intend to implement these rules then you must tell Angular not to use these features.
24555 *
24556 * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
24557 *
24558 *
24559 * The following rules affect Angular:
24560 *
24561 * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
24562 * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
24563 * increase in the speed of evaluating Angular expressions.
24564 *
24565 * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
24566 * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
24567 * To make these directives work when a CSP rule is blocking inline styles, you must link to the
24568 * `angular-csp.css` in your HTML manually.
24569 *
24570 * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
24571 * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
24572 * however, triggers a CSP error to be logged in the console:
24573 *
24574 * ```
24575 * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
24576 * script in the following Content Security Policy directive: "default-src 'self'". Note that
24577 * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
24578 * ```
24579 *
24580 * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
24581 * directive on an element of the HTML document that appears before the `<script>` tag that loads
24582 * the `angular.js` file.
24583 *
24584 * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
24585 *
24586 * You can specify which of the CSP related Angular features should be deactivated by providing
24587 * a value for the `ng-csp` attribute. The options are as follows:
24588 *
24589 * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
24590 *
24591 * * no-unsafe-eval: this stops Angular from optimizing $parse with unsafe eval of strings
24592 *
24593 * You can use these values in the following combinations:
24594 *
24595 *
24596 * * No declaration means that Angular will assume that you can do inline styles, but it will do
24597 * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
24598 * of Angular.
24599 *
24600 * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
24601 * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
24602 * of Angular.
24603 *
24604 * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
24605 * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
24606 *
24607 * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
24608 * run eval - no automatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
24609 *
24610 * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
24611 * styles nor use eval, which is the same as an empty: ng-csp.
24612 * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
24613 *
24614 * @example
24615 * This example shows how to apply the `ngCsp` directive to the `html` tag.
24616 ```html
24617 <!doctype html>
24618 <html ng-app ng-csp>
24619 ...
24620 ...
24621 </html>
24622 ```
24623 * @example
24624 // Note: the suffix `.csp` in the example name triggers
24625 // csp mode in our http server!
24626 <example name="example.csp" module="cspExample" ng-csp="true">
24627 <file name="index.html">
24628 <div ng-controller="MainController as ctrl">
24629 <div>
24630 <button ng-click="ctrl.inc()" id="inc">Increment</button>
24631 <span id="counter">
24632 {{ctrl.counter}}
24633 </span>
24634 </div>
24635
24636 <div>
24637 <button ng-click="ctrl.evil()" id="evil">Evil</button>
24638 <span id="evilError">
24639 {{ctrl.evilError}}
24640 </span>
24641 </div>
24642 </div>
24643 </file>
24644 <file name="script.js">
24645 angular.module('cspExample', [])
24646 .controller('MainController', function() {
24647 this.counter = 0;
24648 this.inc = function() {
24649 this.counter++;
24650 };
24651 this.evil = function() {
24652 // jshint evil:true
24653 try {
24654 eval('1+2');
24655 } catch (e) {
24656 this.evilError = e.message;
24657 }
24658 };
24659 });
24660 </file>
24661 <file name="protractor.js" type="protractor">
24662 var util, webdriver;
24663
24664 var incBtn = element(by.id('inc'));
24665 var counter = element(by.id('counter'));
24666 var evilBtn = element(by.id('evil'));
24667 var evilError = element(by.id('evilError'));
24668
24669 function getAndClearSevereErrors() {
24670 return browser.manage().logs().get('browser').then(function(browserLog) {
24671 return browserLog.filter(function(logEntry) {
24672 return logEntry.level.value > webdriver.logging.Level.WARNING.value;
24673 });
24674 });
24675 }
24676
24677 function clearErrors() {
24678 getAndClearSevereErrors();
24679 }
24680
24681 function expectNoErrors() {
24682 getAndClearSevereErrors().then(function(filteredLog) {
24683 expect(filteredLog.length).toEqual(0);
24684 if (filteredLog.length) {
24685 console.log('browser console errors: ' + util.inspect(filteredLog));
24686 }
24687 });
24688 }
24689
24690 function expectError(regex) {
24691 getAndClearSevereErrors().then(function(filteredLog) {
24692 var found = false;
24693 filteredLog.forEach(function(log) {
24694 if (log.message.match(regex)) {
24695 found = true;
24696 }
24697 });
24698 if (!found) {
24699 throw new Error('expected an error that matches ' + regex);
24700 }
24701 });
24702 }
24703
24704 beforeEach(function() {
24705 util = require('util');
24706 webdriver = require('protractor/node_modules/selenium-webdriver');
24707 });
24708
24709 // For now, we only test on Chrome,
24710 // as Safari does not load the page with Protractor's injected scripts,
24711 // and Firefox webdriver always disables content security policy (#6358)
24712 if (browser.params.browser !== 'chrome') {
24713 return;
24714 }
24715
24716 it('should not report errors when the page is loaded', function() {
24717 // clear errors so we are not dependent on previous tests
24718 clearErrors();
24719 // Need to reload the page as the page is already loaded when
24720 // we come here
24721 browser.driver.getCurrentUrl().then(function(url) {
24722 browser.get(url);
24723 });
24724 expectNoErrors();
24725 });
24726
24727 it('should evaluate expressions', function() {
24728 expect(counter.getText()).toEqual('0');
24729 incBtn.click();
24730 expect(counter.getText()).toEqual('1');
24731 expectNoErrors();
24732 });
24733
24734 it('should throw and report an error when using "eval"', function() {
24735 evilBtn.click();
24736 expect(evilError.getText()).toMatch(/Content Security Policy/);
24737 expectError(/Content Security Policy/);
24738 });
24739 </file>
24740 </example>
24741 */
24742
24743// ngCsp is not implemented as a proper directive any more, because we need it be processed while we
24744// bootstrap the system (before $parse is instantiated), for this reason we just have
24745// the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
24746
24747/**
24748 * @ngdoc directive
24749 * @name ngClick
24750 *
24751 * @description
24752 * The ngClick directive allows you to specify custom behavior when
24753 * an element is clicked.
24754 *
24755 * @element ANY
24756 * @priority 0
24757 * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
24758 * click. ({@link guide/expression#-event- Event object is available as `$event`})
24759 *
24760 * @example
24761 <example>
24762 <file name="index.html">
24763 <button ng-click="count = count + 1" ng-init="count=0">
24764 Increment
24765 </button>
24766 <span>
24767 count: {{count}}
24768 </span>
24769 </file>
24770 <file name="protractor.js" type="protractor">
24771 it('should check ng-click', function() {
24772 expect(element(by.binding('count')).getText()).toMatch('0');
24773 element(by.css('button')).click();
24774 expect(element(by.binding('count')).getText()).toMatch('1');
24775 });
24776 </file>
24777 </example>
24778 */
24779/*
24780 * A collection of directives that allows creation of custom event handlers that are defined as
24781 * angular expressions and are compiled and executed within the current scope.
24782 */
24783var ngEventDirectives = {};
24784
24785// For events that might fire synchronously during DOM manipulation
24786// we need to execute their event handlers asynchronously using $evalAsync,
24787// so that they are not executed in an inconsistent state.
24788var forceAsyncEvents = {
24789 'blur': true,
24790 'focus': true
24791};
24792forEach(
24793 'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
24794 function(eventName) {
24795 var directiveName = directiveNormalize('ng-' + eventName);
24796 ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
24797 return {
24798 restrict: 'A',
24799 compile: function($element, attr) {
24800 // We expose the powerful $event object on the scope that provides access to the Window,
24801 // etc. that isn't protected by the fast paths in $parse. We explicitly request better
24802 // checks at the cost of speed since event handler expressions are not executed as
24803 // frequently as regular change detection.
24804 var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
24805 return function ngEventHandler(scope, element) {
24806 element.on(eventName, function(event) {
24807 var callback = function() {
24808 fn(scope, {$event:event});
24809 };
24810 if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
24811 scope.$evalAsync(callback);
24812 } else {
24813 scope.$apply(callback);
24814 }
24815 });
24816 };
24817 }
24818 };
24819 }];
24820 }
24821);
24822
24823/**
24824 * @ngdoc directive
24825 * @name ngDblclick
24826 *
24827 * @description
24828 * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
24829 *
24830 * @element ANY
24831 * @priority 0
24832 * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
24833 * a dblclick. (The Event object is available as `$event`)
24834 *
24835 * @example
24836 <example>
24837 <file name="index.html">
24838 <button ng-dblclick="count = count + 1" ng-init="count=0">
24839 Increment (on double click)
24840 </button>
24841 count: {{count}}
24842 </file>
24843 </example>
24844 */
24845
24846
24847/**
24848 * @ngdoc directive
24849 * @name ngMousedown
24850 *
24851 * @description
24852 * The ngMousedown directive allows you to specify custom behavior on mousedown event.
24853 *
24854 * @element ANY
24855 * @priority 0
24856 * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
24857 * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
24858 *
24859 * @example
24860 <example>
24861 <file name="index.html">
24862 <button ng-mousedown="count = count + 1" ng-init="count=0">
24863 Increment (on mouse down)
24864 </button>
24865 count: {{count}}
24866 </file>
24867 </example>
24868 */
24869
24870
24871/**
24872 * @ngdoc directive
24873 * @name ngMouseup
24874 *
24875 * @description
24876 * Specify custom behavior on mouseup event.
24877 *
24878 * @element ANY
24879 * @priority 0
24880 * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
24881 * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
24882 *
24883 * @example
24884 <example>
24885 <file name="index.html">
24886 <button ng-mouseup="count = count + 1" ng-init="count=0">
24887 Increment (on mouse up)
24888 </button>
24889 count: {{count}}
24890 </file>
24891 </example>
24892 */
24893
24894/**
24895 * @ngdoc directive
24896 * @name ngMouseover
24897 *
24898 * @description
24899 * Specify custom behavior on mouseover event.
24900 *
24901 * @element ANY
24902 * @priority 0
24903 * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
24904 * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
24905 *
24906 * @example
24907 <example>
24908 <file name="index.html">
24909 <button ng-mouseover="count = count + 1" ng-init="count=0">
24910 Increment (when mouse is over)
24911 </button>
24912 count: {{count}}
24913 </file>
24914 </example>
24915 */
24916
24917
24918/**
24919 * @ngdoc directive
24920 * @name ngMouseenter
24921 *
24922 * @description
24923 * Specify custom behavior on mouseenter event.
24924 *
24925 * @element ANY
24926 * @priority 0
24927 * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
24928 * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
24929 *
24930 * @example
24931 <example>
24932 <file name="index.html">
24933 <button ng-mouseenter="count = count + 1" ng-init="count=0">
24934 Increment (when mouse enters)
24935 </button>
24936 count: {{count}}
24937 </file>
24938 </example>
24939 */
24940
24941
24942/**
24943 * @ngdoc directive
24944 * @name ngMouseleave
24945 *
24946 * @description
24947 * Specify custom behavior on mouseleave event.
24948 *
24949 * @element ANY
24950 * @priority 0
24951 * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
24952 * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
24953 *
24954 * @example
24955 <example>
24956 <file name="index.html">
24957 <button ng-mouseleave="count = count + 1" ng-init="count=0">
24958 Increment (when mouse leaves)
24959 </button>
24960 count: {{count}}
24961 </file>
24962 </example>
24963 */
24964
24965
24966/**
24967 * @ngdoc directive
24968 * @name ngMousemove
24969 *
24970 * @description
24971 * Specify custom behavior on mousemove event.
24972 *
24973 * @element ANY
24974 * @priority 0
24975 * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
24976 * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
24977 *
24978 * @example
24979 <example>
24980 <file name="index.html">
24981 <button ng-mousemove="count = count + 1" ng-init="count=0">
24982 Increment (when mouse moves)
24983 </button>
24984 count: {{count}}
24985 </file>
24986 </example>
24987 */
24988
24989
24990/**
24991 * @ngdoc directive
24992 * @name ngKeydown
24993 *
24994 * @description
24995 * Specify custom behavior on keydown event.
24996 *
24997 * @element ANY
24998 * @priority 0
24999 * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
25000 * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
25001 *
25002 * @example
25003 <example>
25004 <file name="index.html">
25005 <input ng-keydown="count = count + 1" ng-init="count=0">
25006 key down count: {{count}}
25007 </file>
25008 </example>
25009 */
25010
25011
25012/**
25013 * @ngdoc directive
25014 * @name ngKeyup
25015 *
25016 * @description
25017 * Specify custom behavior on keyup event.
25018 *
25019 * @element ANY
25020 * @priority 0
25021 * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
25022 * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
25023 *
25024 * @example
25025 <example>
25026 <file name="index.html">
25027 <p>Typing in the input box below updates the key count</p>
25028 <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
25029
25030 <p>Typing in the input box below updates the keycode</p>
25031 <input ng-keyup="event=$event">
25032 <p>event keyCode: {{ event.keyCode }}</p>
25033 <p>event altKey: {{ event.altKey }}</p>
25034 </file>
25035 </example>
25036 */
25037
25038
25039/**
25040 * @ngdoc directive
25041 * @name ngKeypress
25042 *
25043 * @description
25044 * Specify custom behavior on keypress event.
25045 *
25046 * @element ANY
25047 * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
25048 * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
25049 * and can be interrogated for keyCode, altKey, etc.)
25050 *
25051 * @example
25052 <example>
25053 <file name="index.html">
25054 <input ng-keypress="count = count + 1" ng-init="count=0">
25055 key press count: {{count}}
25056 </file>
25057 </example>
25058 */
25059
25060
25061/**
25062 * @ngdoc directive
25063 * @name ngSubmit
25064 *
25065 * @description
25066 * Enables binding angular expressions to onsubmit events.
25067 *
25068 * Additionally it prevents the default action (which for form means sending the request to the
25069 * server and reloading the current page), but only if the form does not contain `action`,
25070 * `data-action`, or `x-action` attributes.
25071 *
25072 * <div class="alert alert-warning">
25073 * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
25074 * `ngSubmit` handlers together. See the
25075 * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
25076 * for a detailed discussion of when `ngSubmit` may be triggered.
25077 * </div>
25078 *
25079 * @element form
25080 * @priority 0
25081 * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
25082 * ({@link guide/expression#-event- Event object is available as `$event`})
25083 *
25084 * @example
25085 <example module="submitExample">
25086 <file name="index.html">
25087 <script>
25088 angular.module('submitExample', [])
25089 .controller('ExampleController', ['$scope', function($scope) {
25090 $scope.list = [];
25091 $scope.text = 'hello';
25092 $scope.submit = function() {
25093 if ($scope.text) {
25094 $scope.list.push(this.text);
25095 $scope.text = '';
25096 }
25097 };
25098 }]);
25099 </script>
25100 <form ng-submit="submit()" ng-controller="ExampleController">
25101 Enter text and hit enter:
25102 <input type="text" ng-model="text" name="text" />
25103 <input type="submit" id="submit" value="Submit" />
25104 <pre>list={{list}}</pre>
25105 </form>
25106 </file>
25107 <file name="protractor.js" type="protractor">
25108 it('should check ng-submit', function() {
25109 expect(element(by.binding('list')).getText()).toBe('list=[]');
25110 element(by.css('#submit')).click();
25111 expect(element(by.binding('list')).getText()).toContain('hello');
25112 expect(element(by.model('text')).getAttribute('value')).toBe('');
25113 });
25114 it('should ignore empty strings', function() {
25115 expect(element(by.binding('list')).getText()).toBe('list=[]');
25116 element(by.css('#submit')).click();
25117 element(by.css('#submit')).click();
25118 expect(element(by.binding('list')).getText()).toContain('hello');
25119 });
25120 </file>
25121 </example>
25122 */
25123
25124/**
25125 * @ngdoc directive
25126 * @name ngFocus
25127 *
25128 * @description
25129 * Specify custom behavior on focus event.
25130 *
25131 * Note: As the `focus` event is executed synchronously when calling `input.focus()`
25132 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
25133 * during an `$apply` to ensure a consistent state.
25134 *
25135 * @element window, input, select, textarea, a
25136 * @priority 0
25137 * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
25138 * focus. ({@link guide/expression#-event- Event object is available as `$event`})
25139 *
25140 * @example
25141 * See {@link ng.directive:ngClick ngClick}
25142 */
25143
25144/**
25145 * @ngdoc directive
25146 * @name ngBlur
25147 *
25148 * @description
25149 * Specify custom behavior on blur event.
25150 *
25151 * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
25152 * an element has lost focus.
25153 *
25154 * Note: As the `blur` event is executed synchronously also during DOM manipulations
25155 * (e.g. removing a focussed input),
25156 * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
25157 * during an `$apply` to ensure a consistent state.
25158 *
25159 * @element window, input, select, textarea, a
25160 * @priority 0
25161 * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
25162 * blur. ({@link guide/expression#-event- Event object is available as `$event`})
25163 *
25164 * @example
25165 * See {@link ng.directive:ngClick ngClick}
25166 */
25167
25168/**
25169 * @ngdoc directive
25170 * @name ngCopy
25171 *
25172 * @description
25173 * Specify custom behavior on copy event.
25174 *
25175 * @element window, input, select, textarea, a
25176 * @priority 0
25177 * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
25178 * copy. ({@link guide/expression#-event- Event object is available as `$event`})
25179 *
25180 * @example
25181 <example>
25182 <file name="index.html">
25183 <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
25184 copied: {{copied}}
25185 </file>
25186 </example>
25187 */
25188
25189/**
25190 * @ngdoc directive
25191 * @name ngCut
25192 *
25193 * @description
25194 * Specify custom behavior on cut event.
25195 *
25196 * @element window, input, select, textarea, a
25197 * @priority 0
25198 * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
25199 * cut. ({@link guide/expression#-event- Event object is available as `$event`})
25200 *
25201 * @example
25202 <example>
25203 <file name="index.html">
25204 <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
25205 cut: {{cut}}
25206 </file>
25207 </example>
25208 */
25209
25210/**
25211 * @ngdoc directive
25212 * @name ngPaste
25213 *
25214 * @description
25215 * Specify custom behavior on paste event.
25216 *
25217 * @element window, input, select, textarea, a
25218 * @priority 0
25219 * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
25220 * paste. ({@link guide/expression#-event- Event object is available as `$event`})
25221 *
25222 * @example
25223 <example>
25224 <file name="index.html">
25225 <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
25226 pasted: {{paste}}
25227 </file>
25228 </example>
25229 */
25230
25231/**
25232 * @ngdoc directive
25233 * @name ngIf
25234 * @restrict A
25235 * @multiElement
25236 *
25237 * @description
25238 * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
25239 * {expression}. If the expression assigned to `ngIf` evaluates to a false
25240 * value then the element is removed from the DOM, otherwise a clone of the
25241 * element is reinserted into the DOM.
25242 *
25243 * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
25244 * element in the DOM rather than changing its visibility via the `display` css property. A common
25245 * case when this difference is significant is when using css selectors that rely on an element's
25246 * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
25247 *
25248 * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
25249 * is created when the element is restored. The scope created within `ngIf` inherits from
25250 * its parent scope using
25251 * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
25252 * An important implication of this is if `ngModel` is used within `ngIf` to bind to
25253 * a javascript primitive defined in the parent scope. In this case any modifications made to the
25254 * variable within the child scope will override (hide) the value in the parent scope.
25255 *
25256 * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
25257 * is if an element's class attribute is directly modified after it's compiled, using something like
25258 * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
25259 * the added class will be lost because the original compiled state is used to regenerate the element.
25260 *
25261 * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
25262 * and `leave` effects.
25263 *
25264 * @animations
25265 * | Animation | Occurs |
25266 * |----------------------------------|-------------------------------------|
25267 * | {@link ng.$animate#enter enter} | just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container |
25268 * | {@link ng.$animate#leave leave} | just before the `ngIf` contents are removed from the DOM |
25269 *
25270 * @element ANY
25271 * @scope
25272 * @priority 600
25273 * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
25274 * the element is removed from the DOM tree. If it is truthy a copy of the compiled
25275 * element is added to the DOM tree.
25276 *
25277 * @example
25278 <example module="ngAnimate" deps="angular-animate.js" animations="true">
25279 <file name="index.html">
25280 <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
25281 Show when checked:
25282 <span ng-if="checked" class="animate-if">
25283 This is removed when the checkbox is unchecked.
25284 </span>
25285 </file>
25286 <file name="animations.css">
25287 .animate-if {
25288 background:white;
25289 border:1px solid black;
25290 padding:10px;
25291 }
25292
25293 .animate-if.ng-enter, .animate-if.ng-leave {
25294 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25295 }
25296
25297 .animate-if.ng-enter,
25298 .animate-if.ng-leave.ng-leave-active {
25299 opacity:0;
25300 }
25301
25302 .animate-if.ng-leave,
25303 .animate-if.ng-enter.ng-enter-active {
25304 opacity:1;
25305 }
25306 </file>
25307 </example>
25308 */
25309var ngIfDirective = ['$animate', '$compile', function($animate, $compile) {
25310 return {
25311 multiElement: true,
25312 transclude: 'element',
25313 priority: 600,
25314 terminal: true,
25315 restrict: 'A',
25316 $$tlb: true,
25317 link: function($scope, $element, $attr, ctrl, $transclude) {
25318 var block, childScope, previousElements;
25319 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
25320
25321 if (value) {
25322 if (!childScope) {
25323 $transclude(function(clone, newScope) {
25324 childScope = newScope;
25325 clone[clone.length++] = $compile.$$createComment('end ngIf', $attr.ngIf);
25326 // Note: We only need the first/last node of the cloned nodes.
25327 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
25328 // by a directive with templateUrl when its template arrives.
25329 block = {
25330 clone: clone
25331 };
25332 $animate.enter(clone, $element.parent(), $element);
25333 });
25334 }
25335 } else {
25336 if (previousElements) {
25337 previousElements.remove();
25338 previousElements = null;
25339 }
25340 if (childScope) {
25341 childScope.$destroy();
25342 childScope = null;
25343 }
25344 if (block) {
25345 previousElements = getBlockNodes(block.clone);
25346 $animate.leave(previousElements).then(function() {
25347 previousElements = null;
25348 });
25349 block = null;
25350 }
25351 }
25352 });
25353 }
25354 };
25355}];
25356
25357/**
25358 * @ngdoc directive
25359 * @name ngInclude
25360 * @restrict ECA
25361 *
25362 * @description
25363 * Fetches, compiles and includes an external HTML fragment.
25364 *
25365 * By default, the template URL is restricted to the same domain and protocol as the
25366 * application document. This is done by calling {@link $sce#getTrustedResourceUrl
25367 * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
25368 * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
25369 * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
25370 * ng.$sce Strict Contextual Escaping}.
25371 *
25372 * In addition, the browser's
25373 * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
25374 * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
25375 * policy may further restrict whether the template is successfully loaded.
25376 * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
25377 * access on some browsers.
25378 *
25379 * @animations
25380 * | Animation | Occurs |
25381 * |----------------------------------|-------------------------------------|
25382 * | {@link ng.$animate#enter enter} | when the expression changes, on the new include |
25383 * | {@link ng.$animate#leave leave} | when the expression changes, on the old include |
25384 *
25385 * The enter and leave animation occur concurrently.
25386 *
25387 * @scope
25388 * @priority 400
25389 *
25390 * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
25391 * make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
25392 * @param {string=} onload Expression to evaluate when a new partial is loaded.
25393 * <div class="alert alert-warning">
25394 * **Note:** When using onload on SVG elements in IE11, the browser will try to call
25395 * a function with the name on the window element, which will usually throw a
25396 * "function is undefined" error. To fix this, you can instead use `data-onload` or a
25397 * different form that {@link guide/directive#normalization matches} `onload`.
25398 * </div>
25399 *
25400 * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
25401 * $anchorScroll} to scroll the viewport after the content is loaded.
25402 *
25403 * - If the attribute is not set, disable scrolling.
25404 * - If the attribute is set without value, enable scrolling.
25405 * - Otherwise enable scrolling only if the expression evaluates to truthy value.
25406 *
25407 * @example
25408 <example module="includeExample" deps="angular-animate.js" animations="true">
25409 <file name="index.html">
25410 <div ng-controller="ExampleController">
25411 <select ng-model="template" ng-options="t.name for t in templates">
25412 <option value="">(blank)</option>
25413 </select>
25414 url of the template: <code>{{template.url}}</code>
25415 <hr/>
25416 <div class="slide-animate-container">
25417 <div class="slide-animate" ng-include="template.url"></div>
25418 </div>
25419 </div>
25420 </file>
25421 <file name="script.js">
25422 angular.module('includeExample', ['ngAnimate'])
25423 .controller('ExampleController', ['$scope', function($scope) {
25424 $scope.templates =
25425 [ { name: 'template1.html', url: 'template1.html'},
25426 { name: 'template2.html', url: 'template2.html'} ];
25427 $scope.template = $scope.templates[0];
25428 }]);
25429 </file>
25430 <file name="template1.html">
25431 Content of template1.html
25432 </file>
25433 <file name="template2.html">
25434 Content of template2.html
25435 </file>
25436 <file name="animations.css">
25437 .slide-animate-container {
25438 position:relative;
25439 background:white;
25440 border:1px solid black;
25441 height:40px;
25442 overflow:hidden;
25443 }
25444
25445 .slide-animate {
25446 padding:10px;
25447 }
25448
25449 .slide-animate.ng-enter, .slide-animate.ng-leave {
25450 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
25451
25452 position:absolute;
25453 top:0;
25454 left:0;
25455 right:0;
25456 bottom:0;
25457 display:block;
25458 padding:10px;
25459 }
25460
25461 .slide-animate.ng-enter {
25462 top:-50px;
25463 }
25464 .slide-animate.ng-enter.ng-enter-active {
25465 top:0;
25466 }
25467
25468 .slide-animate.ng-leave {
25469 top:0;
25470 }
25471 .slide-animate.ng-leave.ng-leave-active {
25472 top:50px;
25473 }
25474 </file>
25475 <file name="protractor.js" type="protractor">
25476 var templateSelect = element(by.model('template'));
25477 var includeElem = element(by.css('[ng-include]'));
25478
25479 it('should load template1.html', function() {
25480 expect(includeElem.getText()).toMatch(/Content of template1.html/);
25481 });
25482
25483 it('should load template2.html', function() {
25484 if (browser.params.browser == 'firefox') {
25485 // Firefox can't handle using selects
25486 // See https://github.com/angular/protractor/issues/480
25487 return;
25488 }
25489 templateSelect.click();
25490 templateSelect.all(by.css('option')).get(2).click();
25491 expect(includeElem.getText()).toMatch(/Content of template2.html/);
25492 });
25493
25494 it('should change to blank', function() {
25495 if (browser.params.browser == 'firefox') {
25496 // Firefox can't handle using selects
25497 return;
25498 }
25499 templateSelect.click();
25500 templateSelect.all(by.css('option')).get(0).click();
25501 expect(includeElem.isPresent()).toBe(false);
25502 });
25503 </file>
25504 </example>
25505 */
25506
25507
25508/**
25509 * @ngdoc event
25510 * @name ngInclude#$includeContentRequested
25511 * @eventType emit on the scope ngInclude was declared in
25512 * @description
25513 * Emitted every time the ngInclude content is requested.
25514 *
25515 * @param {Object} angularEvent Synthetic event object.
25516 * @param {String} src URL of content to load.
25517 */
25518
25519
25520/**
25521 * @ngdoc event
25522 * @name ngInclude#$includeContentLoaded
25523 * @eventType emit on the current ngInclude scope
25524 * @description
25525 * Emitted every time the ngInclude content is reloaded.
25526 *
25527 * @param {Object} angularEvent Synthetic event object.
25528 * @param {String} src URL of content to load.
25529 */
25530
25531
25532/**
25533 * @ngdoc event
25534 * @name ngInclude#$includeContentError
25535 * @eventType emit on the scope ngInclude was declared in
25536 * @description
25537 * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
25538 *
25539 * @param {Object} angularEvent Synthetic event object.
25540 * @param {String} src URL of content to load.
25541 */
25542var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
25543 function($templateRequest, $anchorScroll, $animate) {
25544 return {
25545 restrict: 'ECA',
25546 priority: 400,
25547 terminal: true,
25548 transclude: 'element',
25549 controller: angular.noop,
25550 compile: function(element, attr) {
25551 var srcExp = attr.ngInclude || attr.src,
25552 onloadExp = attr.onload || '',
25553 autoScrollExp = attr.autoscroll;
25554
25555 return function(scope, $element, $attr, ctrl, $transclude) {
25556 var changeCounter = 0,
25557 currentScope,
25558 previousElement,
25559 currentElement;
25560
25561 var cleanupLastIncludeContent = function() {
25562 if (previousElement) {
25563 previousElement.remove();
25564 previousElement = null;
25565 }
25566 if (currentScope) {
25567 currentScope.$destroy();
25568 currentScope = null;
25569 }
25570 if (currentElement) {
25571 $animate.leave(currentElement).then(function() {
25572 previousElement = null;
25573 });
25574 previousElement = currentElement;
25575 currentElement = null;
25576 }
25577 };
25578
25579 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
25580 var afterAnimation = function() {
25581 if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
25582 $anchorScroll();
25583 }
25584 };
25585 var thisChangeId = ++changeCounter;
25586
25587 if (src) {
25588 //set the 2nd param to true to ignore the template request error so that the inner
25589 //contents and scope can be cleaned up.
25590 $templateRequest(src, true).then(function(response) {
25591 if (scope.$$destroyed) return;
25592
25593 if (thisChangeId !== changeCounter) return;
25594 var newScope = scope.$new();
25595 ctrl.template = response;
25596
25597 // Note: This will also link all children of ng-include that were contained in the original
25598 // html. If that content contains controllers, ... they could pollute/change the scope.
25599 // However, using ng-include on an element with additional content does not make sense...
25600 // Note: We can't remove them in the cloneAttchFn of $transclude as that
25601 // function is called before linking the content, which would apply child
25602 // directives to non existing elements.
25603 var clone = $transclude(newScope, function(clone) {
25604 cleanupLastIncludeContent();
25605 $animate.enter(clone, null, $element).then(afterAnimation);
25606 });
25607
25608 currentScope = newScope;
25609 currentElement = clone;
25610
25611 currentScope.$emit('$includeContentLoaded', src);
25612 scope.$eval(onloadExp);
25613 }, function() {
25614 if (scope.$$destroyed) return;
25615
25616 if (thisChangeId === changeCounter) {
25617 cleanupLastIncludeContent();
25618 scope.$emit('$includeContentError', src);
25619 }
25620 });
25621 scope.$emit('$includeContentRequested', src);
25622 } else {
25623 cleanupLastIncludeContent();
25624 ctrl.template = null;
25625 }
25626 });
25627 };
25628 }
25629 };
25630}];
25631
25632// This directive is called during the $transclude call of the first `ngInclude` directive.
25633// It will replace and compile the content of the element with the loaded template.
25634// We need this directive so that the element content is already filled when
25635// the link function of another directive on the same element as ngInclude
25636// is called.
25637var ngIncludeFillContentDirective = ['$compile',
25638 function($compile) {
25639 return {
25640 restrict: 'ECA',
25641 priority: -400,
25642 require: 'ngInclude',
25643 link: function(scope, $element, $attr, ctrl) {
25644 if (toString.call($element[0]).match(/SVG/)) {
25645 // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
25646 // support innerHTML, so detect this here and try to generate the contents
25647 // specially.
25648 $element.empty();
25649 $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
25650 function namespaceAdaptedClone(clone) {
25651 $element.append(clone);
25652 }, {futureParentElement: $element});
25653 return;
25654 }
25655
25656 $element.html(ctrl.template);
25657 $compile($element.contents())(scope);
25658 }
25659 };
25660 }];
25661
25662/**
25663 * @ngdoc directive
25664 * @name ngInit
25665 * @restrict AC
25666 *
25667 * @description
25668 * The `ngInit` directive allows you to evaluate an expression in the
25669 * current scope.
25670 *
25671 * <div class="alert alert-danger">
25672 * This directive can be abused to add unnecessary amounts of logic into your templates.
25673 * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
25674 * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
25675 * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
25676 * rather than `ngInit` to initialize values on a scope.
25677 * </div>
25678 *
25679 * <div class="alert alert-warning">
25680 * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
25681 * sure you have parentheses to ensure correct operator precedence:
25682 * <pre class="prettyprint">
25683 * `<div ng-init="test1 = ($index | toString)"></div>`
25684 * </pre>
25685 * </div>
25686 *
25687 * @priority 450
25688 *
25689 * @element ANY
25690 * @param {expression} ngInit {@link guide/expression Expression} to eval.
25691 *
25692 * @example
25693 <example module="initExample">
25694 <file name="index.html">
25695 <script>
25696 angular.module('initExample', [])
25697 .controller('ExampleController', ['$scope', function($scope) {
25698 $scope.list = [['a', 'b'], ['c', 'd']];
25699 }]);
25700 </script>
25701 <div ng-controller="ExampleController">
25702 <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
25703 <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
25704 <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
25705 </div>
25706 </div>
25707 </div>
25708 </file>
25709 <file name="protractor.js" type="protractor">
25710 it('should alias index positions', function() {
25711 var elements = element.all(by.css('.example-init'));
25712 expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
25713 expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
25714 expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
25715 expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
25716 });
25717 </file>
25718 </example>
25719 */
25720var ngInitDirective = ngDirective({
25721 priority: 450,
25722 compile: function() {
25723 return {
25724 pre: function(scope, element, attrs) {
25725 scope.$eval(attrs.ngInit);
25726 }
25727 };
25728 }
25729});
25730
25731/**
25732 * @ngdoc directive
25733 * @name ngList
25734 *
25735 * @description
25736 * Text input that converts between a delimited string and an array of strings. The default
25737 * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
25738 * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
25739 *
25740 * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
25741 * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
25742 * list item is respected. This implies that the user of the directive is responsible for
25743 * dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
25744 * tab or newline character.
25745 * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
25746 * when joining the list items back together) and whitespace around each list item is stripped
25747 * before it is added to the model.
25748 *
25749 * ### Example with Validation
25750 *
25751 * <example name="ngList-directive" module="listExample">
25752 * <file name="app.js">
25753 * angular.module('listExample', [])
25754 * .controller('ExampleController', ['$scope', function($scope) {
25755 * $scope.names = ['morpheus', 'neo', 'trinity'];
25756 * }]);
25757 * </file>
25758 * <file name="index.html">
25759 * <form name="myForm" ng-controller="ExampleController">
25760 * <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
25761 * <span role="alert">
25762 * <span class="error" ng-show="myForm.namesInput.$error.required">
25763 * Required!</span>
25764 * </span>
25765 * <br>
25766 * <tt>names = {{names}}</tt><br/>
25767 * <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
25768 * <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
25769 * <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
25770 * <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
25771 * </form>
25772 * </file>
25773 * <file name="protractor.js" type="protractor">
25774 * var listInput = element(by.model('names'));
25775 * var names = element(by.exactBinding('names'));
25776 * var valid = element(by.binding('myForm.namesInput.$valid'));
25777 * var error = element(by.css('span.error'));
25778 *
25779 * it('should initialize to model', function() {
25780 * expect(names.getText()).toContain('["morpheus","neo","trinity"]');
25781 * expect(valid.getText()).toContain('true');
25782 * expect(error.getCssValue('display')).toBe('none');
25783 * });
25784 *
25785 * it('should be invalid if empty', function() {
25786 * listInput.clear();
25787 * listInput.sendKeys('');
25788 *
25789 * expect(names.getText()).toContain('');
25790 * expect(valid.getText()).toContain('false');
25791 * expect(error.getCssValue('display')).not.toBe('none');
25792 * });
25793 * </file>
25794 * </example>
25795 *
25796 * ### Example - splitting on newline
25797 * <example name="ngList-directive-newlines">
25798 * <file name="index.html">
25799 * <textarea ng-model="list" ng-list=" " ng-trim="false"></textarea>
25800 * <pre>{{ list | json }}</pre>
25801 * </file>
25802 * <file name="protractor.js" type="protractor">
25803 * it("should split the text by newlines", function() {
25804 * var listInput = element(by.model('list'));
25805 * var output = element(by.binding('list | json'));
25806 * listInput.sendKeys('abc\ndef\nghi');
25807 * expect(output.getText()).toContain('[\n "abc",\n "def",\n "ghi"\n]');
25808 * });
25809 * </file>
25810 * </example>
25811 *
25812 * @element input
25813 * @param {string=} ngList optional delimiter that should be used to split the value.
25814 */
25815var ngListDirective = function() {
25816 return {
25817 restrict: 'A',
25818 priority: 100,
25819 require: 'ngModel',
25820 link: function(scope, element, attr, ctrl) {
25821 // We want to control whitespace trimming so we use this convoluted approach
25822 // to access the ngList attribute, which doesn't pre-trim the attribute
25823 var ngList = element.attr(attr.$attr.ngList) || ', ';
25824 var trimValues = attr.ngTrim !== 'false';
25825 var separator = trimValues ? trim(ngList) : ngList;
25826
25827 var parse = function(viewValue) {
25828 // If the viewValue is invalid (say required but empty) it will be `undefined`
25829 if (isUndefined(viewValue)) return;
25830
25831 var list = [];
25832
25833 if (viewValue) {
25834 forEach(viewValue.split(separator), function(value) {
25835 if (value) list.push(trimValues ? trim(value) : value);
25836 });
25837 }
25838
25839 return list;
25840 };
25841
25842 ctrl.$parsers.push(parse);
25843 ctrl.$formatters.push(function(value) {
25844 if (isArray(value)) {
25845 return value.join(ngList);
25846 }
25847
25848 return undefined;
25849 });
25850
25851 // Override the standard $isEmpty because an empty array means the input is empty.
25852 ctrl.$isEmpty = function(value) {
25853 return !value || !value.length;
25854 };
25855 }
25856 };
25857};
25858
25859/* global VALID_CLASS: true,
25860 INVALID_CLASS: true,
25861 PRISTINE_CLASS: true,
25862 DIRTY_CLASS: true,
25863 UNTOUCHED_CLASS: true,
25864 TOUCHED_CLASS: true,
25865*/
25866
25867var VALID_CLASS = 'ng-valid',
25868 INVALID_CLASS = 'ng-invalid',
25869 PRISTINE_CLASS = 'ng-pristine',
25870 DIRTY_CLASS = 'ng-dirty',
25871 UNTOUCHED_CLASS = 'ng-untouched',
25872 TOUCHED_CLASS = 'ng-touched',
25873 PENDING_CLASS = 'ng-pending',
25874 EMPTY_CLASS = 'ng-empty',
25875 NOT_EMPTY_CLASS = 'ng-not-empty';
25876
25877var ngModelMinErr = minErr('ngModel');
25878
25879/**
25880 * @ngdoc type
25881 * @name ngModel.NgModelController
25882 *
25883 * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
25884 * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
25885 * is set.
25886 * @property {*} $modelValue The value in the model that the control is bound to.
25887 * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
25888 the control reads value from the DOM. The functions are called in array order, each passing
25889 its return value through to the next. The last return value is forwarded to the
25890 {@link ngModel.NgModelController#$validators `$validators`} collection.
25891
25892Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
25893`$viewValue`}.
25894
25895Returning `undefined` from a parser means a parse error occurred. In that case,
25896no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
25897will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
25898is set to `true`. The parse error is stored in `ngModel.$error.parse`.
25899
25900 *
25901 * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
25902 the model value changes. The functions are called in reverse array order, each passing the value through to the
25903 next. The last return value is used as the actual DOM value.
25904 Used to format / convert values for display in the control.
25905 * ```js
25906 * function formatter(value) {
25907 * if (value) {
25908 * return value.toUpperCase();
25909 * }
25910 * }
25911 * ngModel.$formatters.push(formatter);
25912 * ```
25913 *
25914 * @property {Object.<string, function>} $validators A collection of validators that are applied
25915 * whenever the model value changes. The key value within the object refers to the name of the
25916 * validator while the function refers to the validation operation. The validation operation is
25917 * provided with the model value as an argument and must return a true or false value depending
25918 * on the response of that validation.
25919 *
25920 * ```js
25921 * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
25922 * var value = modelValue || viewValue;
25923 * return /[0-9]+/.test(value) &&
25924 * /[a-z]+/.test(value) &&
25925 * /[A-Z]+/.test(value) &&
25926 * /\W+/.test(value);
25927 * };
25928 * ```
25929 *
25930 * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
25931 * perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
25932 * is expected to return a promise when it is run during the model validation process. Once the promise
25933 * is delivered then the validation status will be set to true when fulfilled and false when rejected.
25934 * When the asynchronous validators are triggered, each of the validators will run in parallel and the model
25935 * value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
25936 * is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
25937 * will only run once all synchronous validators have passed.
25938 *
25939 * Please note that if $http is used then it is important that the server returns a success HTTP response code
25940 * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
25941 *
25942 * ```js
25943 * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
25944 * var value = modelValue || viewValue;
25945 *
25946 * // Lookup user by username
25947 * return $http.get('/api/users/' + value).
25948 * then(function resolved() {
25949 * //username exists, this means validation fails
25950 * return $q.reject('exists');
25951 * }, function rejected() {
25952 * //username does not exist, therefore this validation passes
25953 * return true;
25954 * });
25955 * };
25956 * ```
25957 *
25958 * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
25959 * view value has changed. It is called with no arguments, and its return value is ignored.
25960 * This can be used in place of additional $watches against the model value.
25961 *
25962 * @property {Object} $error An object hash with all failing validator ids as keys.
25963 * @property {Object} $pending An object hash with all pending validator ids as keys.
25964 *
25965 * @property {boolean} $untouched True if control has not lost focus yet.
25966 * @property {boolean} $touched True if control has lost focus.
25967 * @property {boolean} $pristine True if user has not interacted with the control yet.
25968 * @property {boolean} $dirty True if user has already interacted with the control.
25969 * @property {boolean} $valid True if there is no error.
25970 * @property {boolean} $invalid True if at least one error on the control.
25971 * @property {string} $name The name attribute of the control.
25972 *
25973 * @description
25974 *
25975 * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
25976 * The controller contains services for data-binding, validation, CSS updates, and value formatting
25977 * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
25978 * listening to DOM events.
25979 * Such DOM related logic should be provided by other directives which make use of
25980 * `NgModelController` for data-binding to control elements.
25981 * Angular provides this DOM logic for most {@link input `input`} elements.
25982 * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
25983 * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
25984 *
25985 * @example
25986 * ### Custom Control Example
25987 * This example shows how to use `NgModelController` with a custom control to achieve
25988 * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
25989 * collaborate together to achieve the desired result.
25990 *
25991 * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
25992 * contents be edited in place by the user.
25993 *
25994 * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
25995 * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
25996 * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
25997 * that content using the `$sce` service.
25998 *
25999 * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
26000 <file name="style.css">
26001 [contenteditable] {
26002 border: 1px solid black;
26003 background-color: white;
26004 min-height: 20px;
26005 }
26006
26007 .ng-invalid {
26008 border: 1px solid red;
26009 }
26010
26011 </file>
26012 <file name="script.js">
26013 angular.module('customControl', ['ngSanitize']).
26014 directive('contenteditable', ['$sce', function($sce) {
26015 return {
26016 restrict: 'A', // only activate on element attribute
26017 require: '?ngModel', // get a hold of NgModelController
26018 link: function(scope, element, attrs, ngModel) {
26019 if (!ngModel) return; // do nothing if no ng-model
26020
26021 // Specify how UI should be updated
26022 ngModel.$render = function() {
26023 element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
26024 };
26025
26026 // Listen for change events to enable binding
26027 element.on('blur keyup change', function() {
26028 scope.$evalAsync(read);
26029 });
26030 read(); // initialize
26031
26032 // Write data to the model
26033 function read() {
26034 var html = element.html();
26035 // When we clear the content editable the browser leaves a <br> behind
26036 // If strip-br attribute is provided then we strip this out
26037 if ( attrs.stripBr && html == '<br>' ) {
26038 html = '';
26039 }
26040 ngModel.$setViewValue(html);
26041 }
26042 }
26043 };
26044 }]);
26045 </file>
26046 <file name="index.html">
26047 <form name="myForm">
26048 <div contenteditable
26049 name="myWidget" ng-model="userContent"
26050 strip-br="true"
26051 required>Change me!</div>
26052 <span ng-show="myForm.myWidget.$error.required">Required!</span>
26053 <hr>
26054 <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
26055 </form>
26056 </file>
26057 <file name="protractor.js" type="protractor">
26058 it('should data-bind and become invalid', function() {
26059 if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
26060 // SafariDriver can't handle contenteditable
26061 // and Firefox driver can't clear contenteditables very well
26062 return;
26063 }
26064 var contentEditable = element(by.css('[contenteditable]'));
26065 var content = 'Change me!';
26066
26067 expect(contentEditable.getText()).toEqual(content);
26068
26069 contentEditable.clear();
26070 contentEditable.sendKeys(protractor.Key.BACK_SPACE);
26071 expect(contentEditable.getText()).toEqual('');
26072 expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
26073 });
26074 </file>
26075 * </example>
26076 *
26077 *
26078 */
26079var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
26080 function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
26081 this.$viewValue = Number.NaN;
26082 this.$modelValue = Number.NaN;
26083 this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
26084 this.$validators = {};
26085 this.$asyncValidators = {};
26086 this.$parsers = [];
26087 this.$formatters = [];
26088 this.$viewChangeListeners = [];
26089 this.$untouched = true;
26090 this.$touched = false;
26091 this.$pristine = true;
26092 this.$dirty = false;
26093 this.$valid = true;
26094 this.$invalid = false;
26095 this.$error = {}; // keep invalid keys here
26096 this.$$success = {}; // keep valid keys here
26097 this.$pending = undefined; // keep pending keys here
26098 this.$name = $interpolate($attr.name || '', false)($scope);
26099 this.$$parentForm = nullFormCtrl;
26100
26101 var parsedNgModel = $parse($attr.ngModel),
26102 parsedNgModelAssign = parsedNgModel.assign,
26103 ngModelGet = parsedNgModel,
26104 ngModelSet = parsedNgModelAssign,
26105 pendingDebounce = null,
26106 parserValid,
26107 ctrl = this;
26108
26109 this.$$setOptions = function(options) {
26110 ctrl.$options = options;
26111 if (options && options.getterSetter) {
26112 var invokeModelGetter = $parse($attr.ngModel + '()'),
26113 invokeModelSetter = $parse($attr.ngModel + '($$$p)');
26114
26115 ngModelGet = function($scope) {
26116 var modelValue = parsedNgModel($scope);
26117 if (isFunction(modelValue)) {
26118 modelValue = invokeModelGetter($scope);
26119 }
26120 return modelValue;
26121 };
26122 ngModelSet = function($scope, newValue) {
26123 if (isFunction(parsedNgModel($scope))) {
26124 invokeModelSetter($scope, {$$$p: newValue});
26125 } else {
26126 parsedNgModelAssign($scope, newValue);
26127 }
26128 };
26129 } else if (!parsedNgModel.assign) {
26130 throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
26131 $attr.ngModel, startingTag($element));
26132 }
26133 };
26134
26135 /**
26136 * @ngdoc method
26137 * @name ngModel.NgModelController#$render
26138 *
26139 * @description
26140 * Called when the view needs to be updated. It is expected that the user of the ng-model
26141 * directive will implement this method.
26142 *
26143 * The `$render()` method is invoked in the following situations:
26144 *
26145 * * `$rollbackViewValue()` is called. If we are rolling back the view value to the last
26146 * committed value then `$render()` is called to update the input control.
26147 * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
26148 * the `$viewValue` are different from last time.
26149 *
26150 * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
26151 * `$modelValue` and `$viewValue` are actually different from their previous values. If `$modelValue`
26152 * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
26153 * invoked if you only change a property on the objects.
26154 */
26155 this.$render = noop;
26156
26157 /**
26158 * @ngdoc method
26159 * @name ngModel.NgModelController#$isEmpty
26160 *
26161 * @description
26162 * This is called when we need to determine if the value of an input is empty.
26163 *
26164 * For instance, the required directive does this to work out if the input has data or not.
26165 *
26166 * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
26167 *
26168 * You can override this for input directives whose concept of being empty is different from the
26169 * default. The `checkboxInputType` directive does this because in its case a value of `false`
26170 * implies empty.
26171 *
26172 * @param {*} value The value of the input to check for emptiness.
26173 * @returns {boolean} True if `value` is "empty".
26174 */
26175 this.$isEmpty = function(value) {
26176 return isUndefined(value) || value === '' || value === null || value !== value;
26177 };
26178
26179 this.$$updateEmptyClasses = function(value) {
26180 if (ctrl.$isEmpty(value)) {
26181 $animate.removeClass($element, NOT_EMPTY_CLASS);
26182 $animate.addClass($element, EMPTY_CLASS);
26183 } else {
26184 $animate.removeClass($element, EMPTY_CLASS);
26185 $animate.addClass($element, NOT_EMPTY_CLASS);
26186 }
26187 };
26188
26189
26190 var currentValidationRunId = 0;
26191
26192 /**
26193 * @ngdoc method
26194 * @name ngModel.NgModelController#$setValidity
26195 *
26196 * @description
26197 * Change the validity state, and notify the form.
26198 *
26199 * This method can be called within $parsers/$formatters or a custom validation implementation.
26200 * However, in most cases it should be sufficient to use the `ngModel.$validators` and
26201 * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
26202 *
26203 * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
26204 * to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
26205 * (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
26206 * The `validationErrorKey` should be in camelCase and will get converted into dash-case
26207 * for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
26208 * class and can be bound to as `{{someForm.someControl.$error.myError}}` .
26209 * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
26210 * or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
26211 * Skipped is used by Angular when validators do not run because of parse errors and
26212 * when `$asyncValidators` do not run because any of the `$validators` failed.
26213 */
26214 addSetValidityMethod({
26215 ctrl: this,
26216 $element: $element,
26217 set: function(object, property) {
26218 object[property] = true;
26219 },
26220 unset: function(object, property) {
26221 delete object[property];
26222 },
26223 $animate: $animate
26224 });
26225
26226 /**
26227 * @ngdoc method
26228 * @name ngModel.NgModelController#$setPristine
26229 *
26230 * @description
26231 * Sets the control to its pristine state.
26232 *
26233 * This method can be called to remove the `ng-dirty` class and set the control to its pristine
26234 * state (`ng-pristine` class). A model is considered to be pristine when the control
26235 * has not been changed from when first compiled.
26236 */
26237 this.$setPristine = function() {
26238 ctrl.$dirty = false;
26239 ctrl.$pristine = true;
26240 $animate.removeClass($element, DIRTY_CLASS);
26241 $animate.addClass($element, PRISTINE_CLASS);
26242 };
26243
26244 /**
26245 * @ngdoc method
26246 * @name ngModel.NgModelController#$setDirty
26247 *
26248 * @description
26249 * Sets the control to its dirty state.
26250 *
26251 * This method can be called to remove the `ng-pristine` class and set the control to its dirty
26252 * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
26253 * from when first compiled.
26254 */
26255 this.$setDirty = function() {
26256 ctrl.$dirty = true;
26257 ctrl.$pristine = false;
26258 $animate.removeClass($element, PRISTINE_CLASS);
26259 $animate.addClass($element, DIRTY_CLASS);
26260 ctrl.$$parentForm.$setDirty();
26261 };
26262
26263 /**
26264 * @ngdoc method
26265 * @name ngModel.NgModelController#$setUntouched
26266 *
26267 * @description
26268 * Sets the control to its untouched state.
26269 *
26270 * This method can be called to remove the `ng-touched` class and set the control to its
26271 * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
26272 * by default, however this function can be used to restore that state if the model has
26273 * already been touched by the user.
26274 */
26275 this.$setUntouched = function() {
26276 ctrl.$touched = false;
26277 ctrl.$untouched = true;
26278 $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
26279 };
26280
26281 /**
26282 * @ngdoc method
26283 * @name ngModel.NgModelController#$setTouched
26284 *
26285 * @description
26286 * Sets the control to its touched state.
26287 *
26288 * This method can be called to remove the `ng-untouched` class and set the control to its
26289 * touched state (`ng-touched` class). A model is considered to be touched when the user has
26290 * first focused the control element and then shifted focus away from the control (blur event).
26291 */
26292 this.$setTouched = function() {
26293 ctrl.$touched = true;
26294 ctrl.$untouched = false;
26295 $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
26296 };
26297
26298 /**
26299 * @ngdoc method
26300 * @name ngModel.NgModelController#$rollbackViewValue
26301 *
26302 * @description
26303 * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
26304 * which may be caused by a pending debounced event or because the input is waiting for a some
26305 * future event.
26306 *
26307 * If you have an input that uses `ng-model-options` to set up debounced updates or updates that
26308 * depend on special events such as blur, you can have a situation where there is a period when
26309 * the `$viewValue` is out of sync with the ngModel's `$modelValue`.
26310 *
26311 * In this case, you can use `$rollbackViewValue()` to manually cancel the debounced / future update
26312 * and reset the input to the last committed view value.
26313 *
26314 * It is also possible that you run into difficulties if you try to update the ngModel's `$modelValue`
26315 * programmatically before these debounced/future events have resolved/occurred, because Angular's
26316 * dirty checking mechanism is not able to tell whether the model has actually changed or not.
26317 *
26318 * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
26319 * input which may have such events pending. This is important in order to make sure that the
26320 * input field will be updated with the new model value and any pending operations are cancelled.
26321 *
26322 * <example name="ng-model-cancel-update" module="cancel-update-example">
26323 * <file name="app.js">
26324 * angular.module('cancel-update-example', [])
26325 *
26326 * .controller('CancelUpdateController', ['$scope', function($scope) {
26327 * $scope.model = {};
26328 *
26329 * $scope.setEmpty = function(e, value, rollback) {
26330 * if (e.keyCode == 27) {
26331 * e.preventDefault();
26332 * if (rollback) {
26333 * $scope.myForm[value].$rollbackViewValue();
26334 * }
26335 * $scope.model[value] = '';
26336 * }
26337 * };
26338 * }]);
26339 * </file>
26340 * <file name="index.html">
26341 * <div ng-controller="CancelUpdateController">
26342 * <p>Both of these inputs are only updated if they are blurred. Hitting escape should
26343 * empty them. Follow these steps and observe the difference:</p>
26344 * <ol>
26345 * <li>Type something in the input. You will see that the model is not yet updated</li>
26346 * <li>Press the Escape key.
26347 * <ol>
26348 * <li> In the first example, nothing happens, because the model is already '', and no
26349 * update is detected. If you blur the input, the model will be set to the current view.
26350 * </li>
26351 * <li> In the second example, the pending update is cancelled, and the input is set back
26352 * to the last committed view value (''). Blurring the input does nothing.
26353 * </li>
26354 * </ol>
26355 * </li>
26356 * </ol>
26357 *
26358 * <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
26359 * <div>
26360 * <p id="inputDescription1">Without $rollbackViewValue():</p>
26361 * <input name="value1" aria-describedby="inputDescription1" ng-model="model.value1"
26362 * ng-keydown="setEmpty($event, 'value1')">
26363 * value1: "{{ model.value1 }}"
26364 * </div>
26365 *
26366 * <div>
26367 * <p id="inputDescription2">With $rollbackViewValue():</p>
26368 * <input name="value2" aria-describedby="inputDescription2" ng-model="model.value2"
26369 * ng-keydown="setEmpty($event, 'value2', true)">
26370 * value2: "{{ model.value2 }}"
26371 * </div>
26372 * </form>
26373 * </div>
26374 * </file>
26375 <file name="style.css">
26376 div {
26377 display: table-cell;
26378 }
26379 div:nth-child(1) {
26380 padding-right: 30px;
26381 }
26382
26383 </file>
26384 * </example>
26385 */
26386 this.$rollbackViewValue = function() {
26387 $timeout.cancel(pendingDebounce);
26388 ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
26389 ctrl.$render();
26390 };
26391
26392 /**
26393 * @ngdoc method
26394 * @name ngModel.NgModelController#$validate
26395 *
26396 * @description
26397 * Runs each of the registered validators (first synchronous validators and then
26398 * asynchronous validators).
26399 * If the validity changes to invalid, the model will be set to `undefined`,
26400 * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
26401 * If the validity changes to valid, it will set the model to the last available valid
26402 * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
26403 */
26404 this.$validate = function() {
26405 // ignore $validate before model is initialized
26406 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
26407 return;
26408 }
26409
26410 var viewValue = ctrl.$$lastCommittedViewValue;
26411 // Note: we use the $$rawModelValue as $modelValue might have been
26412 // set to undefined during a view -> model update that found validation
26413 // errors. We can't parse the view here, since that could change
26414 // the model although neither viewValue nor the model on the scope changed
26415 var modelValue = ctrl.$$rawModelValue;
26416
26417 var prevValid = ctrl.$valid;
26418 var prevModelValue = ctrl.$modelValue;
26419
26420 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
26421
26422 ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
26423 // If there was no change in validity, don't update the model
26424 // This prevents changing an invalid modelValue to undefined
26425 if (!allowInvalid && prevValid !== allValid) {
26426 // Note: Don't check ctrl.$valid here, as we could have
26427 // external validators (e.g. calculated on the server),
26428 // that just call $setValidity and need the model value
26429 // to calculate their validity.
26430 ctrl.$modelValue = allValid ? modelValue : undefined;
26431
26432 if (ctrl.$modelValue !== prevModelValue) {
26433 ctrl.$$writeModelToScope();
26434 }
26435 }
26436 });
26437
26438 };
26439
26440 this.$$runValidators = function(modelValue, viewValue, doneCallback) {
26441 currentValidationRunId++;
26442 var localValidationRunId = currentValidationRunId;
26443
26444 // check parser error
26445 if (!processParseErrors()) {
26446 validationDone(false);
26447 return;
26448 }
26449 if (!processSyncValidators()) {
26450 validationDone(false);
26451 return;
26452 }
26453 processAsyncValidators();
26454
26455 function processParseErrors() {
26456 var errorKey = ctrl.$$parserName || 'parse';
26457 if (isUndefined(parserValid)) {
26458 setValidity(errorKey, null);
26459 } else {
26460 if (!parserValid) {
26461 forEach(ctrl.$validators, function(v, name) {
26462 setValidity(name, null);
26463 });
26464 forEach(ctrl.$asyncValidators, function(v, name) {
26465 setValidity(name, null);
26466 });
26467 }
26468 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
26469 setValidity(errorKey, parserValid);
26470 return parserValid;
26471 }
26472 return true;
26473 }
26474
26475 function processSyncValidators() {
26476 var syncValidatorsValid = true;
26477 forEach(ctrl.$validators, function(validator, name) {
26478 var result = validator(modelValue, viewValue);
26479 syncValidatorsValid = syncValidatorsValid && result;
26480 setValidity(name, result);
26481 });
26482 if (!syncValidatorsValid) {
26483 forEach(ctrl.$asyncValidators, function(v, name) {
26484 setValidity(name, null);
26485 });
26486 return false;
26487 }
26488 return true;
26489 }
26490
26491 function processAsyncValidators() {
26492 var validatorPromises = [];
26493 var allValid = true;
26494 forEach(ctrl.$asyncValidators, function(validator, name) {
26495 var promise = validator(modelValue, viewValue);
26496 if (!isPromiseLike(promise)) {
26497 throw ngModelMinErr('nopromise',
26498 "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
26499 }
26500 setValidity(name, undefined);
26501 validatorPromises.push(promise.then(function() {
26502 setValidity(name, true);
26503 }, function() {
26504 allValid = false;
26505 setValidity(name, false);
26506 }));
26507 });
26508 if (!validatorPromises.length) {
26509 validationDone(true);
26510 } else {
26511 $q.all(validatorPromises).then(function() {
26512 validationDone(allValid);
26513 }, noop);
26514 }
26515 }
26516
26517 function setValidity(name, isValid) {
26518 if (localValidationRunId === currentValidationRunId) {
26519 ctrl.$setValidity(name, isValid);
26520 }
26521 }
26522
26523 function validationDone(allValid) {
26524 if (localValidationRunId === currentValidationRunId) {
26525
26526 doneCallback(allValid);
26527 }
26528 }
26529 };
26530
26531 /**
26532 * @ngdoc method
26533 * @name ngModel.NgModelController#$commitViewValue
26534 *
26535 * @description
26536 * Commit a pending update to the `$modelValue`.
26537 *
26538 * Updates may be pending by a debounced event or because the input is waiting for a some future
26539 * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
26540 * usually handles calling this in response to input events.
26541 */
26542 this.$commitViewValue = function() {
26543 var viewValue = ctrl.$viewValue;
26544
26545 $timeout.cancel(pendingDebounce);
26546
26547 // If the view value has not changed then we should just exit, except in the case where there is
26548 // a native validator on the element. In this case the validation state may have changed even though
26549 // the viewValue has stayed empty.
26550 if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
26551 return;
26552 }
26553 ctrl.$$updateEmptyClasses(viewValue);
26554 ctrl.$$lastCommittedViewValue = viewValue;
26555
26556 // change to dirty
26557 if (ctrl.$pristine) {
26558 this.$setDirty();
26559 }
26560 this.$$parseAndValidate();
26561 };
26562
26563 this.$$parseAndValidate = function() {
26564 var viewValue = ctrl.$$lastCommittedViewValue;
26565 var modelValue = viewValue;
26566 parserValid = isUndefined(modelValue) ? undefined : true;
26567
26568 if (parserValid) {
26569 for (var i = 0; i < ctrl.$parsers.length; i++) {
26570 modelValue = ctrl.$parsers[i](modelValue);
26571 if (isUndefined(modelValue)) {
26572 parserValid = false;
26573 break;
26574 }
26575 }
26576 }
26577 if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
26578 // ctrl.$modelValue has not been touched yet...
26579 ctrl.$modelValue = ngModelGet($scope);
26580 }
26581 var prevModelValue = ctrl.$modelValue;
26582 var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
26583 ctrl.$$rawModelValue = modelValue;
26584
26585 if (allowInvalid) {
26586 ctrl.$modelValue = modelValue;
26587 writeToModelIfNeeded();
26588 }
26589
26590 // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
26591 // This can happen if e.g. $setViewValue is called from inside a parser
26592 ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
26593 if (!allowInvalid) {
26594 // Note: Don't check ctrl.$valid here, as we could have
26595 // external validators (e.g. calculated on the server),
26596 // that just call $setValidity and need the model value
26597 // to calculate their validity.
26598 ctrl.$modelValue = allValid ? modelValue : undefined;
26599 writeToModelIfNeeded();
26600 }
26601 });
26602
26603 function writeToModelIfNeeded() {
26604 if (ctrl.$modelValue !== prevModelValue) {
26605 ctrl.$$writeModelToScope();
26606 }
26607 }
26608 };
26609
26610 this.$$writeModelToScope = function() {
26611 ngModelSet($scope, ctrl.$modelValue);
26612 forEach(ctrl.$viewChangeListeners, function(listener) {
26613 try {
26614 listener();
26615 } catch (e) {
26616 $exceptionHandler(e);
26617 }
26618 });
26619 };
26620
26621 /**
26622 * @ngdoc method
26623 * @name ngModel.NgModelController#$setViewValue
26624 *
26625 * @description
26626 * Update the view value.
26627 *
26628 * This method should be called when a control wants to change the view value; typically,
26629 * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
26630 * directive calls it when the value of the input changes and {@link ng.directive:select select}
26631 * calls it when an option is selected.
26632 *
26633 * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
26634 * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
26635 * value sent directly for processing, finally to be applied to `$modelValue` and then the
26636 * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
26637 * in the `$viewChangeListeners` list, are called.
26638 *
26639 * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
26640 * and the `default` trigger is not listed, all those actions will remain pending until one of the
26641 * `updateOn` events is triggered on the DOM element.
26642 * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
26643 * directive is used with a custom debounce for this particular event.
26644 * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
26645 * is specified, once the timer runs out.
26646 *
26647 * When used with standard inputs, the view value will always be a string (which is in some cases
26648 * parsed into another type, such as a `Date` object for `input[date]`.)
26649 * However, custom controls might also pass objects to this method. In this case, we should make
26650 * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
26651 * perform a deep watch of objects, it only looks for a change of identity. If you only change
26652 * the property of the object then ngModel will not realize that the object has changed and
26653 * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
26654 * not change properties of the copy once it has been passed to `$setViewValue`.
26655 * Otherwise you may cause the model value on the scope to change incorrectly.
26656 *
26657 * <div class="alert alert-info">
26658 * In any case, the value passed to the method should always reflect the current value
26659 * of the control. For example, if you are calling `$setViewValue` for an input element,
26660 * you should pass the input DOM value. Otherwise, the control and the scope model become
26661 * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
26662 * the control's DOM value in any way. If we want to change the control's DOM value
26663 * programmatically, we should update the `ngModel` scope expression. Its new value will be
26664 * picked up by the model controller, which will run it through the `$formatters`, `$render` it
26665 * to update the DOM, and finally call `$validate` on it.
26666 * </div>
26667 *
26668 * @param {*} value value from the view.
26669 * @param {string} trigger Event that triggered the update.
26670 */
26671 this.$setViewValue = function(value, trigger) {
26672 ctrl.$viewValue = value;
26673 if (!ctrl.$options || ctrl.$options.updateOnDefault) {
26674 ctrl.$$debounceViewValueCommit(trigger);
26675 }
26676 };
26677
26678 this.$$debounceViewValueCommit = function(trigger) {
26679 var debounceDelay = 0,
26680 options = ctrl.$options,
26681 debounce;
26682
26683 if (options && isDefined(options.debounce)) {
26684 debounce = options.debounce;
26685 if (isNumber(debounce)) {
26686 debounceDelay = debounce;
26687 } else if (isNumber(debounce[trigger])) {
26688 debounceDelay = debounce[trigger];
26689 } else if (isNumber(debounce['default'])) {
26690 debounceDelay = debounce['default'];
26691 }
26692 }
26693
26694 $timeout.cancel(pendingDebounce);
26695 if (debounceDelay) {
26696 pendingDebounce = $timeout(function() {
26697 ctrl.$commitViewValue();
26698 }, debounceDelay);
26699 } else if ($rootScope.$$phase) {
26700 ctrl.$commitViewValue();
26701 } else {
26702 $scope.$apply(function() {
26703 ctrl.$commitViewValue();
26704 });
26705 }
26706 };
26707
26708 // model -> value
26709 // Note: we cannot use a normal scope.$watch as we want to detect the following:
26710 // 1. scope value is 'a'
26711 // 2. user enters 'b'
26712 // 3. ng-change kicks in and reverts scope value to 'a'
26713 // -> scope value did not change since the last digest as
26714 // ng-change executes in apply phase
26715 // 4. view should be changed back to 'a'
26716 $scope.$watch(function ngModelWatch() {
26717 var modelValue = ngModelGet($scope);
26718
26719 // if scope model value and ngModel value are out of sync
26720 // TODO(perf): why not move this to the action fn?
26721 if (modelValue !== ctrl.$modelValue &&
26722 // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
26723 (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
26724 ) {
26725 ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
26726 parserValid = undefined;
26727
26728 var formatters = ctrl.$formatters,
26729 idx = formatters.length;
26730
26731 var viewValue = modelValue;
26732 while (idx--) {
26733 viewValue = formatters[idx](viewValue);
26734 }
26735 if (ctrl.$viewValue !== viewValue) {
26736 ctrl.$$updateEmptyClasses(viewValue);
26737 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
26738 ctrl.$render();
26739
26740 ctrl.$$runValidators(modelValue, viewValue, noop);
26741 }
26742 }
26743
26744 return modelValue;
26745 });
26746}];
26747
26748
26749/**
26750 * @ngdoc directive
26751 * @name ngModel
26752 *
26753 * @element input
26754 * @priority 1
26755 *
26756 * @description
26757 * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
26758 * property on the scope using {@link ngModel.NgModelController NgModelController},
26759 * which is created and exposed by this directive.
26760 *
26761 * `ngModel` is responsible for:
26762 *
26763 * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
26764 * require.
26765 * - Providing validation behavior (i.e. required, number, email, url).
26766 * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
26767 * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`,
26768 * `ng-untouched`, `ng-empty`, `ng-not-empty`) including animations.
26769 * - Registering the control with its parent {@link ng.directive:form form}.
26770 *
26771 * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
26772 * current scope. If the property doesn't already exist on this scope, it will be created
26773 * implicitly and added to the scope.
26774 *
26775 * For best practices on using `ngModel`, see:
26776 *
26777 * - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
26778 *
26779 * For basic examples, how to use `ngModel`, see:
26780 *
26781 * - {@link ng.directive:input input}
26782 * - {@link input[text] text}
26783 * - {@link input[checkbox] checkbox}
26784 * - {@link input[radio] radio}
26785 * - {@link input[number] number}
26786 * - {@link input[email] email}
26787 * - {@link input[url] url}
26788 * - {@link input[date] date}
26789 * - {@link input[datetime-local] datetime-local}
26790 * - {@link input[time] time}
26791 * - {@link input[month] month}
26792 * - {@link input[week] week}
26793 * - {@link ng.directive:select select}
26794 * - {@link ng.directive:textarea textarea}
26795 *
26796 * # Complex Models (objects or collections)
26797 *
26798 * By default, `ngModel` watches the model by reference, not value. This is important to know when
26799 * binding inputs to models that are objects (e.g. `Date`) or collections (e.g. arrays). If only properties of the
26800 * object or collection change, `ngModel` will not be notified and so the input will not be re-rendered.
26801 *
26802 * The model must be assigned an entirely new object or collection before a re-rendering will occur.
26803 *
26804 * Some directives have options that will cause them to use a custom `$watchCollection` on the model expression
26805 * - for example, `ngOptions` will do so when a `track by` clause is included in the comprehension expression or
26806 * if the select is given the `multiple` attribute.
26807 *
26808 * The `$watchCollection()` method only does a shallow comparison, meaning that changing properties deeper than the
26809 * first level of the object (or only changing the properties of an item in the collection if it's an array) will still
26810 * not trigger a re-rendering of the model.
26811 *
26812 * # CSS classes
26813 * The following CSS classes are added and removed on the associated input/select/textarea element
26814 * depending on the validity of the model.
26815 *
26816 * - `ng-valid`: the model is valid
26817 * - `ng-invalid`: the model is invalid
26818 * - `ng-valid-[key]`: for each valid key added by `$setValidity`
26819 * - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
26820 * - `ng-pristine`: the control hasn't been interacted with yet
26821 * - `ng-dirty`: the control has been interacted with
26822 * - `ng-touched`: the control has been blurred
26823 * - `ng-untouched`: the control hasn't been blurred
26824 * - `ng-pending`: any `$asyncValidators` are unfulfilled
26825 * - `ng-empty`: the view does not contain a value or the value is deemed "empty", as defined
26826 * by the {@link ngModel.NgModelController#$isEmpty} method
26827 * - `ng-not-empty`: the view contains a non-empty value
26828 *
26829 * Keep in mind that ngAnimate can detect each of these classes when added and removed.
26830 *
26831 * ## Animation Hooks
26832 *
26833 * Animations within models are triggered when any of the associated CSS classes are added and removed
26834 * on the input element which is attached to the model. These classes include: `.ng-pristine`, `.ng-dirty`,
26835 * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
26836 * The animations that are triggered within ngModel are similar to how they work in ngClass and
26837 * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
26838 *
26839 * The following example shows a simple way to utilize CSS transitions to style an input element
26840 * that has been rendered as invalid after it has been validated:
26841 *
26842 * <pre>
26843 * //be sure to include ngAnimate as a module to hook into more
26844 * //advanced animations
26845 * .my-input {
26846 * transition:0.5s linear all;
26847 * background: white;
26848 * }
26849 * .my-input.ng-invalid {
26850 * background: red;
26851 * color:white;
26852 * }
26853 * </pre>
26854 *
26855 * @example
26856 * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
26857 <file name="index.html">
26858 <script>
26859 angular.module('inputExample', [])
26860 .controller('ExampleController', ['$scope', function($scope) {
26861 $scope.val = '1';
26862 }]);
26863 </script>
26864 <style>
26865 .my-input {
26866 transition:all linear 0.5s;
26867 background: transparent;
26868 }
26869 .my-input.ng-invalid {
26870 color:white;
26871 background: red;
26872 }
26873 </style>
26874 <p id="inputDescription">
26875 Update input to see transitions when valid/invalid.
26876 Integer is a valid value.
26877 </p>
26878 <form name="testForm" ng-controller="ExampleController">
26879 <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
26880 aria-describedby="inputDescription" />
26881 </form>
26882 </file>
26883 * </example>
26884 *
26885 * ## Binding to a getter/setter
26886 *
26887 * Sometimes it's helpful to bind `ngModel` to a getter/setter function. A getter/setter is a
26888 * function that returns a representation of the model when called with zero arguments, and sets
26889 * the internal state of a model when called with an argument. It's sometimes useful to use this
26890 * for models that have an internal representation that's different from what the model exposes
26891 * to the view.
26892 *
26893 * <div class="alert alert-success">
26894 * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
26895 * frequently than other parts of your code.
26896 * </div>
26897 *
26898 * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
26899 * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
26900 * a `<form>`, which will enable this behavior for all `<input>`s within it. See
26901 * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
26902 *
26903 * The following example shows how to use `ngModel` with a getter/setter:
26904 *
26905 * @example
26906 * <example name="ngModel-getter-setter" module="getterSetterExample">
26907 <file name="index.html">
26908 <div ng-controller="ExampleController">
26909 <form name="userForm">
26910 <label>Name:
26911 <input type="text" name="userName"
26912 ng-model="user.name"
26913 ng-model-options="{ getterSetter: true }" />
26914 </label>
26915 </form>
26916 <pre>user.name = <span ng-bind="user.name()"></span></pre>
26917 </div>
26918 </file>
26919 <file name="app.js">
26920 angular.module('getterSetterExample', [])
26921 .controller('ExampleController', ['$scope', function($scope) {
26922 var _name = 'Brian';
26923 $scope.user = {
26924 name: function(newName) {
26925 // Note that newName can be undefined for two reasons:
26926 // 1. Because it is called as a getter and thus called with no arguments
26927 // 2. Because the property should actually be set to undefined. This happens e.g. if the
26928 // input is invalid
26929 return arguments.length ? (_name = newName) : _name;
26930 }
26931 };
26932 }]);
26933 </file>
26934 * </example>
26935 */
26936var ngModelDirective = ['$rootScope', function($rootScope) {
26937 return {
26938 restrict: 'A',
26939 require: ['ngModel', '^?form', '^?ngModelOptions'],
26940 controller: NgModelController,
26941 // Prelink needs to run before any input directive
26942 // so that we can set the NgModelOptions in NgModelController
26943 // before anyone else uses it.
26944 priority: 1,
26945 compile: function ngModelCompile(element) {
26946 // Setup initial state of the control
26947 element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
26948
26949 return {
26950 pre: function ngModelPreLink(scope, element, attr, ctrls) {
26951 var modelCtrl = ctrls[0],
26952 formCtrl = ctrls[1] || modelCtrl.$$parentForm;
26953
26954 modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
26955
26956 // notify others, especially parent forms
26957 formCtrl.$addControl(modelCtrl);
26958
26959 attr.$observe('name', function(newValue) {
26960 if (modelCtrl.$name !== newValue) {
26961 modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
26962 }
26963 });
26964
26965 scope.$on('$destroy', function() {
26966 modelCtrl.$$parentForm.$removeControl(modelCtrl);
26967 });
26968 },
26969 post: function ngModelPostLink(scope, element, attr, ctrls) {
26970 var modelCtrl = ctrls[0];
26971 if (modelCtrl.$options && modelCtrl.$options.updateOn) {
26972 element.on(modelCtrl.$options.updateOn, function(ev) {
26973 modelCtrl.$$debounceViewValueCommit(ev && ev.type);
26974 });
26975 }
26976
26977 element.on('blur', function() {
26978 if (modelCtrl.$touched) return;
26979
26980 if ($rootScope.$$phase) {
26981 scope.$evalAsync(modelCtrl.$setTouched);
26982 } else {
26983 scope.$apply(modelCtrl.$setTouched);
26984 }
26985 });
26986 }
26987 };
26988 }
26989 };
26990}];
26991
26992var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
26993
26994/**
26995 * @ngdoc directive
26996 * @name ngModelOptions
26997 *
26998 * @description
26999 * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
27000 * events that will trigger a model update and/or a debouncing delay so that the actual update only
27001 * takes place when a timer expires; this timer will be reset after another change takes place.
27002 *
27003 * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
27004 * be different from the value in the actual model. This means that if you update the model you
27005 * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
27006 * order to make sure it is synchronized with the model and that any debounced action is canceled.
27007 *
27008 * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
27009 * method is by making sure the input is placed inside a form that has a `name` attribute. This is
27010 * important because `form` controllers are published to the related scope under the name in their
27011 * `name` attribute.
27012 *
27013 * Any pending changes will take place immediately when an enclosing form is submitted via the
27014 * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
27015 * to have access to the updated model.
27016 *
27017 * `ngModelOptions` has an effect on the element it's declared on and its descendants.
27018 *
27019 * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
27020 * - `updateOn`: string specifying which event should the input be bound to. You can set several
27021 * events using an space delimited list. There is a special event called `default` that
27022 * matches the default events belonging of the control.
27023 * - `debounce`: integer value which contains the debounce model update value in milliseconds. A
27024 * value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
27025 * custom value for each event. For example:
27026 * `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
27027 * - `allowInvalid`: boolean value which indicates that the model can be set with values that did
27028 * not validate correctly instead of the default behavior of setting the model to undefined.
27029 * - `getterSetter`: boolean value which determines whether or not to treat functions bound to
27030 `ngModel` as getters/setters.
27031 * - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
27032 * `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
27033 * continental US time zone abbreviations, but for general use, use a time zone offset, for
27034 * example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
27035 * If not specified, the timezone of the browser will be used.
27036 *
27037 * @example
27038
27039 The following example shows how to override immediate updates. Changes on the inputs within the
27040 form will update the model only when the control loses focus (blur event). If `escape` key is
27041 pressed while the input field is focused, the value is reset to the value in the current model.
27042
27043 <example name="ngModelOptions-directive-blur" module="optionsExample">
27044 <file name="index.html">
27045 <div ng-controller="ExampleController">
27046 <form name="userForm">
27047 <label>Name:
27048 <input type="text" name="userName"
27049 ng-model="user.name"
27050 ng-model-options="{ updateOn: 'blur' }"
27051 ng-keyup="cancel($event)" />
27052 </label><br />
27053 <label>Other data:
27054 <input type="text" ng-model="user.data" />
27055 </label><br />
27056 </form>
27057 <pre>user.name = <span ng-bind="user.name"></span></pre>
27058 <pre>user.data = <span ng-bind="user.data"></span></pre>
27059 </div>
27060 </file>
27061 <file name="app.js">
27062 angular.module('optionsExample', [])
27063 .controller('ExampleController', ['$scope', function($scope) {
27064 $scope.user = { name: 'John', data: '' };
27065
27066 $scope.cancel = function(e) {
27067 if (e.keyCode == 27) {
27068 $scope.userForm.userName.$rollbackViewValue();
27069 }
27070 };
27071 }]);
27072 </file>
27073 <file name="protractor.js" type="protractor">
27074 var model = element(by.binding('user.name'));
27075 var input = element(by.model('user.name'));
27076 var other = element(by.model('user.data'));
27077
27078 it('should allow custom events', function() {
27079 input.sendKeys(' Doe');
27080 input.click();
27081 expect(model.getText()).toEqual('John');
27082 other.click();
27083 expect(model.getText()).toEqual('John Doe');
27084 });
27085
27086 it('should $rollbackViewValue when model changes', function() {
27087 input.sendKeys(' Doe');
27088 expect(input.getAttribute('value')).toEqual('John Doe');
27089 input.sendKeys(protractor.Key.ESCAPE);
27090 expect(input.getAttribute('value')).toEqual('John');
27091 other.click();
27092 expect(model.getText()).toEqual('John');
27093 });
27094 </file>
27095 </example>
27096
27097 This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
27098 If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
27099
27100 <example name="ngModelOptions-directive-debounce" module="optionsExample">
27101 <file name="index.html">
27102 <div ng-controller="ExampleController">
27103 <form name="userForm">
27104 <label>Name:
27105 <input type="text" name="userName"
27106 ng-model="user.name"
27107 ng-model-options="{ debounce: 1000 }" />
27108 </label>
27109 <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
27110 <br />
27111 </form>
27112 <pre>user.name = <span ng-bind="user.name"></span></pre>
27113 </div>
27114 </file>
27115 <file name="app.js">
27116 angular.module('optionsExample', [])
27117 .controller('ExampleController', ['$scope', function($scope) {
27118 $scope.user = { name: 'Igor' };
27119 }]);
27120 </file>
27121 </example>
27122
27123 This one shows how to bind to getter/setters:
27124
27125 <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
27126 <file name="index.html">
27127 <div ng-controller="ExampleController">
27128 <form name="userForm">
27129 <label>Name:
27130 <input type="text" name="userName"
27131 ng-model="user.name"
27132 ng-model-options="{ getterSetter: true }" />
27133 </label>
27134 </form>
27135 <pre>user.name = <span ng-bind="user.name()"></span></pre>
27136 </div>
27137 </file>
27138 <file name="app.js">
27139 angular.module('getterSetterExample', [])
27140 .controller('ExampleController', ['$scope', function($scope) {
27141 var _name = 'Brian';
27142 $scope.user = {
27143 name: function(newName) {
27144 // Note that newName can be undefined for two reasons:
27145 // 1. Because it is called as a getter and thus called with no arguments
27146 // 2. Because the property should actually be set to undefined. This happens e.g. if the
27147 // input is invalid
27148 return arguments.length ? (_name = newName) : _name;
27149 }
27150 };
27151 }]);
27152 </file>
27153 </example>
27154 */
27155var ngModelOptionsDirective = function() {
27156 return {
27157 restrict: 'A',
27158 controller: ['$scope', '$attrs', function($scope, $attrs) {
27159 var that = this;
27160 this.$options = copy($scope.$eval($attrs.ngModelOptions));
27161 // Allow adding/overriding bound events
27162 if (isDefined(this.$options.updateOn)) {
27163 this.$options.updateOnDefault = false;
27164 // extract "default" pseudo-event from list of events that can trigger a model update
27165 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
27166 that.$options.updateOnDefault = true;
27167 return ' ';
27168 }));
27169 } else {
27170 this.$options.updateOnDefault = true;
27171 }
27172 }]
27173 };
27174};
27175
27176
27177
27178// helper methods
27179function addSetValidityMethod(context) {
27180 var ctrl = context.ctrl,
27181 $element = context.$element,
27182 classCache = {},
27183 set = context.set,
27184 unset = context.unset,
27185 $animate = context.$animate;
27186
27187 classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
27188
27189 ctrl.$setValidity = setValidity;
27190
27191 function setValidity(validationErrorKey, state, controller) {
27192 if (isUndefined(state)) {
27193 createAndSet('$pending', validationErrorKey, controller);
27194 } else {
27195 unsetAndCleanup('$pending', validationErrorKey, controller);
27196 }
27197 if (!isBoolean(state)) {
27198 unset(ctrl.$error, validationErrorKey, controller);
27199 unset(ctrl.$$success, validationErrorKey, controller);
27200 } else {
27201 if (state) {
27202 unset(ctrl.$error, validationErrorKey, controller);
27203 set(ctrl.$$success, validationErrorKey, controller);
27204 } else {
27205 set(ctrl.$error, validationErrorKey, controller);
27206 unset(ctrl.$$success, validationErrorKey, controller);
27207 }
27208 }
27209 if (ctrl.$pending) {
27210 cachedToggleClass(PENDING_CLASS, true);
27211 ctrl.$valid = ctrl.$invalid = undefined;
27212 toggleValidationCss('', null);
27213 } else {
27214 cachedToggleClass(PENDING_CLASS, false);
27215 ctrl.$valid = isObjectEmpty(ctrl.$error);
27216 ctrl.$invalid = !ctrl.$valid;
27217 toggleValidationCss('', ctrl.$valid);
27218 }
27219
27220 // re-read the state as the set/unset methods could have
27221 // combined state in ctrl.$error[validationError] (used for forms),
27222 // where setting/unsetting only increments/decrements the value,
27223 // and does not replace it.
27224 var combinedState;
27225 if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
27226 combinedState = undefined;
27227 } else if (ctrl.$error[validationErrorKey]) {
27228 combinedState = false;
27229 } else if (ctrl.$$success[validationErrorKey]) {
27230 combinedState = true;
27231 } else {
27232 combinedState = null;
27233 }
27234
27235 toggleValidationCss(validationErrorKey, combinedState);
27236 ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
27237 }
27238
27239 function createAndSet(name, value, controller) {
27240 if (!ctrl[name]) {
27241 ctrl[name] = {};
27242 }
27243 set(ctrl[name], value, controller);
27244 }
27245
27246 function unsetAndCleanup(name, value, controller) {
27247 if (ctrl[name]) {
27248 unset(ctrl[name], value, controller);
27249 }
27250 if (isObjectEmpty(ctrl[name])) {
27251 ctrl[name] = undefined;
27252 }
27253 }
27254
27255 function cachedToggleClass(className, switchValue) {
27256 if (switchValue && !classCache[className]) {
27257 $animate.addClass($element, className);
27258 classCache[className] = true;
27259 } else if (!switchValue && classCache[className]) {
27260 $animate.removeClass($element, className);
27261 classCache[className] = false;
27262 }
27263 }
27264
27265 function toggleValidationCss(validationErrorKey, isValid) {
27266 validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
27267
27268 cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
27269 cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
27270 }
27271}
27272
27273function isObjectEmpty(obj) {
27274 if (obj) {
27275 for (var prop in obj) {
27276 if (obj.hasOwnProperty(prop)) {
27277 return false;
27278 }
27279 }
27280 }
27281 return true;
27282}
27283
27284/**
27285 * @ngdoc directive
27286 * @name ngNonBindable
27287 * @restrict AC
27288 * @priority 1000
27289 *
27290 * @description
27291 * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
27292 * DOM element. This is useful if the element contains what appears to be Angular directives and
27293 * bindings but which should be ignored by Angular. This could be the case if you have a site that
27294 * displays snippets of code, for instance.
27295 *
27296 * @element ANY
27297 *
27298 * @example
27299 * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
27300 * but the one wrapped in `ngNonBindable` is left alone.
27301 *
27302 * @example
27303 <example>
27304 <file name="index.html">
27305 <div>Normal: {{1 + 2}}</div>
27306 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
27307 </file>
27308 <file name="protractor.js" type="protractor">
27309 it('should check ng-non-bindable', function() {
27310 expect(element(by.binding('1 + 2')).getText()).toContain('3');
27311 expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
27312 });
27313 </file>
27314 </example>
27315 */
27316var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
27317
27318/* global jqLiteRemove */
27319
27320var ngOptionsMinErr = minErr('ngOptions');
27321
27322/**
27323 * @ngdoc directive
27324 * @name ngOptions
27325 * @restrict A
27326 *
27327 * @description
27328 *
27329 * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
27330 * elements for the `<select>` element using the array or object obtained by evaluating the
27331 * `ngOptions` comprehension expression.
27332 *
27333 * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
27334 * similar result. However, `ngOptions` provides some benefits such as reducing memory and
27335 * increasing speed by not creating a new scope for each repeated instance, as well as providing
27336 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
27337 * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
27338 * to a non-string value. This is because an option element can only be bound to string values at
27339 * present.
27340 *
27341 * When an item in the `<select>` menu is selected, the array element or object property
27342 * represented by the selected option will be bound to the model identified by the `ngModel`
27343 * directive.
27344 *
27345 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
27346 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
27347 * option. See example below for demonstration.
27348 *
27349 * ## Complex Models (objects or collections)
27350 *
27351 * By default, `ngModel` watches the model by reference, not value. This is important to know when
27352 * binding the select to a model that is an object or a collection.
27353 *
27354 * One issue occurs if you want to preselect an option. For example, if you set
27355 * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
27356 * because the objects are not identical. So by default, you should always reference the item in your collection
27357 * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
27358 *
27359 * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
27360 * of the item not by reference, but by the result of the `track by` expression. For example, if your
27361 * collection items have an id property, you would `track by item.id`.
27362 *
27363 * A different issue with objects or collections is that ngModel won't detect if an object property or
27364 * a collection item changes. For that reason, `ngOptions` additionally watches the model using
27365 * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
27366 * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
27367 * has not changed identity, but only a property on the object or an item in the collection changes.
27368 *
27369 * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
27370 * if the model is an array). This means that changing a property deeper than the first level inside the
27371 * object/collection will not trigger a re-rendering.
27372 *
27373 * ## `select` **`as`**
27374 *
27375 * Using `select` **`as`** will bind the result of the `select` expression to the model, but
27376 * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
27377 * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
27378 * is used, the result of that expression will be set as the value of the `option` and `select` elements.
27379 *
27380 *
27381 * ### `select` **`as`** and **`track by`**
27382 *
27383 * <div class="alert alert-warning">
27384 * Be careful when using `select` **`as`** and **`track by`** in the same expression.
27385 * </div>
27386 *
27387 * Given this array of items on the $scope:
27388 *
27389 * ```js
27390 * $scope.items = [{
27391 * id: 1,
27392 * label: 'aLabel',
27393 * subItem: { name: 'aSubItem' }
27394 * }, {
27395 * id: 2,
27396 * label: 'bLabel',
27397 * subItem: { name: 'bSubItem' }
27398 * }];
27399 * ```
27400 *
27401 * This will work:
27402 *
27403 * ```html
27404 * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
27405 * ```
27406 * ```js
27407 * $scope.selected = $scope.items[0];
27408 * ```
27409 *
27410 * but this will not work:
27411 *
27412 * ```html
27413 * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
27414 * ```
27415 * ```js
27416 * $scope.selected = $scope.items[0].subItem;
27417 * ```
27418 *
27419 * In both examples, the **`track by`** expression is applied successfully to each `item` in the
27420 * `items` array. Because the selected option has been set programmatically in the controller, the
27421 * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
27422 * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
27423 * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
27424 * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
27425 * is not matched against any `<option>` and the `<select>` appears as having no selected value.
27426 *
27427 *
27428 * @param {string} ngModel Assignable angular expression to data-bind to.
27429 * @param {string=} name Property name of the form under which the control is published.
27430 * @param {string=} required The control is considered valid only if value is entered.
27431 * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
27432 * the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
27433 * `required` when you want to data-bind to the `required` attribute.
27434 * @param {comprehension_expression=} ngOptions in one of the following forms:
27435 *
27436 * * for array data sources:
27437 * * `label` **`for`** `value` **`in`** `array`
27438 * * `select` **`as`** `label` **`for`** `value` **`in`** `array`
27439 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
27440 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
27441 * * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
27442 * * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
27443 * * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
27444 * (for including a filter with `track by`)
27445 * * for object data sources:
27446 * * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
27447 * * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
27448 * * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
27449 * * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
27450 * * `select` **`as`** `label` **`group by`** `group`
27451 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
27452 * * `select` **`as`** `label` **`disable when`** `disable`
27453 * **`for` `(`**`key`**`,`** `value`**`) in`** `object`
27454 *
27455 * Where:
27456 *
27457 * * `array` / `object`: an expression which evaluates to an array / object to iterate over.
27458 * * `value`: local variable which will refer to each item in the `array` or each property value
27459 * of `object` during iteration.
27460 * * `key`: local variable which will refer to a property name in `object` during iteration.
27461 * * `label`: The result of this expression will be the label for `<option>` element. The
27462 * `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
27463 * * `select`: The result of this expression will be bound to the model of the parent `<select>`
27464 * element. If not specified, `select` expression will default to `value`.
27465 * * `group`: The result of this expression will be used to group options using the `<optgroup>`
27466 * DOM element.
27467 * * `disable`: The result of this expression will be used to disable the rendered `<option>`
27468 * element. Return `true` to disable.
27469 * * `trackexpr`: Used when working with an array of objects. The result of this expression will be
27470 * used to identify the objects in the array. The `trackexpr` will most likely refer to the
27471 * `value` variable (e.g. `value.propertyName`). With this the selection is preserved
27472 * even when the options are recreated (e.g. reloaded from the server).
27473 *
27474 * @example
27475 <example module="selectExample">
27476 <file name="index.html">
27477 <script>
27478 angular.module('selectExample', [])
27479 .controller('ExampleController', ['$scope', function($scope) {
27480 $scope.colors = [
27481 {name:'black', shade:'dark'},
27482 {name:'white', shade:'light', notAnOption: true},
27483 {name:'red', shade:'dark'},
27484 {name:'blue', shade:'dark', notAnOption: true},
27485 {name:'yellow', shade:'light', notAnOption: false}
27486 ];
27487 $scope.myColor = $scope.colors[2]; // red
27488 }]);
27489 </script>
27490 <div ng-controller="ExampleController">
27491 <ul>
27492 <li ng-repeat="color in colors">
27493 <label>Name: <input ng-model="color.name"></label>
27494 <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
27495 <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
27496 </li>
27497 <li>
27498 <button ng-click="colors.push({})">add</button>
27499 </li>
27500 </ul>
27501 <hr/>
27502 <label>Color (null not allowed):
27503 <select ng-model="myColor" ng-options="color.name for color in colors"></select>
27504 </label><br/>
27505 <label>Color (null allowed):
27506 <span class="nullable">
27507 <select ng-model="myColor" ng-options="color.name for color in colors">
27508 <option value="">-- choose color --</option>
27509 </select>
27510 </span></label><br/>
27511
27512 <label>Color grouped by shade:
27513 <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
27514 </select>
27515 </label><br/>
27516
27517 <label>Color grouped by shade, with some disabled:
27518 <select ng-model="myColor"
27519 ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
27520 </select>
27521 </label><br/>
27522
27523
27524
27525 Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
27526 <br/>
27527 <hr/>
27528 Currently selected: {{ {selected_color:myColor} }}
27529 <div style="border:solid 1px black; height:20px"
27530 ng-style="{'background-color':myColor.name}">
27531 </div>
27532 </div>
27533 </file>
27534 <file name="protractor.js" type="protractor">
27535 it('should check ng-options', function() {
27536 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
27537 element.all(by.model('myColor')).first().click();
27538 element.all(by.css('select[ng-model="myColor"] option')).first().click();
27539 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
27540 element(by.css('.nullable select[ng-model="myColor"]')).click();
27541 element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
27542 expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
27543 });
27544 </file>
27545 </example>
27546 */
27547
27548// jshint maxlen: false
27549// //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
27550var NG_OPTIONS_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?(?:\s+group\s+by\s+([\s\S]+?))?(?:\s+disable\s+when\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]+?))?$/;
27551 // 1: value expression (valueFn)
27552 // 2: label expression (displayFn)
27553 // 3: group by expression (groupByFn)
27554 // 4: disable when expression (disableWhenFn)
27555 // 5: array item variable name
27556 // 6: object item key variable name
27557 // 7: object item value variable name
27558 // 8: collection expression
27559 // 9: track by expression
27560// jshint maxlen: 100
27561
27562
27563var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
27564
27565 function parseOptionsExpression(optionsExp, selectElement, scope) {
27566
27567 var match = optionsExp.match(NG_OPTIONS_REGEXP);
27568 if (!(match)) {
27569 throw ngOptionsMinErr('iexp',
27570 "Expected expression in form of " +
27571 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
27572 " but got '{0}'. Element: {1}",
27573 optionsExp, startingTag(selectElement));
27574 }
27575
27576 // Extract the parts from the ngOptions expression
27577
27578 // The variable name for the value of the item in the collection
27579 var valueName = match[5] || match[7];
27580 // The variable name for the key of the item in the collection
27581 var keyName = match[6];
27582
27583 // An expression that generates the viewValue for an option if there is a label expression
27584 var selectAs = / as /.test(match[0]) && match[1];
27585 // An expression that is used to track the id of each object in the options collection
27586 var trackBy = match[9];
27587 // An expression that generates the viewValue for an option if there is no label expression
27588 var valueFn = $parse(match[2] ? match[1] : valueName);
27589 var selectAsFn = selectAs && $parse(selectAs);
27590 var viewValueFn = selectAsFn || valueFn;
27591 var trackByFn = trackBy && $parse(trackBy);
27592
27593 // Get the value by which we are going to track the option
27594 // if we have a trackFn then use that (passing scope and locals)
27595 // otherwise just hash the given viewValue
27596 var getTrackByValueFn = trackBy ?
27597 function(value, locals) { return trackByFn(scope, locals); } :
27598 function getHashOfValue(value) { return hashKey(value); };
27599 var getTrackByValue = function(value, key) {
27600 return getTrackByValueFn(value, getLocals(value, key));
27601 };
27602
27603 var displayFn = $parse(match[2] || match[1]);
27604 var groupByFn = $parse(match[3] || '');
27605 var disableWhenFn = $parse(match[4] || '');
27606 var valuesFn = $parse(match[8]);
27607
27608 var locals = {};
27609 var getLocals = keyName ? function(value, key) {
27610 locals[keyName] = key;
27611 locals[valueName] = value;
27612 return locals;
27613 } : function(value) {
27614 locals[valueName] = value;
27615 return locals;
27616 };
27617
27618
27619 function Option(selectValue, viewValue, label, group, disabled) {
27620 this.selectValue = selectValue;
27621 this.viewValue = viewValue;
27622 this.label = label;
27623 this.group = group;
27624 this.disabled = disabled;
27625 }
27626
27627 function getOptionValuesKeys(optionValues) {
27628 var optionValuesKeys;
27629
27630 if (!keyName && isArrayLike(optionValues)) {
27631 optionValuesKeys = optionValues;
27632 } else {
27633 // if object, extract keys, in enumeration order, unsorted
27634 optionValuesKeys = [];
27635 for (var itemKey in optionValues) {
27636 if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
27637 optionValuesKeys.push(itemKey);
27638 }
27639 }
27640 }
27641 return optionValuesKeys;
27642 }
27643
27644 return {
27645 trackBy: trackBy,
27646 getTrackByValue: getTrackByValue,
27647 getWatchables: $parse(valuesFn, function(optionValues) {
27648 // Create a collection of things that we would like to watch (watchedArray)
27649 // so that they can all be watched using a single $watchCollection
27650 // that only runs the handler once if anything changes
27651 var watchedArray = [];
27652 optionValues = optionValues || [];
27653
27654 var optionValuesKeys = getOptionValuesKeys(optionValues);
27655 var optionValuesLength = optionValuesKeys.length;
27656 for (var index = 0; index < optionValuesLength; index++) {
27657 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
27658 var value = optionValues[key];
27659
27660 var locals = getLocals(value, key);
27661 var selectValue = getTrackByValueFn(value, locals);
27662 watchedArray.push(selectValue);
27663
27664 // Only need to watch the displayFn if there is a specific label expression
27665 if (match[2] || match[1]) {
27666 var label = displayFn(scope, locals);
27667 watchedArray.push(label);
27668 }
27669
27670 // Only need to watch the disableWhenFn if there is a specific disable expression
27671 if (match[4]) {
27672 var disableWhen = disableWhenFn(scope, locals);
27673 watchedArray.push(disableWhen);
27674 }
27675 }
27676 return watchedArray;
27677 }),
27678
27679 getOptions: function() {
27680
27681 var optionItems = [];
27682 var selectValueMap = {};
27683
27684 // The option values were already computed in the `getWatchables` fn,
27685 // which must have been called to trigger `getOptions`
27686 var optionValues = valuesFn(scope) || [];
27687 var optionValuesKeys = getOptionValuesKeys(optionValues);
27688 var optionValuesLength = optionValuesKeys.length;
27689
27690 for (var index = 0; index < optionValuesLength; index++) {
27691 var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
27692 var value = optionValues[key];
27693 var locals = getLocals(value, key);
27694 var viewValue = viewValueFn(scope, locals);
27695 var selectValue = getTrackByValueFn(viewValue, locals);
27696 var label = displayFn(scope, locals);
27697 var group = groupByFn(scope, locals);
27698 var disabled = disableWhenFn(scope, locals);
27699 var optionItem = new Option(selectValue, viewValue, label, group, disabled);
27700
27701 optionItems.push(optionItem);
27702 selectValueMap[selectValue] = optionItem;
27703 }
27704
27705 return {
27706 items: optionItems,
27707 selectValueMap: selectValueMap,
27708 getOptionFromViewValue: function(value) {
27709 return selectValueMap[getTrackByValue(value)];
27710 },
27711 getViewValueFromOption: function(option) {
27712 // If the viewValue could be an object that may be mutated by the application,
27713 // we need to make a copy and not return the reference to the value on the option.
27714 return trackBy ? angular.copy(option.viewValue) : option.viewValue;
27715 }
27716 };
27717 }
27718 };
27719 }
27720
27721
27722 // we can't just jqLite('<option>') since jqLite is not smart enough
27723 // to create it in <select> and IE barfs otherwise.
27724 var optionTemplate = document.createElement('option'),
27725 optGroupTemplate = document.createElement('optgroup');
27726
27727 function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
27728
27729 var selectCtrl = ctrls[0];
27730 var ngModelCtrl = ctrls[1];
27731 var multiple = attr.multiple;
27732
27733 // The emptyOption allows the application developer to provide their own custom "empty"
27734 // option when the viewValue does not match any of the option values.
27735 var emptyOption;
27736 for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
27737 if (children[i].value === '') {
27738 emptyOption = children.eq(i);
27739 break;
27740 }
27741 }
27742
27743 var providedEmptyOption = !!emptyOption;
27744
27745 var unknownOption = jqLite(optionTemplate.cloneNode(false));
27746 unknownOption.val('?');
27747
27748 var options;
27749 var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
27750
27751
27752 var renderEmptyOption = function() {
27753 if (!providedEmptyOption) {
27754 selectElement.prepend(emptyOption);
27755 }
27756 selectElement.val('');
27757 emptyOption.prop('selected', true); // needed for IE
27758 emptyOption.attr('selected', true);
27759 };
27760
27761 var removeEmptyOption = function() {
27762 if (!providedEmptyOption) {
27763 emptyOption.remove();
27764 }
27765 };
27766
27767
27768 var renderUnknownOption = function() {
27769 selectElement.prepend(unknownOption);
27770 selectElement.val('?');
27771 unknownOption.prop('selected', true); // needed for IE
27772 unknownOption.attr('selected', true);
27773 };
27774
27775 var removeUnknownOption = function() {
27776 unknownOption.remove();
27777 };
27778
27779 // Update the controller methods for multiple selectable options
27780 if (!multiple) {
27781
27782 selectCtrl.writeValue = function writeNgOptionsValue(value) {
27783 var option = options.getOptionFromViewValue(value);
27784
27785 if (option && !option.disabled) {
27786 // Don't update the option when it is already selected.
27787 // For example, the browser will select the first option by default. In that case,
27788 // most properties are set automatically - except the `selected` attribute, which we
27789 // set always
27790
27791 if (selectElement[0].value !== option.selectValue) {
27792 removeUnknownOption();
27793 removeEmptyOption();
27794
27795 selectElement[0].value = option.selectValue;
27796 option.element.selected = true;
27797 }
27798
27799 option.element.setAttribute('selected', 'selected');
27800 } else {
27801 if (value === null || providedEmptyOption) {
27802 removeUnknownOption();
27803 renderEmptyOption();
27804 } else {
27805 removeEmptyOption();
27806 renderUnknownOption();
27807 }
27808 }
27809 };
27810
27811 selectCtrl.readValue = function readNgOptionsValue() {
27812
27813 var selectedOption = options.selectValueMap[selectElement.val()];
27814
27815 if (selectedOption && !selectedOption.disabled) {
27816 removeEmptyOption();
27817 removeUnknownOption();
27818 return options.getViewValueFromOption(selectedOption);
27819 }
27820 return null;
27821 };
27822
27823 // If we are using `track by` then we must watch the tracked value on the model
27824 // since ngModel only watches for object identity change
27825 if (ngOptions.trackBy) {
27826 scope.$watch(
27827 function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
27828 function() { ngModelCtrl.$render(); }
27829 );
27830 }
27831
27832 } else {
27833
27834 ngModelCtrl.$isEmpty = function(value) {
27835 return !value || value.length === 0;
27836 };
27837
27838
27839 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
27840 options.items.forEach(function(option) {
27841 option.element.selected = false;
27842 });
27843
27844 if (value) {
27845 value.forEach(function(item) {
27846 var option = options.getOptionFromViewValue(item);
27847 if (option && !option.disabled) option.element.selected = true;
27848 });
27849 }
27850 };
27851
27852
27853 selectCtrl.readValue = function readNgOptionsMultiple() {
27854 var selectedValues = selectElement.val() || [],
27855 selections = [];
27856
27857 forEach(selectedValues, function(value) {
27858 var option = options.selectValueMap[value];
27859 if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
27860 });
27861
27862 return selections;
27863 };
27864
27865 // If we are using `track by` then we must watch these tracked values on the model
27866 // since ngModel only watches for object identity change
27867 if (ngOptions.trackBy) {
27868
27869 scope.$watchCollection(function() {
27870 if (isArray(ngModelCtrl.$viewValue)) {
27871 return ngModelCtrl.$viewValue.map(function(value) {
27872 return ngOptions.getTrackByValue(value);
27873 });
27874 }
27875 }, function() {
27876 ngModelCtrl.$render();
27877 });
27878
27879 }
27880 }
27881
27882
27883 if (providedEmptyOption) {
27884
27885 // we need to remove it before calling selectElement.empty() because otherwise IE will
27886 // remove the label from the element. wtf?
27887 emptyOption.remove();
27888
27889 // compile the element since there might be bindings in it
27890 $compile(emptyOption)(scope);
27891
27892 // remove the class, which is added automatically because we recompile the element and it
27893 // becomes the compilation root
27894 emptyOption.removeClass('ng-scope');
27895 } else {
27896 emptyOption = jqLite(optionTemplate.cloneNode(false));
27897 }
27898
27899 // We need to do this here to ensure that the options object is defined
27900 // when we first hit it in writeNgOptionsValue
27901 updateOptions();
27902
27903 // We will re-render the option elements if the option values or labels change
27904 scope.$watchCollection(ngOptions.getWatchables, updateOptions);
27905
27906 // ------------------------------------------------------------------ //
27907
27908
27909 function updateOptionElement(option, element) {
27910 option.element = element;
27911 element.disabled = option.disabled;
27912 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
27913 // selects in certain circumstances when multiple selects are next to each other and display
27914 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
27915 // See https://github.com/angular/angular.js/issues/11314 for more info.
27916 // This is unfortunately untestable with unit / e2e tests
27917 if (option.label !== element.label) {
27918 element.label = option.label;
27919 element.textContent = option.label;
27920 }
27921 if (option.value !== element.value) element.value = option.selectValue;
27922 }
27923
27924 function addOrReuseElement(parent, current, type, templateElement) {
27925 var element;
27926 // Check whether we can reuse the next element
27927 if (current && lowercase(current.nodeName) === type) {
27928 // The next element is the right type so reuse it
27929 element = current;
27930 } else {
27931 // The next element is not the right type so create a new one
27932 element = templateElement.cloneNode(false);
27933 if (!current) {
27934 // There are no more elements so just append it to the select
27935 parent.appendChild(element);
27936 } else {
27937 // The next element is not a group so insert the new one
27938 parent.insertBefore(element, current);
27939 }
27940 }
27941 return element;
27942 }
27943
27944
27945 function removeExcessElements(current) {
27946 var next;
27947 while (current) {
27948 next = current.nextSibling;
27949 jqLiteRemove(current);
27950 current = next;
27951 }
27952 }
27953
27954
27955 function skipEmptyAndUnknownOptions(current) {
27956 var emptyOption_ = emptyOption && emptyOption[0];
27957 var unknownOption_ = unknownOption && unknownOption[0];
27958
27959 // We cannot rely on the extracted empty option being the same as the compiled empty option,
27960 // because the compiled empty option might have been replaced by a comment because
27961 // it had an "element" transclusion directive on it (such as ngIf)
27962 if (emptyOption_ || unknownOption_) {
27963 while (current &&
27964 (current === emptyOption_ ||
27965 current === unknownOption_ ||
27966 current.nodeType === NODE_TYPE_COMMENT ||
27967 (nodeName_(current) === 'option' && current.value === ''))) {
27968 current = current.nextSibling;
27969 }
27970 }
27971 return current;
27972 }
27973
27974
27975 function updateOptions() {
27976
27977 var previousValue = options && selectCtrl.readValue();
27978
27979 options = ngOptions.getOptions();
27980
27981 var groupMap = {};
27982 var currentElement = selectElement[0].firstChild;
27983
27984 // Ensure that the empty option is always there if it was explicitly provided
27985 if (providedEmptyOption) {
27986 selectElement.prepend(emptyOption);
27987 }
27988
27989 currentElement = skipEmptyAndUnknownOptions(currentElement);
27990
27991 options.items.forEach(function updateOption(option) {
27992 var group;
27993 var groupElement;
27994 var optionElement;
27995
27996 if (isDefined(option.group)) {
27997
27998 // This option is to live in a group
27999 // See if we have already created this group
28000 group = groupMap[option.group];
28001
28002 if (!group) {
28003
28004 // We have not already created this group
28005 groupElement = addOrReuseElement(selectElement[0],
28006 currentElement,
28007 'optgroup',
28008 optGroupTemplate);
28009 // Move to the next element
28010 currentElement = groupElement.nextSibling;
28011
28012 // Update the label on the group element
28013 groupElement.label = option.group;
28014
28015 // Store it for use later
28016 group = groupMap[option.group] = {
28017 groupElement: groupElement,
28018 currentOptionElement: groupElement.firstChild
28019 };
28020
28021 }
28022
28023 // So now we have a group for this option we add the option to the group
28024 optionElement = addOrReuseElement(group.groupElement,
28025 group.currentOptionElement,
28026 'option',
28027 optionTemplate);
28028 updateOptionElement(option, optionElement);
28029 // Move to the next element
28030 group.currentOptionElement = optionElement.nextSibling;
28031
28032 } else {
28033
28034 // This option is not in a group
28035 optionElement = addOrReuseElement(selectElement[0],
28036 currentElement,
28037 'option',
28038 optionTemplate);
28039 updateOptionElement(option, optionElement);
28040 // Move to the next element
28041 currentElement = optionElement.nextSibling;
28042 }
28043 });
28044
28045
28046 // Now remove all excess options and group
28047 Object.keys(groupMap).forEach(function(key) {
28048 removeExcessElements(groupMap[key].currentOptionElement);
28049 });
28050 removeExcessElements(currentElement);
28051
28052 ngModelCtrl.$render();
28053
28054 // Check to see if the value has changed due to the update to the options
28055 if (!ngModelCtrl.$isEmpty(previousValue)) {
28056 var nextValue = selectCtrl.readValue();
28057 var isNotPrimitive = ngOptions.trackBy || multiple;
28058 if (isNotPrimitive ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
28059 ngModelCtrl.$setViewValue(nextValue);
28060 ngModelCtrl.$render();
28061 }
28062 }
28063
28064 }
28065 }
28066
28067 return {
28068 restrict: 'A',
28069 terminal: true,
28070 require: ['select', 'ngModel'],
28071 link: {
28072 pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
28073 // Deactivate the SelectController.register method to prevent
28074 // option directives from accidentally registering themselves
28075 // (and unwanted $destroy handlers etc.)
28076 ctrls[0].registerOption = noop;
28077 },
28078 post: ngOptionsPostLink
28079 }
28080 };
28081}];
28082
28083/**
28084 * @ngdoc directive
28085 * @name ngPluralize
28086 * @restrict EA
28087 *
28088 * @description
28089 * `ngPluralize` is a directive that displays messages according to en-US localization rules.
28090 * These rules are bundled with angular.js, but can be overridden
28091 * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
28092 * by specifying the mappings between
28093 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
28094 * and the strings to be displayed.
28095 *
28096 * # Plural categories and explicit number rules
28097 * There are two
28098 * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
28099 * in Angular's default en-US locale: "one" and "other".
28100 *
28101 * While a plural category may match many numbers (for example, in en-US locale, "other" can match
28102 * any number that is not 1), an explicit number rule can only match one number. For example, the
28103 * explicit number rule for "3" matches the number 3. There are examples of plural categories
28104 * and explicit number rules throughout the rest of this documentation.
28105 *
28106 * # Configuring ngPluralize
28107 * You configure ngPluralize by providing 2 attributes: `count` and `when`.
28108 * You can also provide an optional attribute, `offset`.
28109 *
28110 * The value of the `count` attribute can be either a string or an {@link guide/expression
28111 * Angular expression}; these are evaluated on the current scope for its bound value.
28112 *
28113 * The `when` attribute specifies the mappings between plural categories and the actual
28114 * string to be displayed. The value of the attribute should be a JSON object.
28115 *
28116 * The following example shows how to configure ngPluralize:
28117 *
28118 * ```html
28119 * <ng-pluralize count="personCount"
28120 when="{'0': 'Nobody is viewing.',
28121 * 'one': '1 person is viewing.',
28122 * 'other': '{} people are viewing.'}">
28123 * </ng-pluralize>
28124 *```
28125 *
28126 * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
28127 * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
28128 * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
28129 * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
28130 * show "a dozen people are viewing".
28131 *
28132 * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
28133 * into pluralized strings. In the previous example, Angular will replace `{}` with
28134 * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
28135 * for <span ng-non-bindable>{{numberExpression}}</span>.
28136 *
28137 * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
28138 * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
28139 *
28140 * # Configuring ngPluralize with offset
28141 * The `offset` attribute allows further customization of pluralized text, which can result in
28142 * a better user experience. For example, instead of the message "4 people are viewing this document",
28143 * you might display "John, Kate and 2 others are viewing this document".
28144 * The offset attribute allows you to offset a number by any desired value.
28145 * Let's take a look at an example:
28146 *
28147 * ```html
28148 * <ng-pluralize count="personCount" offset=2
28149 * when="{'0': 'Nobody is viewing.',
28150 * '1': '{{person1}} is viewing.',
28151 * '2': '{{person1}} and {{person2}} are viewing.',
28152 * 'one': '{{person1}}, {{person2}} and one other person are viewing.',
28153 * 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
28154 * </ng-pluralize>
28155 * ```
28156 *
28157 * Notice that we are still using two plural categories(one, other), but we added
28158 * three explicit number rules 0, 1 and 2.
28159 * When one person, perhaps John, views the document, "John is viewing" will be shown.
28160 * When three people view the document, no explicit number rule is found, so
28161 * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
28162 * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
28163 * is shown.
28164 *
28165 * Note that when you specify offsets, you must provide explicit number rules for
28166 * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
28167 * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
28168 * plural categories "one" and "other".
28169 *
28170 * @param {string|expression} count The variable to be bound to.
28171 * @param {string} when The mapping between plural category to its corresponding strings.
28172 * @param {number=} offset Offset to deduct from the total number.
28173 *
28174 * @example
28175 <example module="pluralizeExample">
28176 <file name="index.html">
28177 <script>
28178 angular.module('pluralizeExample', [])
28179 .controller('ExampleController', ['$scope', function($scope) {
28180 $scope.person1 = 'Igor';
28181 $scope.person2 = 'Misko';
28182 $scope.personCount = 1;
28183 }]);
28184 </script>
28185 <div ng-controller="ExampleController">
28186 <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
28187 <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
28188 <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
28189
28190 <!--- Example with simple pluralization rules for en locale --->
28191 Without Offset:
28192 <ng-pluralize count="personCount"
28193 when="{'0': 'Nobody is viewing.',
28194 'one': '1 person is viewing.',
28195 'other': '{} people are viewing.'}">
28196 </ng-pluralize><br>
28197
28198 <!--- Example with offset --->
28199 With Offset(2):
28200 <ng-pluralize count="personCount" offset=2
28201 when="{'0': 'Nobody is viewing.',
28202 '1': '{{person1}} is viewing.',
28203 '2': '{{person1}} and {{person2}} are viewing.',
28204 'one': '{{person1}}, {{person2}} and one other person are viewing.',
28205 'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
28206 </ng-pluralize>
28207 </div>
28208 </file>
28209 <file name="protractor.js" type="protractor">
28210 it('should show correct pluralized string', function() {
28211 var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
28212 var withOffset = element.all(by.css('ng-pluralize')).get(1);
28213 var countInput = element(by.model('personCount'));
28214
28215 expect(withoutOffset.getText()).toEqual('1 person is viewing.');
28216 expect(withOffset.getText()).toEqual('Igor is viewing.');
28217
28218 countInput.clear();
28219 countInput.sendKeys('0');
28220
28221 expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
28222 expect(withOffset.getText()).toEqual('Nobody is viewing.');
28223
28224 countInput.clear();
28225 countInput.sendKeys('2');
28226
28227 expect(withoutOffset.getText()).toEqual('2 people are viewing.');
28228 expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
28229
28230 countInput.clear();
28231 countInput.sendKeys('3');
28232
28233 expect(withoutOffset.getText()).toEqual('3 people are viewing.');
28234 expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
28235
28236 countInput.clear();
28237 countInput.sendKeys('4');
28238
28239 expect(withoutOffset.getText()).toEqual('4 people are viewing.');
28240 expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
28241 });
28242 it('should show data-bound names', function() {
28243 var withOffset = element.all(by.css('ng-pluralize')).get(1);
28244 var personCount = element(by.model('personCount'));
28245 var person1 = element(by.model('person1'));
28246 var person2 = element(by.model('person2'));
28247 personCount.clear();
28248 personCount.sendKeys('4');
28249 person1.clear();
28250 person1.sendKeys('Di');
28251 person2.clear();
28252 person2.sendKeys('Vojta');
28253 expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
28254 });
28255 </file>
28256 </example>
28257 */
28258var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
28259 var BRACE = /{}/g,
28260 IS_WHEN = /^when(Minus)?(.+)$/;
28261
28262 return {
28263 link: function(scope, element, attr) {
28264 var numberExp = attr.count,
28265 whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
28266 offset = attr.offset || 0,
28267 whens = scope.$eval(whenExp) || {},
28268 whensExpFns = {},
28269 startSymbol = $interpolate.startSymbol(),
28270 endSymbol = $interpolate.endSymbol(),
28271 braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
28272 watchRemover = angular.noop,
28273 lastCount;
28274
28275 forEach(attr, function(expression, attributeName) {
28276 var tmpMatch = IS_WHEN.exec(attributeName);
28277 if (tmpMatch) {
28278 var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
28279 whens[whenKey] = element.attr(attr.$attr[attributeName]);
28280 }
28281 });
28282 forEach(whens, function(expression, key) {
28283 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
28284
28285 });
28286
28287 scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
28288 var count = parseFloat(newVal);
28289 var countIsNaN = isNaN(count);
28290
28291 if (!countIsNaN && !(count in whens)) {
28292 // If an explicit number rule such as 1, 2, 3... is defined, just use it.
28293 // Otherwise, check it against pluralization rules in $locale service.
28294 count = $locale.pluralCat(count - offset);
28295 }
28296
28297 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
28298 // In JS `NaN !== NaN`, so we have to explicitly check.
28299 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
28300 watchRemover();
28301 var whenExpFn = whensExpFns[count];
28302 if (isUndefined(whenExpFn)) {
28303 if (newVal != null) {
28304 $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
28305 }
28306 watchRemover = noop;
28307 updateElementText();
28308 } else {
28309 watchRemover = scope.$watch(whenExpFn, updateElementText);
28310 }
28311 lastCount = count;
28312 }
28313 });
28314
28315 function updateElementText(newText) {
28316 element.text(newText || '');
28317 }
28318 }
28319 };
28320}];
28321
28322/**
28323 * @ngdoc directive
28324 * @name ngRepeat
28325 * @multiElement
28326 *
28327 * @description
28328 * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
28329 * instance gets its own scope, where the given loop variable is set to the current collection item,
28330 * and `$index` is set to the item index or key.
28331 *
28332 * Special properties are exposed on the local scope of each template instance, including:
28333 *
28334 * | Variable | Type | Details |
28335 * |-----------|-----------------|-----------------------------------------------------------------------------|
28336 * | `$index` | {@type number} | iterator offset of the repeated element (0..length-1) |
28337 * | `$first` | {@type boolean} | true if the repeated element is first in the iterator. |
28338 * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
28339 * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. |
28340 * | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
28341 * | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
28342 *
28343 * <div class="alert alert-info">
28344 * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
28345 * This may be useful when, for instance, nesting ngRepeats.
28346 * </div>
28347 *
28348 *
28349 * # Iterating over object properties
28350 *
28351 * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
28352 * syntax:
28353 *
28354 * ```js
28355 * <div ng-repeat="(key, value) in myObj"> ... </div>
28356 * ```
28357 *
28358 * However, there are a limitations compared to array iteration:
28359 *
28360 * - The JavaScript specification does not define the order of keys
28361 * returned for an object, so Angular relies on the order returned by the browser
28362 * when running `for key in myObj`. Browsers generally follow the strategy of providing
28363 * keys in the order in which they were defined, although there are exceptions when keys are deleted
28364 * and reinstated. See the
28365 * [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
28366 *
28367 * - `ngRepeat` will silently *ignore* object keys starting with `$`, because
28368 * it's a prefix used by Angular for public (`$`) and private (`$$`) properties.
28369 *
28370 * - The built-in filters {@link ng.orderBy orderBy} and {@link ng.filter filter} do not work with
28371 * objects, and will throw if used with one.
28372 *
28373 * If you are hitting any of these limitations, the recommended workaround is to convert your object into an array
28374 * that is sorted into the order that you prefer before providing it to `ngRepeat`. You could
28375 * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
28376 * or implement a `$watch` on the object yourself.
28377 *
28378 *
28379 * # Tracking and Duplicates
28380 *
28381 * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
28382 * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
28383 *
28384 * * When an item is added, a new instance of the template is added to the DOM.
28385 * * When an item is removed, its template instance is removed from the DOM.
28386 * * When items are reordered, their respective templates are reordered in the DOM.
28387 *
28388 * To minimize creation of DOM elements, `ngRepeat` uses a function
28389 * to "keep track" of all items in the collection and their corresponding DOM elements.
28390 * For example, if an item is added to the collection, ngRepeat will know that all other items
28391 * already have DOM elements, and will not re-render them.
28392 *
28393 * The default tracking function (which tracks items by their identity) does not allow
28394 * duplicate items in arrays. This is because when there are duplicates, it is not possible
28395 * to maintain a one-to-one mapping between collection items and DOM elements.
28396 *
28397 * If you do need to repeat duplicate items, you can substitute the default tracking behavior
28398 * with your own using the `track by` expression.
28399 *
28400 * For example, you may track items by the index of each item in the collection, using the
28401 * special scope property `$index`:
28402 * ```html
28403 * <div ng-repeat="n in [42, 42, 43, 43] track by $index">
28404 * {{n}}
28405 * </div>
28406 * ```
28407 *
28408 * You may also use arbitrary expressions in `track by`, including references to custom functions
28409 * on the scope:
28410 * ```html
28411 * <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
28412 * {{n}}
28413 * </div>
28414 * ```
28415 *
28416 * <div class="alert alert-success">
28417 * If you are working with objects that have an identifier property, you should track
28418 * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
28419 * will not have to rebuild the DOM elements for items it has already rendered, even if the
28420 * JavaScript objects in the collection have been substituted for new ones. For large collections,
28421 * this significantly improves rendering performance. If you don't have a unique identifier,
28422 * `track by $index` can also provide a performance boost.
28423 * </div>
28424 * ```html
28425 * <div ng-repeat="model in collection track by model.id">
28426 * {{model.name}}
28427 * </div>
28428 * ```
28429 *
28430 * When no `track by` expression is provided, it is equivalent to tracking by the built-in
28431 * `$id` function, which tracks items by their identity:
28432 * ```html
28433 * <div ng-repeat="obj in collection track by $id(obj)">
28434 * {{obj.prop}}
28435 * </div>
28436 * ```
28437 *
28438 * <div class="alert alert-warning">
28439 * **Note:** `track by` must always be the last expression:
28440 * </div>
28441 * ```
28442 * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
28443 * {{model.name}}
28444 * </div>
28445 * ```
28446 *
28447 * # Special repeat start and end points
28448 * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
28449 * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
28450 * 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)
28451 * up to and including the ending HTML tag where **ng-repeat-end** is placed.
28452 *
28453 * The example below makes use of this feature:
28454 * ```html
28455 * <header ng-repeat-start="item in items">
28456 * Header {{ item }}
28457 * </header>
28458 * <div class="body">
28459 * Body {{ item }}
28460 * </div>
28461 * <footer ng-repeat-end>
28462 * Footer {{ item }}
28463 * </footer>
28464 * ```
28465 *
28466 * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
28467 * ```html
28468 * <header>
28469 * Header A
28470 * </header>
28471 * <div class="body">
28472 * Body A
28473 * </div>
28474 * <footer>
28475 * Footer A
28476 * </footer>
28477 * <header>
28478 * Header B
28479 * </header>
28480 * <div class="body">
28481 * Body B
28482 * </div>
28483 * <footer>
28484 * Footer B
28485 * </footer>
28486 * ```
28487 *
28488 * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
28489 * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
28490 *
28491 * @animations
28492 * | Animation | Occurs |
28493 * |----------------------------------|-------------------------------------|
28494 * | {@link ng.$animate#enter enter} | when a new item is added to the list or when an item is revealed after a filter |
28495 * | {@link ng.$animate#leave leave} | when an item is removed from the list or when an item is filtered out |
28496 * | {@link ng.$animate#move move } | when an adjacent item is filtered out causing a reorder or when the item contents are reordered |
28497 *
28498 * See the example below for defining CSS animations with ngRepeat.
28499 *
28500 * @element ANY
28501 * @scope
28502 * @priority 1000
28503 * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
28504 * formats are currently supported:
28505 *
28506 * * `variable in expression` – where variable is the user defined loop variable and `expression`
28507 * is a scope expression giving the collection to enumerate.
28508 *
28509 * For example: `album in artist.albums`.
28510 *
28511 * * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
28512 * and `expression` is the scope expression giving the collection to enumerate.
28513 *
28514 * For example: `(name, age) in {'adam':10, 'amalie':12}`.
28515 *
28516 * * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
28517 * which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
28518 * is specified, ng-repeat associates elements by identity. It is an error to have
28519 * more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
28520 * mapped to the same DOM element, which is not possible.)
28521 *
28522 * Note that the tracking expression must come last, after any filters, and the alias expression.
28523 *
28524 * For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
28525 * will be associated by item identity in the array.
28526 *
28527 * For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
28528 * `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
28529 * with the corresponding item in the array by identity. Moving the same object in array would move the DOM
28530 * element in the same way in the DOM.
28531 *
28532 * For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
28533 * case the object identity does not matter. Two objects are considered equivalent as long as their `id`
28534 * property is same.
28535 *
28536 * For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
28537 * to items in conjunction with a tracking expression.
28538 *
28539 * * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
28540 * intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
28541 * when a filter is active on the repeater, but the filtered result set is empty.
28542 *
28543 * For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
28544 * the items have been processed through the filter.
28545 *
28546 * Please note that `as [variable name] is not an operator but rather a part of ngRepeat micro-syntax so it can be used only at the end
28547 * (and not as operator, inside an expression).
28548 *
28549 * For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
28550 *
28551 * @example
28552 * This example uses `ngRepeat` to display a list of people. A filter is used to restrict the displayed
28553 * results by name. New (entering) and removed (leaving) items are animated.
28554 <example module="ngRepeat" name="ngRepeat" deps="angular-animate.js" animations="true">
28555 <file name="index.html">
28556 <div ng-controller="repeatController">
28557 I have {{friends.length}} friends. They are:
28558 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
28559 <ul class="example-animate-container">
28560 <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
28561 [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
28562 </li>
28563 <li class="animate-repeat" ng-if="results.length == 0">
28564 <strong>No results found...</strong>
28565 </li>
28566 </ul>
28567 </div>
28568 </file>
28569 <file name="script.js">
28570 angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope) {
28571 $scope.friends = [
28572 {name:'John', age:25, gender:'boy'},
28573 {name:'Jessie', age:30, gender:'girl'},
28574 {name:'Johanna', age:28, gender:'girl'},
28575 {name:'Joy', age:15, gender:'girl'},
28576 {name:'Mary', age:28, gender:'girl'},
28577 {name:'Peter', age:95, gender:'boy'},
28578 {name:'Sebastian', age:50, gender:'boy'},
28579 {name:'Erika', age:27, gender:'girl'},
28580 {name:'Patrick', age:40, gender:'boy'},
28581 {name:'Samantha', age:60, gender:'girl'}
28582 ];
28583 });
28584 </file>
28585 <file name="animations.css">
28586 .example-animate-container {
28587 background:white;
28588 border:1px solid black;
28589 list-style:none;
28590 margin:0;
28591 padding:0 10px;
28592 }
28593
28594 .animate-repeat {
28595 line-height:30px;
28596 list-style:none;
28597 box-sizing:border-box;
28598 }
28599
28600 .animate-repeat.ng-move,
28601 .animate-repeat.ng-enter,
28602 .animate-repeat.ng-leave {
28603 transition:all linear 0.5s;
28604 }
28605
28606 .animate-repeat.ng-leave.ng-leave-active,
28607 .animate-repeat.ng-move,
28608 .animate-repeat.ng-enter {
28609 opacity:0;
28610 max-height:0;
28611 }
28612
28613 .animate-repeat.ng-leave,
28614 .animate-repeat.ng-move.ng-move-active,
28615 .animate-repeat.ng-enter.ng-enter-active {
28616 opacity:1;
28617 max-height:30px;
28618 }
28619 </file>
28620 <file name="protractor.js" type="protractor">
28621 var friends = element.all(by.repeater('friend in friends'));
28622
28623 it('should render initial data set', function() {
28624 expect(friends.count()).toBe(10);
28625 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
28626 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
28627 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
28628 expect(element(by.binding('friends.length')).getText())
28629 .toMatch("I have 10 friends. They are:");
28630 });
28631
28632 it('should update repeater when filter predicate changes', function() {
28633 expect(friends.count()).toBe(10);
28634
28635 element(by.model('q')).sendKeys('ma');
28636
28637 expect(friends.count()).toBe(2);
28638 expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
28639 expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
28640 });
28641 </file>
28642 </example>
28643 */
28644var ngRepeatDirective = ['$parse', '$animate', '$compile', function($parse, $animate, $compile) {
28645 var NG_REMOVED = '$$NG_REMOVED';
28646 var ngRepeatMinErr = minErr('ngRepeat');
28647
28648 var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
28649 // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
28650 scope[valueIdentifier] = value;
28651 if (keyIdentifier) scope[keyIdentifier] = key;
28652 scope.$index = index;
28653 scope.$first = (index === 0);
28654 scope.$last = (index === (arrayLength - 1));
28655 scope.$middle = !(scope.$first || scope.$last);
28656 // jshint bitwise: false
28657 scope.$odd = !(scope.$even = (index&1) === 0);
28658 // jshint bitwise: true
28659 };
28660
28661 var getBlockStart = function(block) {
28662 return block.clone[0];
28663 };
28664
28665 var getBlockEnd = function(block) {
28666 return block.clone[block.clone.length - 1];
28667 };
28668
28669
28670 return {
28671 restrict: 'A',
28672 multiElement: true,
28673 transclude: 'element',
28674 priority: 1000,
28675 terminal: true,
28676 $$tlb: true,
28677 compile: function ngRepeatCompile($element, $attr) {
28678 var expression = $attr.ngRepeat;
28679 var ngRepeatEndComment = $compile.$$createComment('end ngRepeat', expression);
28680
28681 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*$/);
28682
28683 if (!match) {
28684 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
28685 expression);
28686 }
28687
28688 var lhs = match[1];
28689 var rhs = match[2];
28690 var aliasAs = match[3];
28691 var trackByExp = match[4];
28692
28693 match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
28694
28695 if (!match) {
28696 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
28697 lhs);
28698 }
28699 var valueIdentifier = match[3] || match[1];
28700 var keyIdentifier = match[2];
28701
28702 if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
28703 /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
28704 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
28705 aliasAs);
28706 }
28707
28708 var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
28709 var hashFnLocals = {$id: hashKey};
28710
28711 if (trackByExp) {
28712 trackByExpGetter = $parse(trackByExp);
28713 } else {
28714 trackByIdArrayFn = function(key, value) {
28715 return hashKey(value);
28716 };
28717 trackByIdObjFn = function(key) {
28718 return key;
28719 };
28720 }
28721
28722 return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
28723
28724 if (trackByExpGetter) {
28725 trackByIdExpFn = function(key, value, index) {
28726 // assign key, value, and $index to the locals so that they can be used in hash functions
28727 if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
28728 hashFnLocals[valueIdentifier] = value;
28729 hashFnLocals.$index = index;
28730 return trackByExpGetter($scope, hashFnLocals);
28731 };
28732 }
28733
28734 // Store a list of elements from previous run. This is a hash where key is the item from the
28735 // iterator, and the value is objects with following properties.
28736 // - scope: bound scope
28737 // - element: previous element.
28738 // - index: position
28739 //
28740 // We are using no-proto object so that we don't need to guard against inherited props via
28741 // hasOwnProperty.
28742 var lastBlockMap = createMap();
28743
28744 //watch props
28745 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
28746 var index, length,
28747 previousNode = $element[0], // node that cloned nodes should be inserted after
28748 // initialized to the comment node anchor
28749 nextNode,
28750 // Same as lastBlockMap but it has the current state. It will become the
28751 // lastBlockMap on the next iteration.
28752 nextBlockMap = createMap(),
28753 collectionLength,
28754 key, value, // key/value of iteration
28755 trackById,
28756 trackByIdFn,
28757 collectionKeys,
28758 block, // last object information {scope, element, id}
28759 nextBlockOrder,
28760 elementsToRemove;
28761
28762 if (aliasAs) {
28763 $scope[aliasAs] = collection;
28764 }
28765
28766 if (isArrayLike(collection)) {
28767 collectionKeys = collection;
28768 trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
28769 } else {
28770 trackByIdFn = trackByIdExpFn || trackByIdObjFn;
28771 // if object, extract keys, in enumeration order, unsorted
28772 collectionKeys = [];
28773 for (var itemKey in collection) {
28774 if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
28775 collectionKeys.push(itemKey);
28776 }
28777 }
28778 }
28779
28780 collectionLength = collectionKeys.length;
28781 nextBlockOrder = new Array(collectionLength);
28782
28783 // locate existing items
28784 for (index = 0; index < collectionLength; index++) {
28785 key = (collection === collectionKeys) ? index : collectionKeys[index];
28786 value = collection[key];
28787 trackById = trackByIdFn(key, value, index);
28788 if (lastBlockMap[trackById]) {
28789 // found previously seen block
28790 block = lastBlockMap[trackById];
28791 delete lastBlockMap[trackById];
28792 nextBlockMap[trackById] = block;
28793 nextBlockOrder[index] = block;
28794 } else if (nextBlockMap[trackById]) {
28795 // if collision detected. restore lastBlockMap and throw an error
28796 forEach(nextBlockOrder, function(block) {
28797 if (block && block.scope) lastBlockMap[block.id] = block;
28798 });
28799 throw ngRepeatMinErr('dupes',
28800 "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
28801 expression, trackById, value);
28802 } else {
28803 // new never before seen block
28804 nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
28805 nextBlockMap[trackById] = true;
28806 }
28807 }
28808
28809 // remove leftover items
28810 for (var blockKey in lastBlockMap) {
28811 block = lastBlockMap[blockKey];
28812 elementsToRemove = getBlockNodes(block.clone);
28813 $animate.leave(elementsToRemove);
28814 if (elementsToRemove[0].parentNode) {
28815 // if the element was not removed yet because of pending animation, mark it as deleted
28816 // so that we can ignore it later
28817 for (index = 0, length = elementsToRemove.length; index < length; index++) {
28818 elementsToRemove[index][NG_REMOVED] = true;
28819 }
28820 }
28821 block.scope.$destroy();
28822 }
28823
28824 // we are not using forEach for perf reasons (trying to avoid #call)
28825 for (index = 0; index < collectionLength; index++) {
28826 key = (collection === collectionKeys) ? index : collectionKeys[index];
28827 value = collection[key];
28828 block = nextBlockOrder[index];
28829
28830 if (block.scope) {
28831 // if we have already seen this object, then we need to reuse the
28832 // associated scope/element
28833
28834 nextNode = previousNode;
28835
28836 // skip nodes that are already pending removal via leave animation
28837 do {
28838 nextNode = nextNode.nextSibling;
28839 } while (nextNode && nextNode[NG_REMOVED]);
28840
28841 if (getBlockStart(block) != nextNode) {
28842 // existing item which got moved
28843 $animate.move(getBlockNodes(block.clone), null, previousNode);
28844 }
28845 previousNode = getBlockEnd(block);
28846 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28847 } else {
28848 // new item which we don't know about
28849 $transclude(function ngRepeatTransclude(clone, scope) {
28850 block.scope = scope;
28851 // http://jsperf.com/clone-vs-createcomment
28852 var endNode = ngRepeatEndComment.cloneNode(false);
28853 clone[clone.length++] = endNode;
28854
28855 $animate.enter(clone, null, previousNode);
28856 previousNode = endNode;
28857 // Note: We only need the first/last node of the cloned nodes.
28858 // However, we need to keep the reference to the jqlite wrapper as it might be changed later
28859 // by a directive with templateUrl when its template arrives.
28860 block.clone = clone;
28861 nextBlockMap[block.id] = block;
28862 updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
28863 });
28864 }
28865 }
28866 lastBlockMap = nextBlockMap;
28867 });
28868 };
28869 }
28870 };
28871}];
28872
28873var NG_HIDE_CLASS = 'ng-hide';
28874var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
28875/**
28876 * @ngdoc directive
28877 * @name ngShow
28878 * @multiElement
28879 *
28880 * @description
28881 * The `ngShow` directive shows or hides the given HTML element based on the expression
28882 * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
28883 * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
28884 * in AngularJS and sets the display style to none (using an !important flag).
28885 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
28886 *
28887 * ```html
28888 * <!-- when $scope.myValue is truthy (element is visible) -->
28889 * <div ng-show="myValue"></div>
28890 *
28891 * <!-- when $scope.myValue is falsy (element is hidden) -->
28892 * <div ng-show="myValue" class="ng-hide"></div>
28893 * ```
28894 *
28895 * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
28896 * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
28897 * from the element causing the element not to appear hidden.
28898 *
28899 * ## Why is !important used?
28900 *
28901 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
28902 * can be easily overridden by heavier selectors. For example, something as simple
28903 * as changing the display style on a HTML list item would make hidden elements appear visible.
28904 * This also becomes a bigger issue when dealing with CSS frameworks.
28905 *
28906 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
28907 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
28908 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
28909 *
28910 * ### Overriding `.ng-hide`
28911 *
28912 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
28913 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
28914 * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
28915 * with extra animation classes that can be added.
28916 *
28917 * ```css
28918 * .ng-hide:not(.ng-hide-animate) {
28919 * /* this is just another form of hiding an element */
28920 * display: block!important;
28921 * position: absolute;
28922 * top: -9999px;
28923 * left: -9999px;
28924 * }
28925 * ```
28926 *
28927 * By default you don't need to override in CSS anything and the animations will work around the display style.
28928 *
28929 * ## A note about animations with `ngShow`
28930 *
28931 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
28932 * is true and false. This system works like the animation system present with ngClass except that
28933 * you must also include the !important flag to override the display property
28934 * so that you can perform an animation when the element is hidden during the time of the animation.
28935 *
28936 * ```css
28937 * //
28938 * //a working example can be found at the bottom of this page
28939 * //
28940 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
28941 * /* this is required as of 1.3x to properly
28942 * apply all styling in a show/hide animation */
28943 * transition: 0s linear all;
28944 * }
28945 *
28946 * .my-element.ng-hide-add-active,
28947 * .my-element.ng-hide-remove-active {
28948 * /* the transition is defined in the active class */
28949 * transition: 1s linear all;
28950 * }
28951 *
28952 * .my-element.ng-hide-add { ... }
28953 * .my-element.ng-hide-add.ng-hide-add-active { ... }
28954 * .my-element.ng-hide-remove { ... }
28955 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
28956 * ```
28957 *
28958 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
28959 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
28960 *
28961 * @animations
28962 * | Animation | Occurs |
28963 * |----------------------------------|-------------------------------------|
28964 * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden |
28965 * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngShow` expression evaluates to a truthy value and just before contents are set to visible |
28966 *
28967 * @element ANY
28968 * @param {expression} ngShow If the {@link guide/expression expression} is truthy
28969 * then the element is shown or hidden respectively.
28970 *
28971 * @example
28972 <example module="ngAnimate" deps="angular-animate.js" animations="true">
28973 <file name="index.html">
28974 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
28975 <div>
28976 Show:
28977 <div class="check-element animate-show" ng-show="checked">
28978 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
28979 </div>
28980 </div>
28981 <div>
28982 Hide:
28983 <div class="check-element animate-show" ng-hide="checked">
28984 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
28985 </div>
28986 </div>
28987 </file>
28988 <file name="glyphicons.css">
28989 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
28990 </file>
28991 <file name="animations.css">
28992 .animate-show {
28993 line-height: 20px;
28994 opacity: 1;
28995 padding: 10px;
28996 border: 1px solid black;
28997 background: white;
28998 }
28999
29000 .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
29001 transition: all linear 0.5s;
29002 }
29003
29004 .animate-show.ng-hide {
29005 line-height: 0;
29006 opacity: 0;
29007 padding: 0 10px;
29008 }
29009
29010 .check-element {
29011 padding: 10px;
29012 border: 1px solid black;
29013 background: white;
29014 }
29015 </file>
29016 <file name="protractor.js" type="protractor">
29017 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
29018 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
29019
29020 it('should check ng-show / ng-hide', function() {
29021 expect(thumbsUp.isDisplayed()).toBeFalsy();
29022 expect(thumbsDown.isDisplayed()).toBeTruthy();
29023
29024 element(by.model('checked')).click();
29025
29026 expect(thumbsUp.isDisplayed()).toBeTruthy();
29027 expect(thumbsDown.isDisplayed()).toBeFalsy();
29028 });
29029 </file>
29030 </example>
29031 */
29032var ngShowDirective = ['$animate', function($animate) {
29033 return {
29034 restrict: 'A',
29035 multiElement: true,
29036 link: function(scope, element, attr) {
29037 scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
29038 // we're adding a temporary, animation-specific class for ng-hide since this way
29039 // we can control when the element is actually displayed on screen without having
29040 // to have a global/greedy CSS selector that breaks when other animations are run.
29041 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
29042 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
29043 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
29044 });
29045 });
29046 }
29047 };
29048}];
29049
29050
29051/**
29052 * @ngdoc directive
29053 * @name ngHide
29054 * @multiElement
29055 *
29056 * @description
29057 * The `ngHide` directive shows or hides the given HTML element based on the expression
29058 * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
29059 * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
29060 * in AngularJS and sets the display style to none (using an !important flag).
29061 * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
29062 *
29063 * ```html
29064 * <!-- when $scope.myValue is truthy (element is hidden) -->
29065 * <div ng-hide="myValue" class="ng-hide"></div>
29066 *
29067 * <!-- when $scope.myValue is falsy (element is visible) -->
29068 * <div ng-hide="myValue"></div>
29069 * ```
29070 *
29071 * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
29072 * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
29073 * from the element causing the element not to appear hidden.
29074 *
29075 * ## Why is !important used?
29076 *
29077 * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
29078 * can be easily overridden by heavier selectors. For example, something as simple
29079 * as changing the display style on a HTML list item would make hidden elements appear visible.
29080 * This also becomes a bigger issue when dealing with CSS frameworks.
29081 *
29082 * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
29083 * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
29084 * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
29085 *
29086 * ### Overriding `.ng-hide`
29087 *
29088 * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
29089 * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
29090 * class in CSS:
29091 *
29092 * ```css
29093 * .ng-hide {
29094 * /* this is just another form of hiding an element */
29095 * display: block!important;
29096 * position: absolute;
29097 * top: -9999px;
29098 * left: -9999px;
29099 * }
29100 * ```
29101 *
29102 * By default you don't need to override in CSS anything and the animations will work around the display style.
29103 *
29104 * ## A note about animations with `ngHide`
29105 *
29106 * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
29107 * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
29108 * CSS class is added and removed for you instead of your own CSS class.
29109 *
29110 * ```css
29111 * //
29112 * //a working example can be found at the bottom of this page
29113 * //
29114 * .my-element.ng-hide-add, .my-element.ng-hide-remove {
29115 * transition: 0.5s linear all;
29116 * }
29117 *
29118 * .my-element.ng-hide-add { ... }
29119 * .my-element.ng-hide-add.ng-hide-add-active { ... }
29120 * .my-element.ng-hide-remove { ... }
29121 * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
29122 * ```
29123 *
29124 * Keep in mind that, as of AngularJS version 1.3, there is no need to change the display
29125 * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
29126 *
29127 * @animations
29128 * | Animation | Occurs |
29129 * |----------------------------------|-------------------------------------|
29130 * | {@link $animate#addClass addClass} `.ng-hide` | after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden |
29131 * | {@link $animate#removeClass removeClass} `.ng-hide` | after the `ngHide` expression evaluates to a non truthy value and just before contents are set to visible |
29132 *
29133 *
29134 * @element ANY
29135 * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
29136 * the element is shown or hidden respectively.
29137 *
29138 * @example
29139 <example module="ngAnimate" deps="angular-animate.js" animations="true">
29140 <file name="index.html">
29141 Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
29142 <div>
29143 Show:
29144 <div class="check-element animate-hide" ng-show="checked">
29145 <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
29146 </div>
29147 </div>
29148 <div>
29149 Hide:
29150 <div class="check-element animate-hide" ng-hide="checked">
29151 <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
29152 </div>
29153 </div>
29154 </file>
29155 <file name="glyphicons.css">
29156 @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
29157 </file>
29158 <file name="animations.css">
29159 .animate-hide {
29160 transition: all linear 0.5s;
29161 line-height: 20px;
29162 opacity: 1;
29163 padding: 10px;
29164 border: 1px solid black;
29165 background: white;
29166 }
29167
29168 .animate-hide.ng-hide {
29169 line-height: 0;
29170 opacity: 0;
29171 padding: 0 10px;
29172 }
29173
29174 .check-element {
29175 padding: 10px;
29176 border: 1px solid black;
29177 background: white;
29178 }
29179 </file>
29180 <file name="protractor.js" type="protractor">
29181 var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
29182 var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
29183
29184 it('should check ng-show / ng-hide', function() {
29185 expect(thumbsUp.isDisplayed()).toBeFalsy();
29186 expect(thumbsDown.isDisplayed()).toBeTruthy();
29187
29188 element(by.model('checked')).click();
29189
29190 expect(thumbsUp.isDisplayed()).toBeTruthy();
29191 expect(thumbsDown.isDisplayed()).toBeFalsy();
29192 });
29193 </file>
29194 </example>
29195 */
29196var ngHideDirective = ['$animate', function($animate) {
29197 return {
29198 restrict: 'A',
29199 multiElement: true,
29200 link: function(scope, element, attr) {
29201 scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
29202 // The comment inside of the ngShowDirective explains why we add and
29203 // remove a temporary class for the show/hide animation
29204 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
29205 tempClasses: NG_HIDE_IN_PROGRESS_CLASS
29206 });
29207 });
29208 }
29209 };
29210}];
29211
29212/**
29213 * @ngdoc directive
29214 * @name ngStyle
29215 * @restrict AC
29216 *
29217 * @description
29218 * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
29219 *
29220 * @element ANY
29221 * @param {expression} ngStyle
29222 *
29223 * {@link guide/expression Expression} which evals to an
29224 * object whose keys are CSS style names and values are corresponding values for those CSS
29225 * keys.
29226 *
29227 * Since some CSS style names are not valid keys for an object, they must be quoted.
29228 * See the 'background-color' style in the example below.
29229 *
29230 * @example
29231 <example>
29232 <file name="index.html">
29233 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
29234 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
29235 <input type="button" value="clear" ng-click="myStyle={}">
29236 <br/>
29237 <span ng-style="myStyle">Sample Text</span>
29238 <pre>myStyle={{myStyle}}</pre>
29239 </file>
29240 <file name="style.css">
29241 span {
29242 color: black;
29243 }
29244 </file>
29245 <file name="protractor.js" type="protractor">
29246 var colorSpan = element(by.css('span'));
29247
29248 it('should check ng-style', function() {
29249 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
29250 element(by.css('input[value=\'set color\']')).click();
29251 expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
29252 element(by.css('input[value=clear]')).click();
29253 expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
29254 });
29255 </file>
29256 </example>
29257 */
29258var ngStyleDirective = ngDirective(function(scope, element, attr) {
29259 scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
29260 if (oldStyles && (newStyles !== oldStyles)) {
29261 forEach(oldStyles, function(val, style) { element.css(style, '');});
29262 }
29263 if (newStyles) element.css(newStyles);
29264 }, true);
29265});
29266
29267/**
29268 * @ngdoc directive
29269 * @name ngSwitch
29270 * @restrict EA
29271 *
29272 * @description
29273 * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
29274 * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
29275 * as specified in the template.
29276 *
29277 * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
29278 * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
29279 * matches the value obtained from the evaluated expression. In other words, you define a container element
29280 * (where you place the directive), place an expression on the **`on="..."` attribute**
29281 * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
29282 * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
29283 * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
29284 * attribute is displayed.
29285 *
29286 * <div class="alert alert-info">
29287 * Be aware that the attribute values to match against cannot be expressions. They are interpreted
29288 * as literal string values to match against.
29289 * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
29290 * value of the expression `$scope.someVal`.
29291 * </div>
29292
29293 * @animations
29294 * | Animation | Occurs |
29295 * |----------------------------------|-------------------------------------|
29296 * | {@link ng.$animate#enter enter} | after the ngSwitch contents change and the matched child element is placed inside the container |
29297 * | {@link ng.$animate#leave leave} | after the ngSwitch contents change and just before the former contents are removed from the DOM |
29298 *
29299 * @usage
29300 *
29301 * ```
29302 * <ANY ng-switch="expression">
29303 * <ANY ng-switch-when="matchValue1">...</ANY>
29304 * <ANY ng-switch-when="matchValue2">...</ANY>
29305 * <ANY ng-switch-default>...</ANY>
29306 * </ANY>
29307 * ```
29308 *
29309 *
29310 * @scope
29311 * @priority 1200
29312 * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
29313 * On child elements add:
29314 *
29315 * * `ngSwitchWhen`: the case statement to match against. If match then this
29316 * case will be displayed. If the same match appears multiple times, all the
29317 * elements will be displayed.
29318 * * `ngSwitchDefault`: the default case when no other case match. If there
29319 * are multiple default cases, all of them will be displayed when no other
29320 * case match.
29321 *
29322 *
29323 * @example
29324 <example module="switchExample" deps="angular-animate.js" animations="true">
29325 <file name="index.html">
29326 <div ng-controller="ExampleController">
29327 <select ng-model="selection" ng-options="item for item in items">
29328 </select>
29329 <code>selection={{selection}}</code>
29330 <hr/>
29331 <div class="animate-switch-container"
29332 ng-switch on="selection">
29333 <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
29334 <div class="animate-switch" ng-switch-when="home">Home Span</div>
29335 <div class="animate-switch" ng-switch-default>default</div>
29336 </div>
29337 </div>
29338 </file>
29339 <file name="script.js">
29340 angular.module('switchExample', ['ngAnimate'])
29341 .controller('ExampleController', ['$scope', function($scope) {
29342 $scope.items = ['settings', 'home', 'other'];
29343 $scope.selection = $scope.items[0];
29344 }]);
29345 </file>
29346 <file name="animations.css">
29347 .animate-switch-container {
29348 position:relative;
29349 background:white;
29350 border:1px solid black;
29351 height:40px;
29352 overflow:hidden;
29353 }
29354
29355 .animate-switch {
29356 padding:10px;
29357 }
29358
29359 .animate-switch.ng-animate {
29360 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
29361
29362 position:absolute;
29363 top:0;
29364 left:0;
29365 right:0;
29366 bottom:0;
29367 }
29368
29369 .animate-switch.ng-leave.ng-leave-active,
29370 .animate-switch.ng-enter {
29371 top:-50px;
29372 }
29373 .animate-switch.ng-leave,
29374 .animate-switch.ng-enter.ng-enter-active {
29375 top:0;
29376 }
29377 </file>
29378 <file name="protractor.js" type="protractor">
29379 var switchElem = element(by.css('[ng-switch]'));
29380 var select = element(by.model('selection'));
29381
29382 it('should start in settings', function() {
29383 expect(switchElem.getText()).toMatch(/Settings Div/);
29384 });
29385 it('should change to home', function() {
29386 select.all(by.css('option')).get(1).click();
29387 expect(switchElem.getText()).toMatch(/Home Span/);
29388 });
29389 it('should select default', function() {
29390 select.all(by.css('option')).get(2).click();
29391 expect(switchElem.getText()).toMatch(/default/);
29392 });
29393 </file>
29394 </example>
29395 */
29396var ngSwitchDirective = ['$animate', '$compile', function($animate, $compile) {
29397 return {
29398 require: 'ngSwitch',
29399
29400 // asks for $scope to fool the BC controller module
29401 controller: ['$scope', function ngSwitchController() {
29402 this.cases = {};
29403 }],
29404 link: function(scope, element, attr, ngSwitchController) {
29405 var watchExpr = attr.ngSwitch || attr.on,
29406 selectedTranscludes = [],
29407 selectedElements = [],
29408 previousLeaveAnimations = [],
29409 selectedScopes = [];
29410
29411 var spliceFactory = function(array, index) {
29412 return function() { array.splice(index, 1); };
29413 };
29414
29415 scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
29416 var i, ii;
29417 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
29418 $animate.cancel(previousLeaveAnimations[i]);
29419 }
29420 previousLeaveAnimations.length = 0;
29421
29422 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
29423 var selected = getBlockNodes(selectedElements[i].clone);
29424 selectedScopes[i].$destroy();
29425 var promise = previousLeaveAnimations[i] = $animate.leave(selected);
29426 promise.then(spliceFactory(previousLeaveAnimations, i));
29427 }
29428
29429 selectedElements.length = 0;
29430 selectedScopes.length = 0;
29431
29432 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
29433 forEach(selectedTranscludes, function(selectedTransclude) {
29434 selectedTransclude.transclude(function(caseElement, selectedScope) {
29435 selectedScopes.push(selectedScope);
29436 var anchor = selectedTransclude.element;
29437 caseElement[caseElement.length++] = $compile.$$createComment('end ngSwitchWhen');
29438 var block = { clone: caseElement };
29439
29440 selectedElements.push(block);
29441 $animate.enter(caseElement, anchor.parent(), anchor);
29442 });
29443 });
29444 }
29445 });
29446 }
29447 };
29448}];
29449
29450var ngSwitchWhenDirective = ngDirective({
29451 transclude: 'element',
29452 priority: 1200,
29453 require: '^ngSwitch',
29454 multiElement: true,
29455 link: function(scope, element, attrs, ctrl, $transclude) {
29456 ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
29457 ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
29458 }
29459});
29460
29461var ngSwitchDefaultDirective = ngDirective({
29462 transclude: 'element',
29463 priority: 1200,
29464 require: '^ngSwitch',
29465 multiElement: true,
29466 link: function(scope, element, attr, ctrl, $transclude) {
29467 ctrl.cases['?'] = (ctrl.cases['?'] || []);
29468 ctrl.cases['?'].push({ transclude: $transclude, element: element });
29469 }
29470});
29471
29472/**
29473 * @ngdoc directive
29474 * @name ngTransclude
29475 * @restrict EAC
29476 *
29477 * @description
29478 * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
29479 *
29480 * You can specify that you want to insert a named transclusion slot, instead of the default slot, by providing the slot name
29481 * as the value of the `ng-transclude` or `ng-transclude-slot` attribute.
29482 *
29483 * If the transcluded content is not empty (i.e. contains one or more DOM nodes, including whitespace text nodes), any existing
29484 * content of this element will be removed before the transcluded content is inserted.
29485 * If the transcluded content is empty, the existing content is left intact. This lets you provide fallback content in the case
29486 * that no transcluded content is provided.
29487 *
29488 * @element ANY
29489 *
29490 * @param {string} ngTransclude|ngTranscludeSlot the name of the slot to insert at this point. If this is not provided, is empty
29491 * or its value is the same as the name of the attribute then the default slot is used.
29492 *
29493 * @example
29494 * ### Basic transclusion
29495 * This example demonstrates basic transclusion of content into a component directive.
29496 * <example name="simpleTranscludeExample" module="transcludeExample">
29497 * <file name="index.html">
29498 * <script>
29499 * angular.module('transcludeExample', [])
29500 * .directive('pane', function(){
29501 * return {
29502 * restrict: 'E',
29503 * transclude: true,
29504 * scope: { title:'@' },
29505 * template: '<div style="border: 1px solid black;">' +
29506 * '<div style="background-color: gray">{{title}}</div>' +
29507 * '<ng-transclude></ng-transclude>' +
29508 * '</div>'
29509 * };
29510 * })
29511 * .controller('ExampleController', ['$scope', function($scope) {
29512 * $scope.title = 'Lorem Ipsum';
29513 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
29514 * }]);
29515 * </script>
29516 * <div ng-controller="ExampleController">
29517 * <input ng-model="title" aria-label="title"> <br/>
29518 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
29519 * <pane title="{{title}}">{{text}}</pane>
29520 * </div>
29521 * </file>
29522 * <file name="protractor.js" type="protractor">
29523 * it('should have transcluded', function() {
29524 * var titleElement = element(by.model('title'));
29525 * titleElement.clear();
29526 * titleElement.sendKeys('TITLE');
29527 * var textElement = element(by.model('text'));
29528 * textElement.clear();
29529 * textElement.sendKeys('TEXT');
29530 * expect(element(by.binding('title')).getText()).toEqual('TITLE');
29531 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
29532 * });
29533 * </file>
29534 * </example>
29535 *
29536 * @example
29537 * ### Transclude fallback content
29538 * This example shows how to use `NgTransclude` with fallback content, that
29539 * is displayed if no transcluded content is provided.
29540 *
29541 * <example module="transcludeFallbackContentExample">
29542 * <file name="index.html">
29543 * <script>
29544 * angular.module('transcludeFallbackContentExample', [])
29545 * .directive('myButton', function(){
29546 * return {
29547 * restrict: 'E',
29548 * transclude: true,
29549 * scope: true,
29550 * template: '<button style="cursor: pointer;">' +
29551 * '<ng-transclude>' +
29552 * '<b style="color: red;">Button1</b>' +
29553 * '</ng-transclude>' +
29554 * '</button>'
29555 * };
29556 * });
29557 * </script>
29558 * <!-- fallback button content -->
29559 * <my-button id="fallback"></my-button>
29560 * <!-- modified button content -->
29561 * <my-button id="modified">
29562 * <i style="color: green;">Button2</i>
29563 * </my-button>
29564 * </file>
29565 * <file name="protractor.js" type="protractor">
29566 * it('should have different transclude element content', function() {
29567 * expect(element(by.id('fallback')).getText()).toBe('Button1');
29568 * expect(element(by.id('modified')).getText()).toBe('Button2');
29569 * });
29570 * </file>
29571 * </example>
29572 *
29573 * @example
29574 * ### Multi-slot transclusion
29575 * This example demonstrates using multi-slot transclusion in a component directive.
29576 * <example name="multiSlotTranscludeExample" module="multiSlotTranscludeExample">
29577 * <file name="index.html">
29578 * <style>
29579 * .title, .footer {
29580 * background-color: gray
29581 * }
29582 * </style>
29583 * <div ng-controller="ExampleController">
29584 * <input ng-model="title" aria-label="title"> <br/>
29585 * <textarea ng-model="text" aria-label="text"></textarea> <br/>
29586 * <pane>
29587 * <pane-title><a ng-href="{{link}}">{{title}}</a></pane-title>
29588 * <pane-body><p>{{text}}</p></pane-body>
29589 * </pane>
29590 * </div>
29591 * </file>
29592 * <file name="app.js">
29593 * angular.module('multiSlotTranscludeExample', [])
29594 * .directive('pane', function(){
29595 * return {
29596 * restrict: 'E',
29597 * transclude: {
29598 * 'title': '?paneTitle',
29599 * 'body': 'paneBody',
29600 * 'footer': '?paneFooter'
29601 * },
29602 * template: '<div style="border: 1px solid black;">' +
29603 * '<div class="title" ng-transclude="title">Fallback Title</div>' +
29604 * '<div ng-transclude="body"></div>' +
29605 * '<div class="footer" ng-transclude="footer">Fallback Footer</div>' +
29606 * '</div>'
29607 * };
29608 * })
29609 * .controller('ExampleController', ['$scope', function($scope) {
29610 * $scope.title = 'Lorem Ipsum';
29611 * $scope.link = "https://google.com";
29612 * $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
29613 * }]);
29614 * </file>
29615 * <file name="protractor.js" type="protractor">
29616 * it('should have transcluded the title and the body', function() {
29617 * var titleElement = element(by.model('title'));
29618 * titleElement.clear();
29619 * titleElement.sendKeys('TITLE');
29620 * var textElement = element(by.model('text'));
29621 * textElement.clear();
29622 * textElement.sendKeys('TEXT');
29623 * expect(element(by.css('.title')).getText()).toEqual('TITLE');
29624 * expect(element(by.binding('text')).getText()).toEqual('TEXT');
29625 * expect(element(by.css('.footer')).getText()).toEqual('Fallback Footer');
29626 * });
29627 * </file>
29628 * </example>
29629 */
29630var ngTranscludeMinErr = minErr('ngTransclude');
29631var ngTranscludeDirective = ngDirective({
29632 restrict: 'EAC',
29633 link: function($scope, $element, $attrs, controller, $transclude) {
29634
29635 if ($attrs.ngTransclude === $attrs.$attr.ngTransclude) {
29636 // If the attribute is of the form: `ng-transclude="ng-transclude"`
29637 // then treat it like the default
29638 $attrs.ngTransclude = '';
29639 }
29640
29641 function ngTranscludeCloneAttachFn(clone) {
29642 if (clone.length) {
29643 $element.empty();
29644 $element.append(clone);
29645 }
29646 }
29647
29648 if (!$transclude) {
29649 throw ngTranscludeMinErr('orphan',
29650 'Illegal use of ngTransclude directive in the template! ' +
29651 'No parent directive that requires a transclusion found. ' +
29652 'Element: {0}',
29653 startingTag($element));
29654 }
29655
29656 // If there is no slot name defined or the slot name is not optional
29657 // then transclude the slot
29658 var slotName = $attrs.ngTransclude || $attrs.ngTranscludeSlot;
29659 $transclude(ngTranscludeCloneAttachFn, null, slotName);
29660 }
29661});
29662
29663/**
29664 * @ngdoc directive
29665 * @name script
29666 * @restrict E
29667 *
29668 * @description
29669 * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
29670 * template can be used by {@link ng.directive:ngInclude `ngInclude`},
29671 * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
29672 * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
29673 * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
29674 *
29675 * @param {string} type Must be set to `'text/ng-template'`.
29676 * @param {string} id Cache name of the template.
29677 *
29678 * @example
29679 <example>
29680 <file name="index.html">
29681 <script type="text/ng-template" id="/tpl.html">
29682 Content of the template.
29683 </script>
29684
29685 <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
29686 <div id="tpl-content" ng-include src="currentTpl"></div>
29687 </file>
29688 <file name="protractor.js" type="protractor">
29689 it('should load template defined inside script tag', function() {
29690 element(by.css('#tpl-link')).click();
29691 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
29692 });
29693 </file>
29694 </example>
29695 */
29696var scriptDirective = ['$templateCache', function($templateCache) {
29697 return {
29698 restrict: 'E',
29699 terminal: true,
29700 compile: function(element, attr) {
29701 if (attr.type == 'text/ng-template') {
29702 var templateUrl = attr.id,
29703 text = element[0].text;
29704
29705 $templateCache.put(templateUrl, text);
29706 }
29707 }
29708 };
29709}];
29710
29711var noopNgModelController = { $setViewValue: noop, $render: noop };
29712
29713function chromeHack(optionElement) {
29714 // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
29715 // Adding an <option selected="selected"> element to a <select required="required"> should
29716 // automatically select the new element
29717 if (optionElement[0].hasAttribute('selected')) {
29718 optionElement[0].selected = true;
29719 }
29720}
29721
29722/**
29723 * @ngdoc type
29724 * @name select.SelectController
29725 * @description
29726 * The controller for the `<select>` directive. This provides support for reading
29727 * and writing the selected value(s) of the control and also coordinates dynamically
29728 * added `<option>` elements, perhaps by an `ngRepeat` directive.
29729 */
29730var SelectController =
29731 ['$element', '$scope', function($element, $scope) {
29732
29733 var self = this,
29734 optionsMap = new HashMap();
29735
29736 // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
29737 self.ngModelCtrl = noopNgModelController;
29738
29739 // The "unknown" option is one that is prepended to the list if the viewValue
29740 // does not match any of the options. When it is rendered the value of the unknown
29741 // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
29742 //
29743 // We can't just jqLite('<option>') since jqLite is not smart enough
29744 // to create it in <select> and IE barfs otherwise.
29745 self.unknownOption = jqLite(document.createElement('option'));
29746 self.renderUnknownOption = function(val) {
29747 var unknownVal = '? ' + hashKey(val) + ' ?';
29748 self.unknownOption.val(unknownVal);
29749 $element.prepend(self.unknownOption);
29750 $element.val(unknownVal);
29751 };
29752
29753 $scope.$on('$destroy', function() {
29754 // disable unknown option so that we don't do work when the whole select is being destroyed
29755 self.renderUnknownOption = noop;
29756 });
29757
29758 self.removeUnknownOption = function() {
29759 if (self.unknownOption.parent()) self.unknownOption.remove();
29760 };
29761
29762
29763 // Read the value of the select control, the implementation of this changes depending
29764 // upon whether the select can have multiple values and whether ngOptions is at work.
29765 self.readValue = function readSingleValue() {
29766 self.removeUnknownOption();
29767 return $element.val();
29768 };
29769
29770
29771 // Write the value to the select control, the implementation of this changes depending
29772 // upon whether the select can have multiple values and whether ngOptions is at work.
29773 self.writeValue = function writeSingleValue(value) {
29774 if (self.hasOption(value)) {
29775 self.removeUnknownOption();
29776 $element.val(value);
29777 if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
29778 } else {
29779 if (value == null && self.emptyOption) {
29780 self.removeUnknownOption();
29781 $element.val('');
29782 } else {
29783 self.renderUnknownOption(value);
29784 }
29785 }
29786 };
29787
29788
29789 // Tell the select control that an option, with the given value, has been added
29790 self.addOption = function(value, element) {
29791 // Skip comment nodes, as they only pollute the `optionsMap`
29792 if (element[0].nodeType === NODE_TYPE_COMMENT) return;
29793
29794 assertNotHasOwnProperty(value, '"option value"');
29795 if (value === '') {
29796 self.emptyOption = element;
29797 }
29798 var count = optionsMap.get(value) || 0;
29799 optionsMap.put(value, count + 1);
29800 self.ngModelCtrl.$render();
29801 chromeHack(element);
29802 };
29803
29804 // Tell the select control that an option, with the given value, has been removed
29805 self.removeOption = function(value) {
29806 var count = optionsMap.get(value);
29807 if (count) {
29808 if (count === 1) {
29809 optionsMap.remove(value);
29810 if (value === '') {
29811 self.emptyOption = undefined;
29812 }
29813 } else {
29814 optionsMap.put(value, count - 1);
29815 }
29816 }
29817 };
29818
29819 // Check whether the select control has an option matching the given value
29820 self.hasOption = function(value) {
29821 return !!optionsMap.get(value);
29822 };
29823
29824
29825 self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
29826
29827 if (interpolateValueFn) {
29828 // The value attribute is interpolated
29829 var oldVal;
29830 optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
29831 if (isDefined(oldVal)) {
29832 self.removeOption(oldVal);
29833 }
29834 oldVal = newVal;
29835 self.addOption(newVal, optionElement);
29836 });
29837 } else if (interpolateTextFn) {
29838 // The text content is interpolated
29839 optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
29840 optionAttrs.$set('value', newVal);
29841 if (oldVal !== newVal) {
29842 self.removeOption(oldVal);
29843 }
29844 self.addOption(newVal, optionElement);
29845 });
29846 } else {
29847 // The value attribute is static
29848 self.addOption(optionAttrs.value, optionElement);
29849 }
29850
29851 optionElement.on('$destroy', function() {
29852 self.removeOption(optionAttrs.value);
29853 self.ngModelCtrl.$render();
29854 });
29855 };
29856}];
29857
29858/**
29859 * @ngdoc directive
29860 * @name select
29861 * @restrict E
29862 *
29863 * @description
29864 * HTML `SELECT` element with angular data-binding.
29865 *
29866 * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
29867 * between the scope and the `<select>` control (including setting default values).
29868 * It also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
29869 * {@link ngOptions `ngOptions`} directives.
29870 *
29871 * When an item in the `<select>` menu is selected, the value of the selected option will be bound
29872 * to the model identified by the `ngModel` directive. With static or repeated options, this is
29873 * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
29874 * If you want dynamic value attributes, you can use interpolation inside the value attribute.
29875 *
29876 * <div class="alert alert-warning">
29877 * Note that the value of a `select` directive used without `ngOptions` is always a string.
29878 * When the model needs to be bound to a non-string value, you must either explicitly convert it
29879 * using a directive (see example below) or use `ngOptions` to specify the set of options.
29880 * This is because an option element can only be bound to string values at present.
29881 * </div>
29882 *
29883 * If the viewValue of `ngModel` does not match any of the options, then the control
29884 * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
29885 *
29886 * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
29887 * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
29888 * option. See example below for demonstration.
29889 *
29890 * <div class="alert alert-info">
29891 * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
29892 * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
29893 * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
29894 * comprehension expression, and additionally in reducing memory and increasing speed by not creating
29895 * a new scope for each repeated instance.
29896 * </div>
29897 *
29898 *
29899 * @param {string} ngModel Assignable angular expression to data-bind to.
29900 * @param {string=} name Property name of the form under which the control is published.
29901 * @param {string=} multiple Allows multiple options to be selected. The selected values will be
29902 * bound to the model as an array.
29903 * @param {string=} required Sets `required` validation error key if the value is not entered.
29904 * @param {string=} ngRequired Adds required attribute and required validation constraint to
29905 * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
29906 * when you want to data-bind to the required attribute.
29907 * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
29908 * interaction with the select element.
29909 * @param {string=} ngOptions sets the options that the select is populated with and defines what is
29910 * set on the model on selection. See {@link ngOptions `ngOptions`}.
29911 *
29912 * @example
29913 * ### Simple `select` elements with static options
29914 *
29915 * <example name="static-select" module="staticSelect">
29916 * <file name="index.html">
29917 * <div ng-controller="ExampleController">
29918 * <form name="myForm">
29919 * <label for="singleSelect"> Single select: </label><br>
29920 * <select name="singleSelect" ng-model="data.singleSelect">
29921 * <option value="option-1">Option 1</option>
29922 * <option value="option-2">Option 2</option>
29923 * </select><br>
29924 *
29925 * <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
29926 * <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
29927 * <option value="">---Please select---</option> <!-- not selected / blank option -->
29928 * <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
29929 * <option value="option-2">Option 2</option>
29930 * </select><br>
29931 * <button ng-click="forceUnknownOption()">Force unknown option</button><br>
29932 * <tt>singleSelect = {{data.singleSelect}}</tt>
29933 *
29934 * <hr>
29935 * <label for="multipleSelect"> Multiple select: </label><br>
29936 * <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
29937 * <option value="option-1">Option 1</option>
29938 * <option value="option-2">Option 2</option>
29939 * <option value="option-3">Option 3</option>
29940 * </select><br>
29941 * <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
29942 * </form>
29943 * </div>
29944 * </file>
29945 * <file name="app.js">
29946 * angular.module('staticSelect', [])
29947 * .controller('ExampleController', ['$scope', function($scope) {
29948 * $scope.data = {
29949 * singleSelect: null,
29950 * multipleSelect: [],
29951 * option1: 'option-1',
29952 * };
29953 *
29954 * $scope.forceUnknownOption = function() {
29955 * $scope.data.singleSelect = 'nonsense';
29956 * };
29957 * }]);
29958 * </file>
29959 *</example>
29960 *
29961 * ### Using `ngRepeat` to generate `select` options
29962 * <example name="ngrepeat-select" module="ngrepeatSelect">
29963 * <file name="index.html">
29964 * <div ng-controller="ExampleController">
29965 * <form name="myForm">
29966 * <label for="repeatSelect"> Repeat select: </label>
29967 * <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
29968 * <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
29969 * </select>
29970 * </form>
29971 * <hr>
29972 * <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
29973 * </div>
29974 * </file>
29975 * <file name="app.js">
29976 * angular.module('ngrepeatSelect', [])
29977 * .controller('ExampleController', ['$scope', function($scope) {
29978 * $scope.data = {
29979 * repeatSelect: null,
29980 * availableOptions: [
29981 * {id: '1', name: 'Option A'},
29982 * {id: '2', name: 'Option B'},
29983 * {id: '3', name: 'Option C'}
29984 * ],
29985 * };
29986 * }]);
29987 * </file>
29988 *</example>
29989 *
29990 *
29991 * ### Using `select` with `ngOptions` and setting a default value
29992 * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
29993 *
29994 * <example name="select-with-default-values" module="defaultValueSelect">
29995 * <file name="index.html">
29996 * <div ng-controller="ExampleController">
29997 * <form name="myForm">
29998 * <label for="mySelect">Make a choice:</label>
29999 * <select name="mySelect" id="mySelect"
30000 * ng-options="option.name for option in data.availableOptions track by option.id"
30001 * ng-model="data.selectedOption"></select>
30002 * </form>
30003 * <hr>
30004 * <tt>option = {{data.selectedOption}}</tt><br/>
30005 * </div>
30006 * </file>
30007 * <file name="app.js">
30008 * angular.module('defaultValueSelect', [])
30009 * .controller('ExampleController', ['$scope', function($scope) {
30010 * $scope.data = {
30011 * availableOptions: [
30012 * {id: '1', name: 'Option A'},
30013 * {id: '2', name: 'Option B'},
30014 * {id: '3', name: 'Option C'}
30015 * ],
30016 * selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
30017 * };
30018 * }]);
30019 * </file>
30020 *</example>
30021 *
30022 *
30023 * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
30024 *
30025 * <example name="select-with-non-string-options" module="nonStringSelect">
30026 * <file name="index.html">
30027 * <select ng-model="model.id" convert-to-number>
30028 * <option value="0">Zero</option>
30029 * <option value="1">One</option>
30030 * <option value="2">Two</option>
30031 * </select>
30032 * {{ model }}
30033 * </file>
30034 * <file name="app.js">
30035 * angular.module('nonStringSelect', [])
30036 * .run(function($rootScope) {
30037 * $rootScope.model = { id: 2 };
30038 * })
30039 * .directive('convertToNumber', function() {
30040 * return {
30041 * require: 'ngModel',
30042 * link: function(scope, element, attrs, ngModel) {
30043 * ngModel.$parsers.push(function(val) {
30044 * return parseInt(val, 10);
30045 * });
30046 * ngModel.$formatters.push(function(val) {
30047 * return '' + val;
30048 * });
30049 * }
30050 * };
30051 * });
30052 * </file>
30053 * <file name="protractor.js" type="protractor">
30054 * it('should initialize to model', function() {
30055 * var select = element(by.css('select'));
30056 * expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
30057 * });
30058 * </file>
30059 * </example>
30060 *
30061 */
30062var selectDirective = function() {
30063
30064 return {
30065 restrict: 'E',
30066 require: ['select', '?ngModel'],
30067 controller: SelectController,
30068 priority: 1,
30069 link: {
30070 pre: selectPreLink,
30071 post: selectPostLink
30072 }
30073 };
30074
30075 function selectPreLink(scope, element, attr, ctrls) {
30076
30077 // if ngModel is not defined, we don't need to do anything
30078 var ngModelCtrl = ctrls[1];
30079 if (!ngModelCtrl) return;
30080
30081 var selectCtrl = ctrls[0];
30082
30083 selectCtrl.ngModelCtrl = ngModelCtrl;
30084
30085 // When the selected item(s) changes we delegate getting the value of the select control
30086 // to the `readValue` method, which can be changed if the select can have multiple
30087 // selected values or if the options are being generated by `ngOptions`
30088 element.on('change', function() {
30089 scope.$apply(function() {
30090 ngModelCtrl.$setViewValue(selectCtrl.readValue());
30091 });
30092 });
30093
30094 // If the select allows multiple values then we need to modify how we read and write
30095 // values from and to the control; also what it means for the value to be empty and
30096 // we have to add an extra watch since ngModel doesn't work well with arrays - it
30097 // doesn't trigger rendering if only an item in the array changes.
30098 if (attr.multiple) {
30099
30100 // Read value now needs to check each option to see if it is selected
30101 selectCtrl.readValue = function readMultipleValue() {
30102 var array = [];
30103 forEach(element.find('option'), function(option) {
30104 if (option.selected) {
30105 array.push(option.value);
30106 }
30107 });
30108 return array;
30109 };
30110
30111 // Write value now needs to set the selected property of each matching option
30112 selectCtrl.writeValue = function writeMultipleValue(value) {
30113 var items = new HashMap(value);
30114 forEach(element.find('option'), function(option) {
30115 option.selected = isDefined(items.get(option.value));
30116 });
30117 };
30118
30119 // we have to do it on each watch since ngModel watches reference, but
30120 // we need to work of an array, so we need to see if anything was inserted/removed
30121 var lastView, lastViewRef = NaN;
30122 scope.$watch(function selectMultipleWatch() {
30123 if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
30124 lastView = shallowCopy(ngModelCtrl.$viewValue);
30125 ngModelCtrl.$render();
30126 }
30127 lastViewRef = ngModelCtrl.$viewValue;
30128 });
30129
30130 // If we are a multiple select then value is now a collection
30131 // so the meaning of $isEmpty changes
30132 ngModelCtrl.$isEmpty = function(value) {
30133 return !value || value.length === 0;
30134 };
30135
30136 }
30137 }
30138
30139 function selectPostLink(scope, element, attrs, ctrls) {
30140 // if ngModel is not defined, we don't need to do anything
30141 var ngModelCtrl = ctrls[1];
30142 if (!ngModelCtrl) return;
30143
30144 var selectCtrl = ctrls[0];
30145
30146 // We delegate rendering to the `writeValue` method, which can be changed
30147 // if the select can have multiple selected values or if the options are being
30148 // generated by `ngOptions`.
30149 // This must be done in the postLink fn to prevent $render to be called before
30150 // all nodes have been linked correctly.
30151 ngModelCtrl.$render = function() {
30152 selectCtrl.writeValue(ngModelCtrl.$viewValue);
30153 };
30154 }
30155};
30156
30157
30158// The option directive is purely designed to communicate the existence (or lack of)
30159// of dynamically created (and destroyed) option elements to their containing select
30160// directive via its controller.
30161var optionDirective = ['$interpolate', function($interpolate) {
30162 return {
30163 restrict: 'E',
30164 priority: 100,
30165 compile: function(element, attr) {
30166 if (isDefined(attr.value)) {
30167 // If the value attribute is defined, check if it contains an interpolation
30168 var interpolateValueFn = $interpolate(attr.value, true);
30169 } else {
30170 // If the value attribute is not defined then we fall back to the
30171 // text content of the option element, which may be interpolated
30172 var interpolateTextFn = $interpolate(element.text(), true);
30173 if (!interpolateTextFn) {
30174 attr.$set('value', element.text());
30175 }
30176 }
30177
30178 return function(scope, element, attr) {
30179 // This is an optimization over using ^^ since we don't want to have to search
30180 // all the way to the root of the DOM for every single option element
30181 var selectCtrlName = '$selectController',
30182 parent = element.parent(),
30183 selectCtrl = parent.data(selectCtrlName) ||
30184 parent.parent().data(selectCtrlName); // in case we are in optgroup
30185
30186 if (selectCtrl) {
30187 selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
30188 }
30189 };
30190 }
30191 };
30192}];
30193
30194var styleDirective = valueFn({
30195 restrict: 'E',
30196 terminal: false
30197});
30198
30199/**
30200 * @ngdoc directive
30201 * @name ngRequired
30202 *
30203 * @description
30204 *
30205 * ngRequired adds the required {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30206 * It is most often used for {@link input `input`} and {@link select `select`} controls, but can also be
30207 * applied to custom controls.
30208 *
30209 * The directive sets the `required` attribute on the element if the Angular expression inside
30210 * `ngRequired` evaluates to true. A special directive for setting `required` is necessary because we
30211 * cannot use interpolation inside `required`. See the {@link guide/interpolation interpolation guide}
30212 * for more info.
30213 *
30214 * The validator will set the `required` error key to true if the `required` attribute is set and
30215 * calling {@link ngModel.NgModelController#$isEmpty `NgModelController.$isEmpty`} with the
30216 * {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`} returns `true`. For example, the
30217 * `$isEmpty()` implementation for `input[text]` checks the length of the `$viewValue`. When developing
30218 * custom controls, `$isEmpty()` can be overwritten to account for a $viewValue that is not string-based.
30219 *
30220 * @example
30221 * <example name="ngRequiredDirective" module="ngRequiredExample">
30222 * <file name="index.html">
30223 * <script>
30224 * angular.module('ngRequiredExample', [])
30225 * .controller('ExampleController', ['$scope', function($scope) {
30226 * $scope.required = true;
30227 * }]);
30228 * </script>
30229 * <div ng-controller="ExampleController">
30230 * <form name="form">
30231 * <label for="required">Toggle required: </label>
30232 * <input type="checkbox" ng-model="required" id="required" />
30233 * <br>
30234 * <label for="input">This input must be filled if `required` is true: </label>
30235 * <input type="text" ng-model="model" id="input" name="input" ng-required="required" /><br>
30236 * <hr>
30237 * required error set? = <code>{{form.input.$error.required}}</code><br>
30238 * model = <code>{{model}}</code>
30239 * </form>
30240 * </div>
30241 * </file>
30242 * <file name="protractor.js" type="protractor">
30243 var required = element(by.binding('form.input.$error.required'));
30244 var model = element(by.binding('model'));
30245 var input = element(by.id('input'));
30246
30247 it('should set the required error', function() {
30248 expect(required.getText()).toContain('true');
30249
30250 input.sendKeys('123');
30251 expect(required.getText()).not.toContain('true');
30252 expect(model.getText()).toContain('123');
30253 });
30254 * </file>
30255 * </example>
30256 */
30257var requiredDirective = function() {
30258 return {
30259 restrict: 'A',
30260 require: '?ngModel',
30261 link: function(scope, elm, attr, ctrl) {
30262 if (!ctrl) return;
30263 attr.required = true; // force truthy in case we are on non input element
30264
30265 ctrl.$validators.required = function(modelValue, viewValue) {
30266 return !attr.required || !ctrl.$isEmpty(viewValue);
30267 };
30268
30269 attr.$observe('required', function() {
30270 ctrl.$validate();
30271 });
30272 }
30273 };
30274};
30275
30276/**
30277 * @ngdoc directive
30278 * @name ngPattern
30279 *
30280 * @description
30281 *
30282 * ngPattern adds the pattern {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30283 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
30284 *
30285 * The validator sets the `pattern` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
30286 * does not match a RegExp which is obtained by evaluating the Angular expression given in the
30287 * `ngPattern` attribute value:
30288 * * If the expression evaluates to a RegExp object, then this is used directly.
30289 * * If the expression evaluates to a string, then it will be converted to a RegExp after wrapping it
30290 * in `^` and `$` characters. For instance, `"abc"` will be converted to `new RegExp('^abc$')`.
30291 *
30292 * <div class="alert alert-info">
30293 * **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
30294 * start at the index of the last search's match, thus not taking the whole input value into
30295 * account.
30296 * </div>
30297 *
30298 * <div class="alert alert-info">
30299 * **Note:** This directive is also added when the plain `pattern` attribute is used, with two
30300 * differences:
30301 * <ol>
30302 * <li>
30303 * `ngPattern` does not set the `pattern` attribute and therefore HTML5 constraint validation is
30304 * not available.
30305 * </li>
30306 * <li>
30307 * The `ngPattern` attribute must be an expression, while the `pattern` value must be
30308 * interpolated.
30309 * </li>
30310 * </ol>
30311 * </div>
30312 *
30313 * @example
30314 * <example name="ngPatternDirective" module="ngPatternExample">
30315 * <file name="index.html">
30316 * <script>
30317 * angular.module('ngPatternExample', [])
30318 * .controller('ExampleController', ['$scope', function($scope) {
30319 * $scope.regex = '\\d+';
30320 * }]);
30321 * </script>
30322 * <div ng-controller="ExampleController">
30323 * <form name="form">
30324 * <label for="regex">Set a pattern (regex string): </label>
30325 * <input type="text" ng-model="regex" id="regex" />
30326 * <br>
30327 * <label for="input">This input is restricted by the current pattern: </label>
30328 * <input type="text" ng-model="model" id="input" name="input" ng-pattern="regex" /><br>
30329 * <hr>
30330 * input valid? = <code>{{form.input.$valid}}</code><br>
30331 * model = <code>{{model}}</code>
30332 * </form>
30333 * </div>
30334 * </file>
30335 * <file name="protractor.js" type="protractor">
30336 var model = element(by.binding('model'));
30337 var input = element(by.id('input'));
30338
30339 it('should validate the input with the default pattern', function() {
30340 input.sendKeys('aaa');
30341 expect(model.getText()).not.toContain('aaa');
30342
30343 input.clear().then(function() {
30344 input.sendKeys('123');
30345 expect(model.getText()).toContain('123');
30346 });
30347 });
30348 * </file>
30349 * </example>
30350 */
30351var patternDirective = function() {
30352 return {
30353 restrict: 'A',
30354 require: '?ngModel',
30355 link: function(scope, elm, attr, ctrl) {
30356 if (!ctrl) return;
30357
30358 var regexp, patternExp = attr.ngPattern || attr.pattern;
30359 attr.$observe('pattern', function(regex) {
30360 if (isString(regex) && regex.length > 0) {
30361 regex = new RegExp('^' + regex + '$');
30362 }
30363
30364 if (regex && !regex.test) {
30365 throw minErr('ngPattern')('noregexp',
30366 'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
30367 regex, startingTag(elm));
30368 }
30369
30370 regexp = regex || undefined;
30371 ctrl.$validate();
30372 });
30373
30374 ctrl.$validators.pattern = function(modelValue, viewValue) {
30375 // HTML5 pattern constraint validates the input value, so we validate the viewValue
30376 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
30377 };
30378 }
30379 };
30380};
30381
30382/**
30383 * @ngdoc directive
30384 * @name ngMaxlength
30385 *
30386 * @description
30387 *
30388 * ngMaxlength adds the maxlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30389 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
30390 *
30391 * The validator sets the `maxlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
30392 * is longer than the integer obtained by evaluating the Angular expression given in the
30393 * `ngMaxlength` attribute value.
30394 *
30395 * <div class="alert alert-info">
30396 * **Note:** This directive is also added when the plain `maxlength` attribute is used, with two
30397 * differences:
30398 * <ol>
30399 * <li>
30400 * `ngMaxlength` does not set the `maxlength` attribute and therefore HTML5 constraint
30401 * validation is not available.
30402 * </li>
30403 * <li>
30404 * The `ngMaxlength` attribute must be an expression, while the `maxlength` value must be
30405 * interpolated.
30406 * </li>
30407 * </ol>
30408 * </div>
30409 *
30410 * @example
30411 * <example name="ngMaxlengthDirective" module="ngMaxlengthExample">
30412 * <file name="index.html">
30413 * <script>
30414 * angular.module('ngMaxlengthExample', [])
30415 * .controller('ExampleController', ['$scope', function($scope) {
30416 * $scope.maxlength = 5;
30417 * }]);
30418 * </script>
30419 * <div ng-controller="ExampleController">
30420 * <form name="form">
30421 * <label for="maxlength">Set a maxlength: </label>
30422 * <input type="number" ng-model="maxlength" id="maxlength" />
30423 * <br>
30424 * <label for="input">This input is restricted by the current maxlength: </label>
30425 * <input type="text" ng-model="model" id="input" name="input" ng-maxlength="maxlength" /><br>
30426 * <hr>
30427 * input valid? = <code>{{form.input.$valid}}</code><br>
30428 * model = <code>{{model}}</code>
30429 * </form>
30430 * </div>
30431 * </file>
30432 * <file name="protractor.js" type="protractor">
30433 var model = element(by.binding('model'));
30434 var input = element(by.id('input'));
30435
30436 it('should validate the input with the default maxlength', function() {
30437 input.sendKeys('abcdef');
30438 expect(model.getText()).not.toContain('abcdef');
30439
30440 input.clear().then(function() {
30441 input.sendKeys('abcde');
30442 expect(model.getText()).toContain('abcde');
30443 });
30444 });
30445 * </file>
30446 * </example>
30447 */
30448var maxlengthDirective = function() {
30449 return {
30450 restrict: 'A',
30451 require: '?ngModel',
30452 link: function(scope, elm, attr, ctrl) {
30453 if (!ctrl) return;
30454
30455 var maxlength = -1;
30456 attr.$observe('maxlength', function(value) {
30457 var intVal = toInt(value);
30458 maxlength = isNaN(intVal) ? -1 : intVal;
30459 ctrl.$validate();
30460 });
30461 ctrl.$validators.maxlength = function(modelValue, viewValue) {
30462 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
30463 };
30464 }
30465 };
30466};
30467
30468/**
30469 * @ngdoc directive
30470 * @name ngMinlength
30471 *
30472 * @description
30473 *
30474 * ngMinlength adds the minlength {@link ngModel.NgModelController#$validators `validator`} to {@link ngModel `ngModel`}.
30475 * It is most often used for text-based {@link input `input`} controls, but can also be applied to custom text-based controls.
30476 *
30477 * The validator sets the `minlength` error key if the {@link ngModel.NgModelController#$viewValue `ngModel.$viewValue`}
30478 * is shorter than the integer obtained by evaluating the Angular expression given in the
30479 * `ngMinlength` attribute value.
30480 *
30481 * <div class="alert alert-info">
30482 * **Note:** This directive is also added when the plain `minlength` attribute is used, with two
30483 * differences:
30484 * <ol>
30485 * <li>
30486 * `ngMinlength` does not set the `minlength` attribute and therefore HTML5 constraint
30487 * validation is not available.
30488 * </li>
30489 * <li>
30490 * The `ngMinlength` value must be an expression, while the `minlength` value must be
30491 * interpolated.
30492 * </li>
30493 * </ol>
30494 * </div>
30495 *
30496 * @example
30497 * <example name="ngMinlengthDirective" module="ngMinlengthExample">
30498 * <file name="index.html">
30499 * <script>
30500 * angular.module('ngMinlengthExample', [])
30501 * .controller('ExampleController', ['$scope', function($scope) {
30502 * $scope.minlength = 3;
30503 * }]);
30504 * </script>
30505 * <div ng-controller="ExampleController">
30506 * <form name="form">
30507 * <label for="minlength">Set a minlength: </label>
30508 * <input type="number" ng-model="minlength" id="minlength" />
30509 * <br>
30510 * <label for="input">This input is restricted by the current minlength: </label>
30511 * <input type="text" ng-model="model" id="input" name="input" ng-minlength="minlength" /><br>
30512 * <hr>
30513 * input valid? = <code>{{form.input.$valid}}</code><br>
30514 * model = <code>{{model}}</code>
30515 * </form>
30516 * </div>
30517 * </file>
30518 * <file name="protractor.js" type="protractor">
30519 var model = element(by.binding('model'));
30520 var input = element(by.id('input'));
30521
30522 it('should validate the input with the default minlength', function() {
30523 input.sendKeys('ab');
30524 expect(model.getText()).not.toContain('ab');
30525
30526 input.sendKeys('abc');
30527 expect(model.getText()).toContain('abc');
30528 });
30529 * </file>
30530 * </example>
30531 */
30532var minlengthDirective = function() {
30533 return {
30534 restrict: 'A',
30535 require: '?ngModel',
30536 link: function(scope, elm, attr, ctrl) {
30537 if (!ctrl) return;
30538
30539 var minlength = 0;
30540 attr.$observe('minlength', function(value) {
30541 minlength = toInt(value) || 0;
30542 ctrl.$validate();
30543 });
30544 ctrl.$validators.minlength = function(modelValue, viewValue) {
30545 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
30546 };
30547 }
30548 };
30549};
30550
30551if (window.angular.bootstrap) {
30552 //AngularJS is already loaded, so we can return here...
30553 if (window.console) {
30554 console.log('WARNING: Tried to load angular more than once.');
30555 }
30556 return;
30557}
30558
30559//try to bind to jquery now so that one can write jqLite(document).ready()
30560//but we will rebind on bootstrap again.
30561bindJQuery();
30562
30563publishExternalAPI(angular);
30564
30565angular.module("ngLocale", [], ["$provide", function($provide) {
30566var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
30567function getDecimals(n) {
30568 n = n + '';
30569 var i = n.indexOf('.');
30570 return (i == -1) ? 0 : n.length - i - 1;
30571}
30572
30573function getVF(n, opt_precision) {
30574 var v = opt_precision;
30575
30576 if (undefined === v) {
30577 v = Math.min(getDecimals(n), 3);
30578 }
30579
30580 var base = Math.pow(10, v);
30581 var f = ((n * base) | 0) % base;
30582 return {v: v, f: f};
30583}
30584
30585$provide.value("$locale", {
30586 "DATETIME_FORMATS": {
30587 "AMPMS": [
30588 "AM",
30589 "PM"
30590 ],
30591 "DAY": [
30592 "Sunday",
30593 "Monday",
30594 "Tuesday",
30595 "Wednesday",
30596 "Thursday",
30597 "Friday",
30598 "Saturday"
30599 ],
30600 "ERANAMES": [
30601 "Before Christ",
30602 "Anno Domini"
30603 ],
30604 "ERAS": [
30605 "BC",
30606 "AD"
30607 ],
30608 "FIRSTDAYOFWEEK": 6,
30609 "MONTH": [
30610 "January",
30611 "February",
30612 "March",
30613 "April",
30614 "May",
30615 "June",
30616 "July",
30617 "August",
30618 "September",
30619 "October",
30620 "November",
30621 "December"
30622 ],
30623 "SHORTDAY": [
30624 "Sun",
30625 "Mon",
30626 "Tue",
30627 "Wed",
30628 "Thu",
30629 "Fri",
30630 "Sat"
30631 ],
30632 "SHORTMONTH": [
30633 "Jan",
30634 "Feb",
30635 "Mar",
30636 "Apr",
30637 "May",
30638 "Jun",
30639 "Jul",
30640 "Aug",
30641 "Sep",
30642 "Oct",
30643 "Nov",
30644 "Dec"
30645 ],
30646 "STANDALONEMONTH": [
30647 "January",
30648 "February",
30649 "March",
30650 "April",
30651 "May",
30652 "June",
30653 "July",
30654 "August",
30655 "September",
30656 "October",
30657 "November",
30658 "December"
30659 ],
30660 "WEEKENDRANGE": [
30661 5,
30662 6
30663 ],
30664 "fullDate": "EEEE, MMMM d, y",
30665 "longDate": "MMMM d, y",
30666 "medium": "MMM d, y h:mm:ss a",
30667 "mediumDate": "MMM d, y",
30668 "mediumTime": "h:mm:ss a",
30669 "short": "M/d/yy h:mm a",
30670 "shortDate": "M/d/yy",
30671 "shortTime": "h:mm a"
30672 },
30673 "NUMBER_FORMATS": {
30674 "CURRENCY_SYM": "$",
30675 "DECIMAL_SEP": ".",
30676 "GROUP_SEP": ",",
30677 "PATTERNS": [
30678 {
30679 "gSize": 3,
30680 "lgSize": 3,
30681 "maxFrac": 3,
30682 "minFrac": 0,
30683 "minInt": 1,
30684 "negPre": "-",
30685 "negSuf": "",
30686 "posPre": "",
30687 "posSuf": ""
30688 },
30689 {
30690 "gSize": 3,
30691 "lgSize": 3,
30692 "maxFrac": 2,
30693 "minFrac": 2,
30694 "minInt": 1,
30695 "negPre": "-\u00a4",
30696 "negSuf": "",
30697 "posPre": "\u00a4",
30698 "posSuf": ""
30699 }
30700 ]
30701 },
30702 "id": "en-us",
30703 "localeID": "en_US",
30704 "pluralCat": function(n, opt_precision) { var i = n | 0; var vf = getVF(n, opt_precision); if (i == 1 && vf.v == 0) { return PLURAL_CATEGORY.ONE; } return PLURAL_CATEGORY.OTHER;}
30705});
30706}]);
30707
30708 jqLite(document).ready(function() {
30709 angularInit(document, bootstrap);
30710 });
30711
30712})(window, document);
30713
30714!window.angular.$$csp().noInlineStyle && window.angular.element(document.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;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>');