OSDN Git Service

Typescript, rearrangement project directory, develop zephyr command
[eos/zephyr.git] / front-end / dist / bundle.js
1 /******/ (function(modules) { // webpackBootstrap
2 /******/        // The module cache
3 /******/        var installedModules = {};
4
5 /******/        // The require function
6 /******/        function __webpack_require__(moduleId) {
7
8 /******/                // Check if module is in cache
9 /******/                if(installedModules[moduleId])
10 /******/                        return installedModules[moduleId].exports;
11
12 /******/                // Create a new module (and put it into the cache)
13 /******/                var module = installedModules[moduleId] = {
14 /******/                        exports: {},
15 /******/                        id: moduleId,
16 /******/                        loaded: false
17 /******/                };
18
19 /******/                // Execute the module function
20 /******/                modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
21
22 /******/                // Flag the module as loaded
23 /******/                module.loaded = true;
24
25 /******/                // Return the exports of the module
26 /******/                return module.exports;
27 /******/        }
28
29
30 /******/        // expose the modules object (__webpack_modules__)
31 /******/        __webpack_require__.m = modules;
32
33 /******/        // expose the module cache
34 /******/        __webpack_require__.c = installedModules;
35
36 /******/        // __webpack_public_path__
37 /******/        __webpack_require__.p = "";
38
39 /******/        // Load entry module and return exports
40 /******/        return __webpack_require__(0);
41 /******/ })
42 /************************************************************************/
43 /******/ ([
44 /* 0 */
45 /***/ function(module, exports, __webpack_require__) {
46
47         __webpack_require__(1);
48         __webpack_require__(3);
49         __webpack_require__(4);
50         __webpack_require__(6);
51         __webpack_require__(8);
52         __webpack_require__(9);
53         __webpack_require__(10);
54         __webpack_require__(11);
55         __webpack_require__(12);
56         __webpack_require__(13);
57         __webpack_require__(14);
58         __webpack_require__(15);
59         __webpack_require__(16);
60         __webpack_require__(17);
61
62
63 /***/ },
64 /* 1 */
65 /***/ function(module, exports, __webpack_require__) {
66
67         __webpack_require__(2);
68         module.exports = angular;
69
70
71 /***/ },
72 /* 2 */
73 /***/ function(module, exports) {
74
75         /**
76          * @license AngularJS v1.4.8
77          * (c) 2010-2015 Google, Inc. http://angularjs.org
78          * License: MIT
79          */
80         (function(window, document, undefined) {'use strict';
81
82         /**
83          * @description
84          *
85          * This object provides a utility for producing rich Error messages within
86          * Angular. It can be called as follows:
87          *
88          * var exampleMinErr = minErr('example');
89          * throw exampleMinErr('one', 'This {0} is {1}', foo, bar);
90          *
91          * The above creates an instance of minErr in the example namespace. The
92          * resulting error will have a namespaced error code of example.one.  The
93          * resulting error will replace {0} with the value of foo, and {1} with the
94          * value of bar. The object is not restricted in the number of arguments it can
95          * take.
96          *
97          * If fewer arguments are specified than necessary for interpolation, the extra
98          * interpolation markers will be preserved in the final string.
99          *
100          * Since data will be parsed statically during a build step, some restrictions
101          * are applied with respect to how minErr instances are created and called.
102          * Instances should have names of the form namespaceMinErr for a minErr created
103          * using minErr('namespace') . Error codes, namespaces and template strings
104          * should all be static strings, not variables or general expressions.
105          *
106          * @param {string} module The namespace to use for the new minErr instance.
107          * @param {function} ErrorConstructor Custom error constructor to be instantiated when returning
108          *   error from returned function, for cases when a particular type of error is useful.
109          * @returns {function(code:string, template:string, ...templateArgs): Error} minErr instance
110          */
111
112         function minErr(module, ErrorConstructor) {
113           ErrorConstructor = ErrorConstructor || Error;
114           return function() {
115             var SKIP_INDEXES = 2;
116
117             var templateArgs = arguments,
118               code = templateArgs[0],
119               message = '[' + (module ? module + ':' : '') + code + '] ',
120               template = templateArgs[1],
121               paramPrefix, i;
122
123             message += template.replace(/\{\d+\}/g, function(match) {
124               var index = +match.slice(1, -1),
125                 shiftedIndex = index + SKIP_INDEXES;
126
127               if (shiftedIndex < templateArgs.length) {
128                 return toDebugString(templateArgs[shiftedIndex]);
129               }
130
131               return match;
132             });
133
134             message += '\nhttp://errors.angularjs.org/1.4.8/' +
135               (module ? module + '/' : '') + code;
136
137             for (i = SKIP_INDEXES, paramPrefix = '?'; i < templateArgs.length; i++, paramPrefix = '&') {
138               message += paramPrefix + 'p' + (i - SKIP_INDEXES) + '=' +
139                 encodeURIComponent(toDebugString(templateArgs[i]));
140             }
141
142             return new ErrorConstructor(message);
143           };
144         }
145
146         /* We need to tell jshint what variables are being exported */
147         /* global angular: true,
148           msie: true,
149           jqLite: true,
150           jQuery: true,
151           slice: true,
152           splice: true,
153           push: true,
154           toString: true,
155           ngMinErr: true,
156           angularModule: true,
157           uid: true,
158           REGEX_STRING_REGEXP: true,
159           VALIDITY_STATE_PROPERTY: true,
160
161           lowercase: true,
162           uppercase: true,
163           manualLowercase: true,
164           manualUppercase: true,
165           nodeName_: true,
166           isArrayLike: true,
167           forEach: true,
168           forEachSorted: true,
169           reverseParams: true,
170           nextUid: true,
171           setHashKey: true,
172           extend: true,
173           toInt: true,
174           inherit: true,
175           merge: true,
176           noop: true,
177           identity: true,
178           valueFn: true,
179           isUndefined: true,
180           isDefined: true,
181           isObject: true,
182           isBlankObject: true,
183           isString: true,
184           isNumber: true,
185           isDate: true,
186           isArray: true,
187           isFunction: true,
188           isRegExp: true,
189           isWindow: true,
190           isScope: true,
191           isFile: true,
192           isFormData: true,
193           isBlob: true,
194           isBoolean: true,
195           isPromiseLike: true,
196           trim: true,
197           escapeForRegexp: true,
198           isElement: true,
199           makeMap: true,
200           includes: true,
201           arrayRemove: true,
202           copy: true,
203           shallowCopy: true,
204           equals: true,
205           csp: true,
206           jq: true,
207           concat: true,
208           sliceArgs: true,
209           bind: true,
210           toJsonReplacer: true,
211           toJson: true,
212           fromJson: true,
213           convertTimezoneToLocal: true,
214           timezoneToOffset: true,
215           startingTag: true,
216           tryDecodeURIComponent: true,
217           parseKeyValue: true,
218           toKeyValue: true,
219           encodeUriSegment: true,
220           encodeUriQuery: true,
221           angularInit: true,
222           bootstrap: true,
223           getTestability: true,
224           snake_case: true,
225           bindJQuery: true,
226           assertArg: true,
227           assertArgFn: true,
228           assertNotHasOwnProperty: true,
229           getter: true,
230           getBlockNodes: true,
231           hasOwnProperty: true,
232           createMap: true,
233
234           NODE_TYPE_ELEMENT: true,
235           NODE_TYPE_ATTRIBUTE: true,
236           NODE_TYPE_TEXT: true,
237           NODE_TYPE_COMMENT: true,
238           NODE_TYPE_DOCUMENT: true,
239           NODE_TYPE_DOCUMENT_FRAGMENT: true,
240         */
241
242         ////////////////////////////////////
243
244         /**
245          * @ngdoc module
246          * @name ng
247          * @module ng
248          * @description
249          *
250          * # ng (core module)
251          * The ng module is loaded by default when an AngularJS application is started. The module itself
252          * contains the essential components for an AngularJS application to function. The table below
253          * lists a high level breakdown of each of the services/factories, filters, directives and testing
254          * components available within this core module.
255          *
256          * <div doc-module-components="ng"></div>
257          */
258
259         var REGEX_STRING_REGEXP = /^\/(.+)\/([a-z]*)$/;
260
261         // The name of a form control's ValidityState property.
262         // This is used so that it's possible for internal tests to create mock ValidityStates.
263         var VALIDITY_STATE_PROPERTY = 'validity';
264
265         /**
266          * @ngdoc function
267          * @name angular.lowercase
268          * @module ng
269          * @kind function
270          *
271          * @description Converts the specified string to lowercase.
272          * @param {string} string String to be converted to lowercase.
273          * @returns {string} Lowercased string.
274          */
275         var lowercase = function(string) {return isString(string) ? string.toLowerCase() : string;};
276         var hasOwnProperty = Object.prototype.hasOwnProperty;
277
278         /**
279          * @ngdoc function
280          * @name angular.uppercase
281          * @module ng
282          * @kind function
283          *
284          * @description Converts the specified string to uppercase.
285          * @param {string} string String to be converted to uppercase.
286          * @returns {string} Uppercased string.
287          */
288         var uppercase = function(string) {return isString(string) ? string.toUpperCase() : string;};
289
290
291         var manualLowercase = function(s) {
292           /* jshint bitwise: false */
293           return isString(s)
294               ? s.replace(/[A-Z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) | 32);})
295               : s;
296         };
297         var manualUppercase = function(s) {
298           /* jshint bitwise: false */
299           return isString(s)
300               ? s.replace(/[a-z]/g, function(ch) {return String.fromCharCode(ch.charCodeAt(0) & ~32);})
301               : s;
302         };
303
304
305         // String#toLowerCase and String#toUpperCase don't produce correct results in browsers with Turkish
306         // locale, for this reason we need to detect this case and redefine lowercase/uppercase methods
307         // with correct but slower alternatives.
308         if ('i' !== 'I'.toLowerCase()) {
309           lowercase = manualLowercase;
310           uppercase = manualUppercase;
311         }
312
313
314         var
315             msie,             // holds major version number for IE, or NaN if UA is not IE.
316             jqLite,           // delay binding since jQuery could be loaded after us.
317             jQuery,           // delay binding
318             slice             = [].slice,
319             splice            = [].splice,
320             push              = [].push,
321             toString          = Object.prototype.toString,
322             getPrototypeOf    = Object.getPrototypeOf,
323             ngMinErr          = minErr('ng'),
324
325             /** @name angular */
326             angular           = window.angular || (window.angular = {}),
327             angularModule,
328             uid               = 0;
329
330         /**
331          * documentMode is an IE-only property
332          * http://msdn.microsoft.com/en-us/library/ie/cc196988(v=vs.85).aspx
333          */
334         msie = document.documentMode;
335
336
337         /**
338          * @private
339          * @param {*} obj
340          * @return {boolean} Returns true if `obj` is an array or array-like object (NodeList, Arguments,
341          *                   String ...)
342          */
343         function isArrayLike(obj) {
344
345           // `null`, `undefined` and `window` are not array-like
346           if (obj == null || isWindow(obj)) return false;
347
348           // arrays, strings and jQuery/jqLite objects are array like
349           // * jqLite is either the jQuery or jqLite constructor function
350           // * we have to check the existance of jqLite first as this method is called
351           //   via the forEach method when constructing the jqLite object in the first place
352           if (isArray(obj) || isString(obj) || (jqLite && obj instanceof jqLite)) return true;
353
354           // Support: iOS 8.2 (not reproducible in simulator)
355           // "length" in obj used to prevent JIT error (gh-11508)
356           var length = "length" in Object(obj) && obj.length;
357
358           // NodeList objects (with `item` method) and
359           // other objects with suitable length characteristics are array-like
360           return isNumber(length) &&
361             (length >= 0 && (length - 1) in obj || typeof obj.item == 'function');
362         }
363
364         /**
365          * @ngdoc function
366          * @name angular.forEach
367          * @module ng
368          * @kind function
369          *
370          * @description
371          * Invokes the `iterator` function once for each item in `obj` collection, which can be either an
372          * object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
373          * is the value of an object property or an array element, `key` is the object property key or
374          * array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
375          *
376          * It is worth noting that `.forEach` does not iterate over inherited properties because it filters
377          * using the `hasOwnProperty` method.
378          *
379          * Unlike ES262's
380          * [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
381          * Providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
382          * return the value provided.
383          *
384            ```js
385              var values = {name: 'misko', gender: 'male'};
386              var log = [];
387              angular.forEach(values, function(value, key) {
388                this.push(key + ': ' + value);
389              }, log);
390              expect(log).toEqual(['name: misko', 'gender: male']);
391            ```
392          *
393          * @param {Object|Array} obj Object to iterate over.
394          * @param {Function} iterator Iterator function.
395          * @param {Object=} context Object to become context (`this`) for the iterator function.
396          * @returns {Object|Array} Reference to `obj`.
397          */
398
399         function forEach(obj, iterator, context) {
400           var key, length;
401           if (obj) {
402             if (isFunction(obj)) {
403               for (key in obj) {
404                 // Need to check if hasOwnProperty exists,
405                 // as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
406                 if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
407                   iterator.call(context, obj[key], key, obj);
408                 }
409               }
410             } else if (isArray(obj) || isArrayLike(obj)) {
411               var isPrimitive = typeof obj !== 'object';
412               for (key = 0, length = obj.length; key < length; key++) {
413                 if (isPrimitive || key in obj) {
414                   iterator.call(context, obj[key], key, obj);
415                 }
416               }
417             } else if (obj.forEach && obj.forEach !== forEach) {
418                 obj.forEach(iterator, context, obj);
419             } else if (isBlankObject(obj)) {
420               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
421               for (key in obj) {
422                 iterator.call(context, obj[key], key, obj);
423               }
424             } else if (typeof obj.hasOwnProperty === 'function') {
425               // Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
426               for (key in obj) {
427                 if (obj.hasOwnProperty(key)) {
428                   iterator.call(context, obj[key], key, obj);
429                 }
430               }
431             } else {
432               // Slow path for objects which do not have a method `hasOwnProperty`
433               for (key in obj) {
434                 if (hasOwnProperty.call(obj, key)) {
435                   iterator.call(context, obj[key], key, obj);
436                 }
437               }
438             }
439           }
440           return obj;
441         }
442
443         function forEachSorted(obj, iterator, context) {
444           var keys = Object.keys(obj).sort();
445           for (var i = 0; i < keys.length; i++) {
446             iterator.call(context, obj[keys[i]], keys[i]);
447           }
448           return keys;
449         }
450
451
452         /**
453          * when using forEach the params are value, key, but it is often useful to have key, value.
454          * @param {function(string, *)} iteratorFn
455          * @returns {function(*, string)}
456          */
457         function reverseParams(iteratorFn) {
458           return function(value, key) { iteratorFn(key, value); };
459         }
460
461         /**
462          * A consistent way of creating unique IDs in angular.
463          *
464          * Using simple numbers allows us to generate 28.6 million unique ids per second for 10 years before
465          * we hit number precision issues in JavaScript.
466          *
467          * Math.pow(2,53) / 60 / 60 / 24 / 365 / 10 = 28.6M
468          *
469          * @returns {number} an unique alpha-numeric string
470          */
471         function nextUid() {
472           return ++uid;
473         }
474
475
476         /**
477          * Set or clear the hashkey for an object.
478          * @param obj object
479          * @param h the hashkey (!truthy to delete the hashkey)
480          */
481         function setHashKey(obj, h) {
482           if (h) {
483             obj.$$hashKey = h;
484           } else {
485             delete obj.$$hashKey;
486           }
487         }
488
489
490         function baseExtend(dst, objs, deep) {
491           var h = dst.$$hashKey;
492
493           for (var i = 0, ii = objs.length; i < ii; ++i) {
494             var obj = objs[i];
495             if (!isObject(obj) && !isFunction(obj)) continue;
496             var keys = Object.keys(obj);
497             for (var j = 0, jj = keys.length; j < jj; j++) {
498               var key = keys[j];
499               var src = obj[key];
500
501               if (deep && isObject(src)) {
502                 if (isDate(src)) {
503                   dst[key] = new Date(src.valueOf());
504                 } else if (isRegExp(src)) {
505                   dst[key] = new RegExp(src);
506                 } else if (src.nodeName) {
507                   dst[key] = src.cloneNode(true);
508                 } else if (isElement(src)) {
509                   dst[key] = src.clone();
510                 } else {
511                   if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
512                   baseExtend(dst[key], [src], true);
513                 }
514               } else {
515                 dst[key] = src;
516               }
517             }
518           }
519
520           setHashKey(dst, h);
521           return dst;
522         }
523
524         /**
525          * @ngdoc function
526          * @name angular.extend
527          * @module ng
528          * @kind function
529          *
530          * @description
531          * Extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
532          * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
533          * by passing an empty object as the target: `var object = angular.extend({}, object1, object2)`.
534          *
535          * **Note:** Keep in mind that `angular.extend` does not support recursive merge (deep copy). Use
536          * {@link angular.merge} for this.
537          *
538          * @param {Object} dst Destination object.
539          * @param {...Object} src Source object(s).
540          * @returns {Object} Reference to `dst`.
541          */
542         function extend(dst) {
543           return baseExtend(dst, slice.call(arguments, 1), false);
544         }
545
546
547         /**
548         * @ngdoc function
549         * @name angular.merge
550         * @module ng
551         * @kind function
552         *
553         * @description
554         * Deeply extends the destination object `dst` by copying own enumerable properties from the `src` object(s)
555         * to `dst`. You can specify multiple `src` objects. If you want to preserve original objects, you can do so
556         * by passing an empty object as the target: `var object = angular.merge({}, object1, object2)`.
557         *
558         * Unlike {@link angular.extend extend()}, `merge()` recursively descends into object properties of source
559         * objects, performing a deep copy.
560         *
561         * @param {Object} dst Destination object.
562         * @param {...Object} src Source object(s).
563         * @returns {Object} Reference to `dst`.
564         */
565         function merge(dst) {
566           return baseExtend(dst, slice.call(arguments, 1), true);
567         }
568
569
570
571         function toInt(str) {
572           return parseInt(str, 10);
573         }
574
575
576         function inherit(parent, extra) {
577           return extend(Object.create(parent), extra);
578         }
579
580         /**
581          * @ngdoc function
582          * @name angular.noop
583          * @module ng
584          * @kind function
585          *
586          * @description
587          * A function that performs no operations. This function can be useful when writing code in the
588          * functional style.
589            ```js
590              function foo(callback) {
591                var result = calculateResult();
592                (callback || angular.noop)(result);
593              }
594            ```
595          */
596         function noop() {}
597         noop.$inject = [];
598
599
600         /**
601          * @ngdoc function
602          * @name angular.identity
603          * @module ng
604          * @kind function
605          *
606          * @description
607          * A function that returns its first argument. This function is useful when writing code in the
608          * functional style.
609          *
610            ```js
611              function transformer(transformationFn, value) {
612                return (transformationFn || angular.identity)(value);
613              };
614            ```
615           * @param {*} value to be returned.
616           * @returns {*} the value passed in.
617          */
618         function identity($) {return $;}
619         identity.$inject = [];
620
621
622         function valueFn(value) {return function() {return value;};}
623
624         function hasCustomToString(obj) {
625           return isFunction(obj.toString) && obj.toString !== toString;
626         }
627
628
629         /**
630          * @ngdoc function
631          * @name angular.isUndefined
632          * @module ng
633          * @kind function
634          *
635          * @description
636          * Determines if a reference is undefined.
637          *
638          * @param {*} value Reference to check.
639          * @returns {boolean} True if `value` is undefined.
640          */
641         function isUndefined(value) {return typeof value === 'undefined';}
642
643
644         /**
645          * @ngdoc function
646          * @name angular.isDefined
647          * @module ng
648          * @kind function
649          *
650          * @description
651          * Determines if a reference is defined.
652          *
653          * @param {*} value Reference to check.
654          * @returns {boolean} True if `value` is defined.
655          */
656         function isDefined(value) {return typeof value !== 'undefined';}
657
658
659         /**
660          * @ngdoc function
661          * @name angular.isObject
662          * @module ng
663          * @kind function
664          *
665          * @description
666          * Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
667          * considered to be objects. Note that JavaScript arrays are objects.
668          *
669          * @param {*} value Reference to check.
670          * @returns {boolean} True if `value` is an `Object` but not `null`.
671          */
672         function isObject(value) {
673           // http://jsperf.com/isobject4
674           return value !== null && typeof value === 'object';
675         }
676
677
678         /**
679          * Determine if a value is an object with a null prototype
680          *
681          * @returns {boolean} True if `value` is an `Object` with a null prototype
682          */
683         function isBlankObject(value) {
684           return value !== null && typeof value === 'object' && !getPrototypeOf(value);
685         }
686
687
688         /**
689          * @ngdoc function
690          * @name angular.isString
691          * @module ng
692          * @kind function
693          *
694          * @description
695          * Determines if a reference is a `String`.
696          *
697          * @param {*} value Reference to check.
698          * @returns {boolean} True if `value` is a `String`.
699          */
700         function isString(value) {return typeof value === 'string';}
701
702
703         /**
704          * @ngdoc function
705          * @name angular.isNumber
706          * @module ng
707          * @kind function
708          *
709          * @description
710          * Determines if a reference is a `Number`.
711          *
712          * This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
713          *
714          * If you wish to exclude these then you can use the native
715          * [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
716          * method.
717          *
718          * @param {*} value Reference to check.
719          * @returns {boolean} True if `value` is a `Number`.
720          */
721         function isNumber(value) {return typeof value === 'number';}
722
723
724         /**
725          * @ngdoc function
726          * @name angular.isDate
727          * @module ng
728          * @kind function
729          *
730          * @description
731          * Determines if a value is a date.
732          *
733          * @param {*} value Reference to check.
734          * @returns {boolean} True if `value` is a `Date`.
735          */
736         function isDate(value) {
737           return toString.call(value) === '[object Date]';
738         }
739
740
741         /**
742          * @ngdoc function
743          * @name angular.isArray
744          * @module ng
745          * @kind function
746          *
747          * @description
748          * Determines if a reference is an `Array`.
749          *
750          * @param {*} value Reference to check.
751          * @returns {boolean} True if `value` is an `Array`.
752          */
753         var isArray = Array.isArray;
754
755         /**
756          * @ngdoc function
757          * @name angular.isFunction
758          * @module ng
759          * @kind function
760          *
761          * @description
762          * Determines if a reference is a `Function`.
763          *
764          * @param {*} value Reference to check.
765          * @returns {boolean} True if `value` is a `Function`.
766          */
767         function isFunction(value) {return typeof value === 'function';}
768
769
770         /**
771          * Determines if a value is a regular expression object.
772          *
773          * @private
774          * @param {*} value Reference to check.
775          * @returns {boolean} True if `value` is a `RegExp`.
776          */
777         function isRegExp(value) {
778           return toString.call(value) === '[object RegExp]';
779         }
780
781
782         /**
783          * Checks if `obj` is a window object.
784          *
785          * @private
786          * @param {*} obj Object to check
787          * @returns {boolean} True if `obj` is a window obj.
788          */
789         function isWindow(obj) {
790           return obj && obj.window === obj;
791         }
792
793
794         function isScope(obj) {
795           return obj && obj.$evalAsync && obj.$watch;
796         }
797
798
799         function isFile(obj) {
800           return toString.call(obj) === '[object File]';
801         }
802
803
804         function isFormData(obj) {
805           return toString.call(obj) === '[object FormData]';
806         }
807
808
809         function isBlob(obj) {
810           return toString.call(obj) === '[object Blob]';
811         }
812
813
814         function isBoolean(value) {
815           return typeof value === 'boolean';
816         }
817
818
819         function isPromiseLike(obj) {
820           return obj && isFunction(obj.then);
821         }
822
823
824         var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/;
825         function isTypedArray(value) {
826           return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value));
827         }
828
829
830         var trim = function(value) {
831           return isString(value) ? value.trim() : value;
832         };
833
834         // Copied from:
835         // http://docs.closure-library.googlecode.com/git/local_closure_goog_string_string.js.source.html#line1021
836         // Prereq: s is a string.
837         var escapeForRegexp = function(s) {
838           return s.replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1').
839                    replace(/\x08/g, '\\x08');
840         };
841
842
843         /**
844          * @ngdoc function
845          * @name angular.isElement
846          * @module ng
847          * @kind function
848          *
849          * @description
850          * Determines if a reference is a DOM element (or wrapped jQuery element).
851          *
852          * @param {*} value Reference to check.
853          * @returns {boolean} True if `value` is a DOM element (or wrapped jQuery element).
854          */
855         function isElement(node) {
856           return !!(node &&
857             (node.nodeName  // we are a direct element
858             || (node.prop && node.attr && node.find)));  // we have an on and find method part of jQuery API
859         }
860
861         /**
862          * @param str 'key1,key2,...'
863          * @returns {object} in the form of {key1:true, key2:true, ...}
864          */
865         function makeMap(str) {
866           var obj = {}, items = str.split(","), i;
867           for (i = 0; i < items.length; i++) {
868             obj[items[i]] = true;
869           }
870           return obj;
871         }
872
873
874         function nodeName_(element) {
875           return lowercase(element.nodeName || (element[0] && element[0].nodeName));
876         }
877
878         function includes(array, obj) {
879           return Array.prototype.indexOf.call(array, obj) != -1;
880         }
881
882         function arrayRemove(array, value) {
883           var index = array.indexOf(value);
884           if (index >= 0) {
885             array.splice(index, 1);
886           }
887           return index;
888         }
889
890         /**
891          * @ngdoc function
892          * @name angular.copy
893          * @module ng
894          * @kind function
895          *
896          * @description
897          * Creates a deep copy of `source`, which should be an object or an array.
898          *
899          * * If no destination is supplied, a copy of the object or array is created.
900          * * If a destination is provided, all of its elements (for arrays) or properties (for objects)
901          *   are deleted and then all elements/properties from the source are copied to it.
902          * * If `source` is not an object or array (inc. `null` and `undefined`), `source` is returned.
903          * * If `source` is identical to 'destination' an exception will be thrown.
904          *
905          * @param {*} source The source that will be used to make a copy.
906          *                   Can be any type, including primitives, `null`, and `undefined`.
907          * @param {(Object|Array)=} destination Destination into which the source is copied. If
908          *     provided, must be of the same type as `source`.
909          * @returns {*} The copy or updated `destination`, if `destination` was specified.
910          *
911          * @example
912          <example module="copyExample">
913          <file name="index.html">
914          <div ng-controller="ExampleController">
915          <form novalidate class="simple-form">
916          Name: <input type="text" ng-model="user.name" /><br />
917          E-mail: <input type="email" ng-model="user.email" /><br />
918          Gender: <input type="radio" ng-model="user.gender" value="male" />male
919          <input type="radio" ng-model="user.gender" value="female" />female<br />
920          <button ng-click="reset()">RESET</button>
921          <button ng-click="update(user)">SAVE</button>
922          </form>
923          <pre>form = {{user | json}}</pre>
924          <pre>master = {{master | json}}</pre>
925          </div>
926
927          <script>
928           angular.module('copyExample', [])
929             .controller('ExampleController', ['$scope', function($scope) {
930               $scope.master= {};
931
932               $scope.update = function(user) {
933                 // Example with 1 argument
934                 $scope.master= angular.copy(user);
935               };
936
937               $scope.reset = function() {
938                 // Example with 2 arguments
939                 angular.copy($scope.master, $scope.user);
940               };
941
942               $scope.reset();
943             }]);
944          </script>
945          </file>
946          </example>
947          */
948         function copy(source, destination) {
949           var stackSource = [];
950           var stackDest = [];
951
952           if (destination) {
953             if (isTypedArray(destination)) {
954               throw ngMinErr('cpta', "Can't copy! TypedArray destination cannot be mutated.");
955             }
956             if (source === destination) {
957               throw ngMinErr('cpi', "Can't copy! Source and destination are identical.");
958             }
959
960             // Empty the destination object
961             if (isArray(destination)) {
962               destination.length = 0;
963             } else {
964               forEach(destination, function(value, key) {
965                 if (key !== '$$hashKey') {
966                   delete destination[key];
967                 }
968               });
969             }
970
971             stackSource.push(source);
972             stackDest.push(destination);
973             return copyRecurse(source, destination);
974           }
975
976           return copyElement(source);
977
978           function copyRecurse(source, destination) {
979             var h = destination.$$hashKey;
980             var result, key;
981             if (isArray(source)) {
982               for (var i = 0, ii = source.length; i < ii; i++) {
983                 destination.push(copyElement(source[i]));
984               }
985             } else if (isBlankObject(source)) {
986               // createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
987               for (key in source) {
988                 destination[key] = copyElement(source[key]);
989               }
990             } else if (source && typeof source.hasOwnProperty === 'function') {
991               // Slow path, which must rely on hasOwnProperty
992               for (key in source) {
993                 if (source.hasOwnProperty(key)) {
994                   destination[key] = copyElement(source[key]);
995                 }
996               }
997             } else {
998               // Slowest path --- hasOwnProperty can't be called as a method
999               for (key in source) {
1000                 if (hasOwnProperty.call(source, key)) {
1001                   destination[key] = copyElement(source[key]);
1002                 }
1003               }
1004             }
1005             setHashKey(destination, h);
1006             return destination;
1007           }
1008
1009           function copyElement(source) {
1010             // Simple values
1011             if (!isObject(source)) {
1012               return source;
1013             }
1014
1015             // Already copied values
1016             var index = stackSource.indexOf(source);
1017             if (index !== -1) {
1018               return stackDest[index];
1019             }
1020
1021             if (isWindow(source) || isScope(source)) {
1022               throw ngMinErr('cpws',
1023                 "Can't copy! Making copies of Window or Scope instances is not supported.");
1024             }
1025
1026             var needsRecurse = false;
1027             var destination;
1028
1029             if (isArray(source)) {
1030               destination = [];
1031               needsRecurse = true;
1032             } else if (isTypedArray(source)) {
1033               destination = new source.constructor(source);
1034             } else if (isDate(source)) {
1035               destination = new Date(source.getTime());
1036             } else if (isRegExp(source)) {
1037               destination = new RegExp(source.source, source.toString().match(/[^\/]*$/)[0]);
1038               destination.lastIndex = source.lastIndex;
1039             } else if (isFunction(source.cloneNode)) {
1040                 destination = source.cloneNode(true);
1041             } else {
1042               destination = Object.create(getPrototypeOf(source));
1043               needsRecurse = true;
1044             }
1045
1046             stackSource.push(source);
1047             stackDest.push(destination);
1048
1049             return needsRecurse
1050               ? copyRecurse(source, destination)
1051               : destination;
1052           }
1053         }
1054
1055         /**
1056          * Creates a shallow copy of an object, an array or a primitive.
1057          *
1058          * Assumes that there are no proto properties for objects.
1059          */
1060         function shallowCopy(src, dst) {
1061           if (isArray(src)) {
1062             dst = dst || [];
1063
1064             for (var i = 0, ii = src.length; i < ii; i++) {
1065               dst[i] = src[i];
1066             }
1067           } else if (isObject(src)) {
1068             dst = dst || {};
1069
1070             for (var key in src) {
1071               if (!(key.charAt(0) === '$' && key.charAt(1) === '$')) {
1072                 dst[key] = src[key];
1073               }
1074             }
1075           }
1076
1077           return dst || src;
1078         }
1079
1080
1081         /**
1082          * @ngdoc function
1083          * @name angular.equals
1084          * @module ng
1085          * @kind function
1086          *
1087          * @description
1088          * Determines if two objects or two values are equivalent. Supports value types, regular
1089          * expressions, arrays and objects.
1090          *
1091          * Two objects or values are considered equivalent if at least one of the following is true:
1092          *
1093          * * Both objects or values pass `===` comparison.
1094          * * Both objects or values are of the same type and all of their properties are equal by
1095          *   comparing them with `angular.equals`.
1096          * * Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
1097          * * Both values represent the same regular expression (In JavaScript,
1098          *   /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual
1099          *   representation matches).
1100          *
1101          * During a property comparison, properties of `function` type and properties with names
1102          * that begin with `$` are ignored.
1103          *
1104          * Scope and DOMWindow objects are being compared only by identify (`===`).
1105          *
1106          * @param {*} o1 Object or value to compare.
1107          * @param {*} o2 Object or value to compare.
1108          * @returns {boolean} True if arguments are equal.
1109          */
1110         function equals(o1, o2) {
1111           if (o1 === o2) return true;
1112           if (o1 === null || o2 === null) return false;
1113           if (o1 !== o1 && o2 !== o2) return true; // NaN === NaN
1114           var t1 = typeof o1, t2 = typeof o2, length, key, keySet;
1115           if (t1 == t2) {
1116             if (t1 == 'object') {
1117               if (isArray(o1)) {
1118                 if (!isArray(o2)) return false;
1119                 if ((length = o1.length) == o2.length) {
1120                   for (key = 0; key < length; key++) {
1121                     if (!equals(o1[key], o2[key])) return false;
1122                   }
1123                   return true;
1124                 }
1125               } else if (isDate(o1)) {
1126                 if (!isDate(o2)) return false;
1127                 return equals(o1.getTime(), o2.getTime());
1128               } else if (isRegExp(o1)) {
1129                 return isRegExp(o2) ? o1.toString() == o2.toString() : false;
1130               } else {
1131                 if (isScope(o1) || isScope(o2) || isWindow(o1) || isWindow(o2) ||
1132                   isArray(o2) || isDate(o2) || isRegExp(o2)) return false;
1133                 keySet = createMap();
1134                 for (key in o1) {
1135                   if (key.charAt(0) === '$' || isFunction(o1[key])) continue;
1136                   if (!equals(o1[key], o2[key])) return false;
1137                   keySet[key] = true;
1138                 }
1139                 for (key in o2) {
1140                   if (!(key in keySet) &&
1141                       key.charAt(0) !== '$' &&
1142                       isDefined(o2[key]) &&
1143                       !isFunction(o2[key])) return false;
1144                 }
1145                 return true;
1146               }
1147             }
1148           }
1149           return false;
1150         }
1151
1152         var csp = function() {
1153           if (!isDefined(csp.rules)) {
1154
1155
1156             var ngCspElement = (document.querySelector('[ng-csp]') ||
1157                             document.querySelector('[data-ng-csp]'));
1158
1159             if (ngCspElement) {
1160               var ngCspAttribute = ngCspElement.getAttribute('ng-csp') ||
1161                             ngCspElement.getAttribute('data-ng-csp');
1162               csp.rules = {
1163                 noUnsafeEval: !ngCspAttribute || (ngCspAttribute.indexOf('no-unsafe-eval') !== -1),
1164                 noInlineStyle: !ngCspAttribute || (ngCspAttribute.indexOf('no-inline-style') !== -1)
1165               };
1166             } else {
1167               csp.rules = {
1168                 noUnsafeEval: noUnsafeEval(),
1169                 noInlineStyle: false
1170               };
1171             }
1172           }
1173
1174           return csp.rules;
1175
1176           function noUnsafeEval() {
1177             try {
1178               /* jshint -W031, -W054 */
1179               new Function('');
1180               /* jshint +W031, +W054 */
1181               return false;
1182             } catch (e) {
1183               return true;
1184             }
1185           }
1186         };
1187
1188         /**
1189          * @ngdoc directive
1190          * @module ng
1191          * @name ngJq
1192          *
1193          * @element ANY
1194          * @param {string=} ngJq the name of the library available under `window`
1195          * to be used for angular.element
1196          * @description
1197          * Use this directive to force the angular.element library.  This should be
1198          * used to force either jqLite by leaving ng-jq blank or setting the name of
1199          * the jquery variable under window (eg. jQuery).
1200          *
1201          * Since angular looks for this directive when it is loaded (doesn't wait for the
1202          * DOMContentLoaded event), it must be placed on an element that comes before the script
1203          * which loads angular. Also, only the first instance of `ng-jq` will be used and all
1204          * others ignored.
1205          *
1206          * @example
1207          * This example shows how to force jqLite using the `ngJq` directive to the `html` tag.
1208          ```html
1209          <!doctype html>
1210          <html ng-app ng-jq>
1211          ...
1212          ...
1213          </html>
1214          ```
1215          * @example
1216          * This example shows how to use a jQuery based library of a different name.
1217          * The library name must be available at the top most 'window'.
1218          ```html
1219          <!doctype html>
1220          <html ng-app ng-jq="jQueryLib">
1221          ...
1222          ...
1223          </html>
1224          ```
1225          */
1226         var jq = function() {
1227           if (isDefined(jq.name_)) return jq.name_;
1228           var el;
1229           var i, ii = ngAttrPrefixes.length, prefix, name;
1230           for (i = 0; i < ii; ++i) {
1231             prefix = ngAttrPrefixes[i];
1232             if (el = document.querySelector('[' + prefix.replace(':', '\\:') + 'jq]')) {
1233               name = el.getAttribute(prefix + 'jq');
1234               break;
1235             }
1236           }
1237
1238           return (jq.name_ = name);
1239         };
1240
1241         function concat(array1, array2, index) {
1242           return array1.concat(slice.call(array2, index));
1243         }
1244
1245         function sliceArgs(args, startIndex) {
1246           return slice.call(args, startIndex || 0);
1247         }
1248
1249
1250         /* jshint -W101 */
1251         /**
1252          * @ngdoc function
1253          * @name angular.bind
1254          * @module ng
1255          * @kind function
1256          *
1257          * @description
1258          * Returns a function which calls function `fn` bound to `self` (`self` becomes the `this` for
1259          * `fn`). You can supply optional `args` that are prebound to the function. This feature is also
1260          * known as [partial application](http://en.wikipedia.org/wiki/Partial_application), as
1261          * distinguished from [function currying](http://en.wikipedia.org/wiki/Currying#Contrast_with_partial_function_application).
1262          *
1263          * @param {Object} self Context which `fn` should be evaluated in.
1264          * @param {function()} fn Function to be bound.
1265          * @param {...*} args Optional arguments to be prebound to the `fn` function call.
1266          * @returns {function()} Function that wraps the `fn` with all the specified bindings.
1267          */
1268         /* jshint +W101 */
1269         function bind(self, fn) {
1270           var curryArgs = arguments.length > 2 ? sliceArgs(arguments, 2) : [];
1271           if (isFunction(fn) && !(fn instanceof RegExp)) {
1272             return curryArgs.length
1273               ? function() {
1274                   return arguments.length
1275                     ? fn.apply(self, concat(curryArgs, arguments, 0))
1276                     : fn.apply(self, curryArgs);
1277                 }
1278               : function() {
1279                   return arguments.length
1280                     ? fn.apply(self, arguments)
1281                     : fn.call(self);
1282                 };
1283           } else {
1284             // in IE, native methods are not functions so they cannot be bound (note: they don't need to be)
1285             return fn;
1286           }
1287         }
1288
1289
1290         function toJsonReplacer(key, value) {
1291           var val = value;
1292
1293           if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
1294             val = undefined;
1295           } else if (isWindow(value)) {
1296             val = '$WINDOW';
1297           } else if (value &&  document === value) {
1298             val = '$DOCUMENT';
1299           } else if (isScope(value)) {
1300             val = '$SCOPE';
1301           }
1302
1303           return val;
1304         }
1305
1306
1307         /**
1308          * @ngdoc function
1309          * @name angular.toJson
1310          * @module ng
1311          * @kind function
1312          *
1313          * @description
1314          * Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
1315          * stripped since angular uses this notation internally.
1316          *
1317          * @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
1318          * @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
1319          *    If set to an integer, the JSON output will contain that many spaces per indentation.
1320          * @returns {string|undefined} JSON-ified string representing `obj`.
1321          */
1322         function toJson(obj, pretty) {
1323           if (typeof obj === 'undefined') return undefined;
1324           if (!isNumber(pretty)) {
1325             pretty = pretty ? 2 : null;
1326           }
1327           return JSON.stringify(obj, toJsonReplacer, pretty);
1328         }
1329
1330
1331         /**
1332          * @ngdoc function
1333          * @name angular.fromJson
1334          * @module ng
1335          * @kind function
1336          *
1337          * @description
1338          * Deserializes a JSON string.
1339          *
1340          * @param {string} json JSON string to deserialize.
1341          * @returns {Object|Array|string|number} Deserialized JSON string.
1342          */
1343         function fromJson(json) {
1344           return isString(json)
1345               ? JSON.parse(json)
1346               : json;
1347         }
1348
1349
1350         function timezoneToOffset(timezone, fallback) {
1351           var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
1352           return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
1353         }
1354
1355
1356         function addDateMinutes(date, minutes) {
1357           date = new Date(date.getTime());
1358           date.setMinutes(date.getMinutes() + minutes);
1359           return date;
1360         }
1361
1362
1363         function convertTimezoneToLocal(date, timezone, reverse) {
1364           reverse = reverse ? -1 : 1;
1365           var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
1366           return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
1367         }
1368
1369
1370         /**
1371          * @returns {string} Returns the string representation of the element.
1372          */
1373         function startingTag(element) {
1374           element = jqLite(element).clone();
1375           try {
1376             // turns out IE does not let you set .html() on elements which
1377             // are not allowed to have children. So we just ignore it.
1378             element.empty();
1379           } catch (e) {}
1380           var elemHtml = jqLite('<div>').append(element).html();
1381           try {
1382             return element[0].nodeType === NODE_TYPE_TEXT ? lowercase(elemHtml) :
1383                 elemHtml.
1384                   match(/^(<[^>]+>)/)[1].
1385                   replace(/^<([\w\-]+)/, function(match, nodeName) { return '<' + lowercase(nodeName); });
1386           } catch (e) {
1387             return lowercase(elemHtml);
1388           }
1389
1390         }
1391
1392
1393         /////////////////////////////////////////////////
1394
1395         /**
1396          * Tries to decode the URI component without throwing an exception.
1397          *
1398          * @private
1399          * @param str value potential URI component to check.
1400          * @returns {boolean} True if `value` can be decoded
1401          * with the decodeURIComponent function.
1402          */
1403         function tryDecodeURIComponent(value) {
1404           try {
1405             return decodeURIComponent(value);
1406           } catch (e) {
1407             // Ignore any invalid uri component
1408           }
1409         }
1410
1411
1412         /**
1413          * Parses an escaped url query string into key-value pairs.
1414          * @returns {Object.<string,boolean|Array>}
1415          */
1416         function parseKeyValue(/**string*/keyValue) {
1417           var obj = {};
1418           forEach((keyValue || "").split('&'), function(keyValue) {
1419             var splitPoint, key, val;
1420             if (keyValue) {
1421               key = keyValue = keyValue.replace(/\+/g,'%20');
1422               splitPoint = keyValue.indexOf('=');
1423               if (splitPoint !== -1) {
1424                 key = keyValue.substring(0, splitPoint);
1425                 val = keyValue.substring(splitPoint + 1);
1426               }
1427               key = tryDecodeURIComponent(key);
1428               if (isDefined(key)) {
1429                 val = isDefined(val) ? tryDecodeURIComponent(val) : true;
1430                 if (!hasOwnProperty.call(obj, key)) {
1431                   obj[key] = val;
1432                 } else if (isArray(obj[key])) {
1433                   obj[key].push(val);
1434                 } else {
1435                   obj[key] = [obj[key],val];
1436                 }
1437               }
1438             }
1439           });
1440           return obj;
1441         }
1442
1443         function toKeyValue(obj) {
1444           var parts = [];
1445           forEach(obj, function(value, key) {
1446             if (isArray(value)) {
1447               forEach(value, function(arrayValue) {
1448                 parts.push(encodeUriQuery(key, true) +
1449                            (arrayValue === true ? '' : '=' + encodeUriQuery(arrayValue, true)));
1450               });
1451             } else {
1452             parts.push(encodeUriQuery(key, true) +
1453                        (value === true ? '' : '=' + encodeUriQuery(value, true)));
1454             }
1455           });
1456           return parts.length ? parts.join('&') : '';
1457         }
1458
1459
1460         /**
1461          * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
1462          * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set (pchar) allowed in path
1463          * segments:
1464          *    segment       = *pchar
1465          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1466          *    pct-encoded   = "%" HEXDIG HEXDIG
1467          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1468          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1469          *                     / "*" / "+" / "," / ";" / "="
1470          */
1471         function encodeUriSegment(val) {
1472           return encodeUriQuery(val, true).
1473                      replace(/%26/gi, '&').
1474                      replace(/%3D/gi, '=').
1475                      replace(/%2B/gi, '+');
1476         }
1477
1478
1479         /**
1480          * This method is intended for encoding *key* or *value* parts of query component. We need a custom
1481          * method because encodeURIComponent is too aggressive and encodes stuff that doesn't have to be
1482          * encoded per http://tools.ietf.org/html/rfc3986:
1483          *    query       = *( pchar / "/" / "?" )
1484          *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
1485          *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
1486          *    pct-encoded   = "%" HEXDIG HEXDIG
1487          *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
1488          *                     / "*" / "+" / "," / ";" / "="
1489          */
1490         function encodeUriQuery(val, pctEncodeSpaces) {
1491           return encodeURIComponent(val).
1492                      replace(/%40/gi, '@').
1493                      replace(/%3A/gi, ':').
1494                      replace(/%24/g, '$').
1495                      replace(/%2C/gi, ',').
1496                      replace(/%3B/gi, ';').
1497                      replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
1498         }
1499
1500         var ngAttrPrefixes = ['ng-', 'data-ng-', 'ng:', 'x-ng-'];
1501
1502         function getNgAttribute(element, ngAttr) {
1503           var attr, i, ii = ngAttrPrefixes.length;
1504           for (i = 0; i < ii; ++i) {
1505             attr = ngAttrPrefixes[i] + ngAttr;
1506             if (isString(attr = element.getAttribute(attr))) {
1507               return attr;
1508             }
1509           }
1510           return null;
1511         }
1512
1513         /**
1514          * @ngdoc directive
1515          * @name ngApp
1516          * @module ng
1517          *
1518          * @element ANY
1519          * @param {angular.Module} ngApp an optional application
1520          *   {@link angular.module module} name to load.
1521          * @param {boolean=} ngStrictDi if this attribute is present on the app element, the injector will be
1522          *   created in "strict-di" mode. This means that the application will fail to invoke functions which
1523          *   do not use explicit function annotation (and are thus unsuitable for minification), as described
1524          *   in {@link guide/di the Dependency Injection guide}, and useful debugging info will assist in
1525          *   tracking down the root of these bugs.
1526          *
1527          * @description
1528          *
1529          * Use this directive to **auto-bootstrap** an AngularJS application. The `ngApp` directive
1530          * designates the **root element** of the application and is typically placed near the root element
1531          * of the page - e.g. on the `<body>` or `<html>` tags.
1532          *
1533          * Only one AngularJS application can be auto-bootstrapped per HTML document. The first `ngApp`
1534          * found in the document will be used to define the root element to auto-bootstrap as an
1535          * application. To run multiple applications in an HTML document you must manually bootstrap them using
1536          * {@link angular.bootstrap} instead. AngularJS applications cannot be nested within each other.
1537          *
1538          * You can specify an **AngularJS module** to be used as the root module for the application.  This
1539          * module will be loaded into the {@link auto.$injector} when the application is bootstrapped. It
1540          * should contain the application code needed or have dependencies on other modules that will
1541          * contain the code. See {@link angular.module} for more information.
1542          *
1543          * In the example below if the `ngApp` directive were not placed on the `html` element then the
1544          * document would not be compiled, the `AppController` would not be instantiated and the `{{ a+b }}`
1545          * would not be resolved to `3`.
1546          *
1547          * `ngApp` is the easiest, and most common way to bootstrap an application.
1548          *
1549          <example module="ngAppDemo">
1550            <file name="index.html">
1551            <div ng-controller="ngAppDemoController">
1552              I can add: {{a}} + {{b}} =  {{ a+b }}
1553            </div>
1554            </file>
1555            <file name="script.js">
1556            angular.module('ngAppDemo', []).controller('ngAppDemoController', function($scope) {
1557              $scope.a = 1;
1558              $scope.b = 2;
1559            });
1560            </file>
1561          </example>
1562          *
1563          * Using `ngStrictDi`, you would see something like this:
1564          *
1565          <example ng-app-included="true">
1566            <file name="index.html">
1567            <div ng-app="ngAppStrictDemo" ng-strict-di>
1568                <div ng-controller="GoodController1">
1569                    I can add: {{a}} + {{b}} =  {{ a+b }}
1570
1571                    <p>This renders because the controller does not fail to
1572                       instantiate, by using explicit annotation style (see
1573                       script.js for details)
1574                    </p>
1575                </div>
1576
1577                <div ng-controller="GoodController2">
1578                    Name: <input ng-model="name"><br />
1579                    Hello, {{name}}!
1580
1581                    <p>This renders because the controller does not fail to
1582                       instantiate, by using explicit annotation style
1583                       (see script.js for details)
1584                    </p>
1585                </div>
1586
1587                <div ng-controller="BadController">
1588                    I can add: {{a}} + {{b}} =  {{ a+b }}
1589
1590                    <p>The controller could not be instantiated, due to relying
1591                       on automatic function annotations (which are disabled in
1592                       strict mode). As such, the content of this section is not
1593                       interpolated, and there should be an error in your web console.
1594                    </p>
1595                </div>
1596            </div>
1597            </file>
1598            <file name="script.js">
1599            angular.module('ngAppStrictDemo', [])
1600              // BadController will fail to instantiate, due to relying on automatic function annotation,
1601              // rather than an explicit annotation
1602              .controller('BadController', function($scope) {
1603                $scope.a = 1;
1604                $scope.b = 2;
1605              })
1606              // Unlike BadController, GoodController1 and GoodController2 will not fail to be instantiated,
1607              // due to using explicit annotations using the array style and $inject property, respectively.
1608              .controller('GoodController1', ['$scope', function($scope) {
1609                $scope.a = 1;
1610                $scope.b = 2;
1611              }])
1612              .controller('GoodController2', GoodController2);
1613              function GoodController2($scope) {
1614                $scope.name = "World";
1615              }
1616              GoodController2.$inject = ['$scope'];
1617            </file>
1618            <file name="style.css">
1619            div[ng-controller] {
1620                margin-bottom: 1em;
1621                -webkit-border-radius: 4px;
1622                border-radius: 4px;
1623                border: 1px solid;
1624                padding: .5em;
1625            }
1626            div[ng-controller^=Good] {
1627                border-color: #d6e9c6;
1628                background-color: #dff0d8;
1629                color: #3c763d;
1630            }
1631            div[ng-controller^=Bad] {
1632                border-color: #ebccd1;
1633                background-color: #f2dede;
1634                color: #a94442;
1635                margin-bottom: 0;
1636            }
1637            </file>
1638          </example>
1639          */
1640         function angularInit(element, bootstrap) {
1641           var appElement,
1642               module,
1643               config = {};
1644
1645           // The element `element` has priority over any other element
1646           forEach(ngAttrPrefixes, function(prefix) {
1647             var name = prefix + 'app';
1648
1649             if (!appElement && element.hasAttribute && element.hasAttribute(name)) {
1650               appElement = element;
1651               module = element.getAttribute(name);
1652             }
1653           });
1654           forEach(ngAttrPrefixes, function(prefix) {
1655             var name = prefix + 'app';
1656             var candidate;
1657
1658             if (!appElement && (candidate = element.querySelector('[' + name.replace(':', '\\:') + ']'))) {
1659               appElement = candidate;
1660               module = candidate.getAttribute(name);
1661             }
1662           });
1663           if (appElement) {
1664             config.strictDi = getNgAttribute(appElement, "strict-di") !== null;
1665             bootstrap(appElement, module ? [module] : [], config);
1666           }
1667         }
1668
1669         /**
1670          * @ngdoc function
1671          * @name angular.bootstrap
1672          * @module ng
1673          * @description
1674          * Use this function to manually start up angular application.
1675          *
1676          * See: {@link guide/bootstrap Bootstrap}
1677          *
1678          * Note that Protractor based end-to-end tests cannot use this function to bootstrap manually.
1679          * They must use {@link ng.directive:ngApp ngApp}.
1680          *
1681          * Angular will detect if it has been loaded into the browser more than once and only allow the
1682          * first loaded script to be bootstrapped and will report a warning to the browser console for
1683          * each of the subsequent scripts. This prevents strange results in applications, where otherwise
1684          * multiple instances of Angular try to work on the DOM.
1685          *
1686          * ```html
1687          * <!doctype html>
1688          * <html>
1689          * <body>
1690          * <div ng-controller="WelcomeController">
1691          *   {{greeting}}
1692          * </div>
1693          *
1694          * <script src="angular.js"></script>
1695          * <script>
1696          *   var app = angular.module('demo', [])
1697          *   .controller('WelcomeController', function($scope) {
1698          *       $scope.greeting = 'Welcome!';
1699          *   });
1700          *   angular.bootstrap(document, ['demo']);
1701          * </script>
1702          * </body>
1703          * </html>
1704          * ```
1705          *
1706          * @param {DOMElement} element DOM element which is the root of angular application.
1707          * @param {Array<String|Function|Array>=} modules an array of modules to load into the application.
1708          *     Each item in the array should be the name of a predefined module or a (DI annotated)
1709          *     function that will be invoked by the injector as a `config` block.
1710          *     See: {@link angular.module modules}
1711          * @param {Object=} config an object for defining configuration options for the application. The
1712          *     following keys are supported:
1713          *
1714          * * `strictDi` - disable automatic function annotation for the application. This is meant to
1715          *   assist in finding bugs which break minified code. Defaults to `false`.
1716          *
1717          * @returns {auto.$injector} Returns the newly created injector for this app.
1718          */
1719         function bootstrap(element, modules, config) {
1720           if (!isObject(config)) config = {};
1721           var defaultConfig = {
1722             strictDi: false
1723           };
1724           config = extend(defaultConfig, config);
1725           var doBootstrap = function() {
1726             element = jqLite(element);
1727
1728             if (element.injector()) {
1729               var tag = (element[0] === document) ? 'document' : startingTag(element);
1730               //Encode angle brackets to prevent input from being sanitized to empty string #8683
1731               throw ngMinErr(
1732                   'btstrpd',
1733                   "App Already Bootstrapped with this Element '{0}'",
1734                   tag.replace(/</,'&lt;').replace(/>/,'&gt;'));
1735             }
1736
1737             modules = modules || [];
1738             modules.unshift(['$provide', function($provide) {
1739               $provide.value('$rootElement', element);
1740             }]);
1741
1742             if (config.debugInfoEnabled) {
1743               // Pushing so that this overrides `debugInfoEnabled` setting defined in user's `modules`.
1744               modules.push(['$compileProvider', function($compileProvider) {
1745                 $compileProvider.debugInfoEnabled(true);
1746               }]);
1747             }
1748
1749             modules.unshift('ng');
1750             var injector = createInjector(modules, config.strictDi);
1751             injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
1752                function bootstrapApply(scope, element, compile, injector) {
1753                 scope.$apply(function() {
1754                   element.data('$injector', injector);
1755                   compile(element)(scope);
1756                 });
1757               }]
1758             );
1759             return injector;
1760           };
1761
1762           var NG_ENABLE_DEBUG_INFO = /^NG_ENABLE_DEBUG_INFO!/;
1763           var NG_DEFER_BOOTSTRAP = /^NG_DEFER_BOOTSTRAP!/;
1764
1765           if (window && NG_ENABLE_DEBUG_INFO.test(window.name)) {
1766             config.debugInfoEnabled = true;
1767             window.name = window.name.replace(NG_ENABLE_DEBUG_INFO, '');
1768           }
1769
1770           if (window && !NG_DEFER_BOOTSTRAP.test(window.name)) {
1771             return doBootstrap();
1772           }
1773
1774           window.name = window.name.replace(NG_DEFER_BOOTSTRAP, '');
1775           angular.resumeBootstrap = function(extraModules) {
1776             forEach(extraModules, function(module) {
1777               modules.push(module);
1778             });
1779             return doBootstrap();
1780           };
1781
1782           if (isFunction(angular.resumeDeferredBootstrap)) {
1783             angular.resumeDeferredBootstrap();
1784           }
1785         }
1786
1787         /**
1788          * @ngdoc function
1789          * @name angular.reloadWithDebugInfo
1790          * @module ng
1791          * @description
1792          * Use this function to reload the current application with debug information turned on.
1793          * This takes precedence over a call to `$compileProvider.debugInfoEnabled(false)`.
1794          *
1795          * See {@link ng.$compileProvider#debugInfoEnabled} for more.
1796          */
1797         function reloadWithDebugInfo() {
1798           window.name = 'NG_ENABLE_DEBUG_INFO!' + window.name;
1799           window.location.reload();
1800         }
1801
1802         /**
1803          * @name angular.getTestability
1804          * @module ng
1805          * @description
1806          * Get the testability service for the instance of Angular on the given
1807          * element.
1808          * @param {DOMElement} element DOM element which is the root of angular application.
1809          */
1810         function getTestability(rootElement) {
1811           var injector = angular.element(rootElement).injector();
1812           if (!injector) {
1813             throw ngMinErr('test',
1814               'no injector found for element argument to getTestability');
1815           }
1816           return injector.get('$$testability');
1817         }
1818
1819         var SNAKE_CASE_REGEXP = /[A-Z]/g;
1820         function snake_case(name, separator) {
1821           separator = separator || '_';
1822           return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
1823             return (pos ? separator : '') + letter.toLowerCase();
1824           });
1825         }
1826
1827         var bindJQueryFired = false;
1828         var skipDestroyOnNextJQueryCleanData;
1829         function bindJQuery() {
1830           var originalCleanData;
1831
1832           if (bindJQueryFired) {
1833             return;
1834           }
1835
1836           // bind to jQuery if present;
1837           var jqName = jq();
1838           jQuery = isUndefined(jqName) ? window.jQuery :   // use jQuery (if present)
1839                    !jqName             ? undefined     :   // use jqLite
1840                                          window[jqName];   // use jQuery specified by `ngJq`
1841
1842           // Use jQuery if it exists with proper functionality, otherwise default to us.
1843           // Angular 1.2+ requires jQuery 1.7+ for on()/off() support.
1844           // Angular 1.3+ technically requires at least jQuery 2.1+ but it may work with older
1845           // versions. It will not work for sure with jQuery <1.7, though.
1846           if (jQuery && jQuery.fn.on) {
1847             jqLite = jQuery;
1848             extend(jQuery.fn, {
1849               scope: JQLitePrototype.scope,
1850               isolateScope: JQLitePrototype.isolateScope,
1851               controller: JQLitePrototype.controller,
1852               injector: JQLitePrototype.injector,
1853               inheritedData: JQLitePrototype.inheritedData
1854             });
1855
1856             // All nodes removed from the DOM via various jQuery APIs like .remove()
1857             // are passed through jQuery.cleanData. Monkey-patch this method to fire
1858             // the $destroy event on all removed nodes.
1859             originalCleanData = jQuery.cleanData;
1860             jQuery.cleanData = function(elems) {
1861               var events;
1862               if (!skipDestroyOnNextJQueryCleanData) {
1863                 for (var i = 0, elem; (elem = elems[i]) != null; i++) {
1864                   events = jQuery._data(elem, "events");
1865                   if (events && events.$destroy) {
1866                     jQuery(elem).triggerHandler('$destroy');
1867                   }
1868                 }
1869               } else {
1870                 skipDestroyOnNextJQueryCleanData = false;
1871               }
1872               originalCleanData(elems);
1873             };
1874           } else {
1875             jqLite = JQLite;
1876           }
1877
1878           angular.element = jqLite;
1879
1880           // Prevent double-proxying.
1881           bindJQueryFired = true;
1882         }
1883
1884         /**
1885          * throw error if the argument is falsy.
1886          */
1887         function assertArg(arg, name, reason) {
1888           if (!arg) {
1889             throw ngMinErr('areq', "Argument '{0}' is {1}", (name || '?'), (reason || "required"));
1890           }
1891           return arg;
1892         }
1893
1894         function assertArgFn(arg, name, acceptArrayAnnotation) {
1895           if (acceptArrayAnnotation && isArray(arg)) {
1896               arg = arg[arg.length - 1];
1897           }
1898
1899           assertArg(isFunction(arg), name, 'not a function, got ' +
1900               (arg && typeof arg === 'object' ? arg.constructor.name || 'Object' : typeof arg));
1901           return arg;
1902         }
1903
1904         /**
1905          * throw error if the name given is hasOwnProperty
1906          * @param  {String} name    the name to test
1907          * @param  {String} context the context in which the name is used, such as module or directive
1908          */
1909         function assertNotHasOwnProperty(name, context) {
1910           if (name === 'hasOwnProperty') {
1911             throw ngMinErr('badname', "hasOwnProperty is not a valid {0} name", context);
1912           }
1913         }
1914
1915         /**
1916          * Return the value accessible from the object by path. Any undefined traversals are ignored
1917          * @param {Object} obj starting object
1918          * @param {String} path path to traverse
1919          * @param {boolean} [bindFnToScope=true]
1920          * @returns {Object} value as accessible by path
1921          */
1922         //TODO(misko): this function needs to be removed
1923         function getter(obj, path, bindFnToScope) {
1924           if (!path) return obj;
1925           var keys = path.split('.');
1926           var key;
1927           var lastInstance = obj;
1928           var len = keys.length;
1929
1930           for (var i = 0; i < len; i++) {
1931             key = keys[i];
1932             if (obj) {
1933               obj = (lastInstance = obj)[key];
1934             }
1935           }
1936           if (!bindFnToScope && isFunction(obj)) {
1937             return bind(lastInstance, obj);
1938           }
1939           return obj;
1940         }
1941
1942         /**
1943          * Return the DOM siblings between the first and last node in the given array.
1944          * @param {Array} array like object
1945          * @returns {Array} the inputted object or a jqLite collection containing the nodes
1946          */
1947         function getBlockNodes(nodes) {
1948           // TODO(perf): update `nodes` instead of creating a new object?
1949           var node = nodes[0];
1950           var endNode = nodes[nodes.length - 1];
1951           var blockNodes;
1952
1953           for (var i = 1; node !== endNode && (node = node.nextSibling); i++) {
1954             if (blockNodes || nodes[i] !== node) {
1955               if (!blockNodes) {
1956                 blockNodes = jqLite(slice.call(nodes, 0, i));
1957               }
1958               blockNodes.push(node);
1959             }
1960           }
1961
1962           return blockNodes || nodes;
1963         }
1964
1965
1966         /**
1967          * Creates a new object without a prototype. This object is useful for lookup without having to
1968          * guard against prototypically inherited properties via hasOwnProperty.
1969          *
1970          * Related micro-benchmarks:
1971          * - http://jsperf.com/object-create2
1972          * - http://jsperf.com/proto-map-lookup/2
1973          * - http://jsperf.com/for-in-vs-object-keys2
1974          *
1975          * @returns {Object}
1976          */
1977         function createMap() {
1978           return Object.create(null);
1979         }
1980
1981         var NODE_TYPE_ELEMENT = 1;
1982         var NODE_TYPE_ATTRIBUTE = 2;
1983         var NODE_TYPE_TEXT = 3;
1984         var NODE_TYPE_COMMENT = 8;
1985         var NODE_TYPE_DOCUMENT = 9;
1986         var NODE_TYPE_DOCUMENT_FRAGMENT = 11;
1987
1988         /**
1989          * @ngdoc type
1990          * @name angular.Module
1991          * @module ng
1992          * @description
1993          *
1994          * Interface for configuring angular {@link angular.module modules}.
1995          */
1996
1997         function setupModuleLoader(window) {
1998
1999           var $injectorMinErr = minErr('$injector');
2000           var ngMinErr = minErr('ng');
2001
2002           function ensure(obj, name, factory) {
2003             return obj[name] || (obj[name] = factory());
2004           }
2005
2006           var angular = ensure(window, 'angular', Object);
2007
2008           // We need to expose `angular.$$minErr` to modules such as `ngResource` that reference it during bootstrap
2009           angular.$$minErr = angular.$$minErr || minErr;
2010
2011           return ensure(angular, 'module', function() {
2012             /** @type {Object.<string, angular.Module>} */
2013             var modules = {};
2014
2015             /**
2016              * @ngdoc function
2017              * @name angular.module
2018              * @module ng
2019              * @description
2020              *
2021              * The `angular.module` is a global place for creating, registering and retrieving Angular
2022              * modules.
2023              * All modules (angular core or 3rd party) that should be available to an application must be
2024              * registered using this mechanism.
2025              *
2026              * Passing one argument retrieves an existing {@link angular.Module},
2027              * whereas passing more than one argument creates a new {@link angular.Module}
2028              *
2029              *
2030              * # Module
2031              *
2032              * A module is a collection of services, directives, controllers, filters, and configuration information.
2033              * `angular.module` is used to configure the {@link auto.$injector $injector}.
2034              *
2035              * ```js
2036              * // Create a new module
2037              * var myModule = angular.module('myModule', []);
2038              *
2039              * // register a new service
2040              * myModule.value('appName', 'MyCoolApp');
2041              *
2042              * // configure existing services inside initialization blocks.
2043              * myModule.config(['$locationProvider', function($locationProvider) {
2044              *   // Configure existing providers
2045              *   $locationProvider.hashPrefix('!');
2046              * }]);
2047              * ```
2048              *
2049              * Then you can create an injector and load your modules like this:
2050              *
2051              * ```js
2052              * var injector = angular.injector(['ng', 'myModule'])
2053              * ```
2054              *
2055              * However it's more likely that you'll just use
2056              * {@link ng.directive:ngApp ngApp} or
2057              * {@link angular.bootstrap} to simplify this process for you.
2058              *
2059              * @param {!string} name The name of the module to create or retrieve.
2060              * @param {!Array.<string>=} requires If specified then new module is being created. If
2061              *        unspecified then the module is being retrieved for further configuration.
2062              * @param {Function=} configFn Optional configuration function for the module. Same as
2063              *        {@link angular.Module#config Module#config()}.
2064              * @returns {module} new module with the {@link angular.Module} api.
2065              */
2066             return function module(name, requires, configFn) {
2067               var assertNotHasOwnProperty = function(name, context) {
2068                 if (name === 'hasOwnProperty') {
2069                   throw ngMinErr('badname', 'hasOwnProperty is not a valid {0} name', context);
2070                 }
2071               };
2072
2073               assertNotHasOwnProperty(name, 'module');
2074               if (requires && modules.hasOwnProperty(name)) {
2075                 modules[name] = null;
2076               }
2077               return ensure(modules, name, function() {
2078                 if (!requires) {
2079                   throw $injectorMinErr('nomod', "Module '{0}' is not available! You either misspelled " +
2080                      "the module name or forgot to load it. If registering a module ensure that you " +
2081                      "specify the dependencies as the second argument.", name);
2082                 }
2083
2084                 /** @type {!Array.<Array.<*>>} */
2085                 var invokeQueue = [];
2086
2087                 /** @type {!Array.<Function>} */
2088                 var configBlocks = [];
2089
2090                 /** @type {!Array.<Function>} */
2091                 var runBlocks = [];
2092
2093                 var config = invokeLater('$injector', 'invoke', 'push', configBlocks);
2094
2095                 /** @type {angular.Module} */
2096                 var moduleInstance = {
2097                   // Private state
2098                   _invokeQueue: invokeQueue,
2099                   _configBlocks: configBlocks,
2100                   _runBlocks: runBlocks,
2101
2102                   /**
2103                    * @ngdoc property
2104                    * @name angular.Module#requires
2105                    * @module ng
2106                    *
2107                    * @description
2108                    * Holds the list of modules which the injector will load before the current module is
2109                    * loaded.
2110                    */
2111                   requires: requires,
2112
2113                   /**
2114                    * @ngdoc property
2115                    * @name angular.Module#name
2116                    * @module ng
2117                    *
2118                    * @description
2119                    * Name of the module.
2120                    */
2121                   name: name,
2122
2123
2124                   /**
2125                    * @ngdoc method
2126                    * @name angular.Module#provider
2127                    * @module ng
2128                    * @param {string} name service name
2129                    * @param {Function} providerType Construction function for creating new instance of the
2130                    *                                service.
2131                    * @description
2132                    * See {@link auto.$provide#provider $provide.provider()}.
2133                    */
2134                   provider: invokeLaterAndSetModuleName('$provide', 'provider'),
2135
2136                   /**
2137                    * @ngdoc method
2138                    * @name angular.Module#factory
2139                    * @module ng
2140                    * @param {string} name service name
2141                    * @param {Function} providerFunction Function for creating new instance of the service.
2142                    * @description
2143                    * See {@link auto.$provide#factory $provide.factory()}.
2144                    */
2145                   factory: invokeLaterAndSetModuleName('$provide', 'factory'),
2146
2147                   /**
2148                    * @ngdoc method
2149                    * @name angular.Module#service
2150                    * @module ng
2151                    * @param {string} name service name
2152                    * @param {Function} constructor A constructor function that will be instantiated.
2153                    * @description
2154                    * See {@link auto.$provide#service $provide.service()}.
2155                    */
2156                   service: invokeLaterAndSetModuleName('$provide', 'service'),
2157
2158                   /**
2159                    * @ngdoc method
2160                    * @name angular.Module#value
2161                    * @module ng
2162                    * @param {string} name service name
2163                    * @param {*} object Service instance object.
2164                    * @description
2165                    * See {@link auto.$provide#value $provide.value()}.
2166                    */
2167                   value: invokeLater('$provide', 'value'),
2168
2169                   /**
2170                    * @ngdoc method
2171                    * @name angular.Module#constant
2172                    * @module ng
2173                    * @param {string} name constant name
2174                    * @param {*} object Constant value.
2175                    * @description
2176                    * Because the constants are fixed, they get applied before other provide methods.
2177                    * See {@link auto.$provide#constant $provide.constant()}.
2178                    */
2179                   constant: invokeLater('$provide', 'constant', 'unshift'),
2180
2181                    /**
2182                    * @ngdoc method
2183                    * @name angular.Module#decorator
2184                    * @module ng
2185                    * @param {string} The name of the service to decorate.
2186                    * @param {Function} This function will be invoked when the service needs to be
2187                    *                                    instantiated and should return the decorated service instance.
2188                    * @description
2189                    * See {@link auto.$provide#decorator $provide.decorator()}.
2190                    */
2191                   decorator: invokeLaterAndSetModuleName('$provide', 'decorator'),
2192
2193                   /**
2194                    * @ngdoc method
2195                    * @name angular.Module#animation
2196                    * @module ng
2197                    * @param {string} name animation name
2198                    * @param {Function} animationFactory Factory function for creating new instance of an
2199                    *                                    animation.
2200                    * @description
2201                    *
2202                    * **NOTE**: animations take effect only if the **ngAnimate** module is loaded.
2203                    *
2204                    *
2205                    * Defines an animation hook that can be later used with
2206                    * {@link $animate $animate} service and directives that use this service.
2207                    *
2208                    * ```js
2209                    * module.animation('.animation-name', function($inject1, $inject2) {
2210                    *   return {
2211                    *     eventName : function(element, done) {
2212                    *       //code to run the animation
2213                    *       //once complete, then run done()
2214                    *       return function cancellationFunction(element) {
2215                    *         //code to cancel the animation
2216                    *       }
2217                    *     }
2218                    *   }
2219                    * })
2220                    * ```
2221                    *
2222                    * See {@link ng.$animateProvider#register $animateProvider.register()} and
2223                    * {@link ngAnimate ngAnimate module} for more information.
2224                    */
2225                   animation: invokeLaterAndSetModuleName('$animateProvider', 'register'),
2226
2227                   /**
2228                    * @ngdoc method
2229                    * @name angular.Module#filter
2230                    * @module ng
2231                    * @param {string} name Filter name - this must be a valid angular expression identifier
2232                    * @param {Function} filterFactory Factory function for creating new instance of filter.
2233                    * @description
2234                    * See {@link ng.$filterProvider#register $filterProvider.register()}.
2235                    *
2236                    * <div class="alert alert-warning">
2237                    * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
2238                    * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
2239                    * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
2240                    * (`myapp_subsection_filterx`).
2241                    * </div>
2242                    */
2243                   filter: invokeLaterAndSetModuleName('$filterProvider', 'register'),
2244
2245                   /**
2246                    * @ngdoc method
2247                    * @name angular.Module#controller
2248                    * @module ng
2249                    * @param {string|Object} name Controller name, or an object map of controllers where the
2250                    *    keys are the names and the values are the constructors.
2251                    * @param {Function} constructor Controller constructor function.
2252                    * @description
2253                    * See {@link ng.$controllerProvider#register $controllerProvider.register()}.
2254                    */
2255                   controller: invokeLaterAndSetModuleName('$controllerProvider', 'register'),
2256
2257                   /**
2258                    * @ngdoc method
2259                    * @name angular.Module#directive
2260                    * @module ng
2261                    * @param {string|Object} name Directive name, or an object map of directives where the
2262                    *    keys are the names and the values are the factories.
2263                    * @param {Function} directiveFactory Factory function for creating new instance of
2264                    * directives.
2265                    * @description
2266                    * See {@link ng.$compileProvider#directive $compileProvider.directive()}.
2267                    */
2268                   directive: invokeLaterAndSetModuleName('$compileProvider', 'directive'),
2269
2270                   /**
2271                    * @ngdoc method
2272                    * @name angular.Module#config
2273                    * @module ng
2274                    * @param {Function} configFn Execute this function on module load. Useful for service
2275                    *    configuration.
2276                    * @description
2277                    * Use this method to register work which needs to be performed on module loading.
2278                    * For more about how to configure services, see
2279                    * {@link providers#provider-recipe Provider Recipe}.
2280                    */
2281                   config: config,
2282
2283                   /**
2284                    * @ngdoc method
2285                    * @name angular.Module#run
2286                    * @module ng
2287                    * @param {Function} initializationFn Execute this function after injector creation.
2288                    *    Useful for application initialization.
2289                    * @description
2290                    * Use this method to register work which should be performed when the injector is done
2291                    * loading all modules.
2292                    */
2293                   run: function(block) {
2294                     runBlocks.push(block);
2295                     return this;
2296                   }
2297                 };
2298
2299                 if (configFn) {
2300                   config(configFn);
2301                 }
2302
2303                 return moduleInstance;
2304
2305                 /**
2306                  * @param {string} provider
2307                  * @param {string} method
2308                  * @param {String=} insertMethod
2309                  * @returns {angular.Module}
2310                  */
2311                 function invokeLater(provider, method, insertMethod, queue) {
2312                   if (!queue) queue = invokeQueue;
2313                   return function() {
2314                     queue[insertMethod || 'push']([provider, method, arguments]);
2315                     return moduleInstance;
2316                   };
2317                 }
2318
2319                 /**
2320                  * @param {string} provider
2321                  * @param {string} method
2322                  * @returns {angular.Module}
2323                  */
2324                 function invokeLaterAndSetModuleName(provider, method) {
2325                   return function(recipeName, factoryFunction) {
2326                     if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;
2327                     invokeQueue.push([provider, method, arguments]);
2328                     return moduleInstance;
2329                   };
2330                 }
2331               });
2332             };
2333           });
2334
2335         }
2336
2337         /* global: toDebugString: true */
2338
2339         function serializeObject(obj) {
2340           var seen = [];
2341
2342           return JSON.stringify(obj, function(key, val) {
2343             val = toJsonReplacer(key, val);
2344             if (isObject(val)) {
2345
2346               if (seen.indexOf(val) >= 0) return '...';
2347
2348               seen.push(val);
2349             }
2350             return val;
2351           });
2352         }
2353
2354         function toDebugString(obj) {
2355           if (typeof obj === 'function') {
2356             return obj.toString().replace(/ \{[\s\S]*$/, '');
2357           } else if (isUndefined(obj)) {
2358             return 'undefined';
2359           } else if (typeof obj !== 'string') {
2360             return serializeObject(obj);
2361           }
2362           return obj;
2363         }
2364
2365         /* global angularModule: true,
2366           version: true,
2367
2368           $CompileProvider,
2369
2370           htmlAnchorDirective,
2371           inputDirective,
2372           inputDirective,
2373           formDirective,
2374           scriptDirective,
2375           selectDirective,
2376           styleDirective,
2377           optionDirective,
2378           ngBindDirective,
2379           ngBindHtmlDirective,
2380           ngBindTemplateDirective,
2381           ngClassDirective,
2382           ngClassEvenDirective,
2383           ngClassOddDirective,
2384           ngCloakDirective,
2385           ngControllerDirective,
2386           ngFormDirective,
2387           ngHideDirective,
2388           ngIfDirective,
2389           ngIncludeDirective,
2390           ngIncludeFillContentDirective,
2391           ngInitDirective,
2392           ngNonBindableDirective,
2393           ngPluralizeDirective,
2394           ngRepeatDirective,
2395           ngShowDirective,
2396           ngStyleDirective,
2397           ngSwitchDirective,
2398           ngSwitchWhenDirective,
2399           ngSwitchDefaultDirective,
2400           ngOptionsDirective,
2401           ngTranscludeDirective,
2402           ngModelDirective,
2403           ngListDirective,
2404           ngChangeDirective,
2405           patternDirective,
2406           patternDirective,
2407           requiredDirective,
2408           requiredDirective,
2409           minlengthDirective,
2410           minlengthDirective,
2411           maxlengthDirective,
2412           maxlengthDirective,
2413           ngValueDirective,
2414           ngModelOptionsDirective,
2415           ngAttributeAliasDirectives,
2416           ngEventDirectives,
2417
2418           $AnchorScrollProvider,
2419           $AnimateProvider,
2420           $CoreAnimateCssProvider,
2421           $$CoreAnimateQueueProvider,
2422           $$CoreAnimateRunnerProvider,
2423           $BrowserProvider,
2424           $CacheFactoryProvider,
2425           $ControllerProvider,
2426           $DocumentProvider,
2427           $ExceptionHandlerProvider,
2428           $FilterProvider,
2429           $$ForceReflowProvider,
2430           $InterpolateProvider,
2431           $IntervalProvider,
2432           $$HashMapProvider,
2433           $HttpProvider,
2434           $HttpParamSerializerProvider,
2435           $HttpParamSerializerJQLikeProvider,
2436           $HttpBackendProvider,
2437           $xhrFactoryProvider,
2438           $LocationProvider,
2439           $LogProvider,
2440           $ParseProvider,
2441           $RootScopeProvider,
2442           $QProvider,
2443           $$QProvider,
2444           $$SanitizeUriProvider,
2445           $SceProvider,
2446           $SceDelegateProvider,
2447           $SnifferProvider,
2448           $TemplateCacheProvider,
2449           $TemplateRequestProvider,
2450           $$TestabilityProvider,
2451           $TimeoutProvider,
2452           $$RAFProvider,
2453           $WindowProvider,
2454           $$jqLiteProvider,
2455           $$CookieReaderProvider
2456         */
2457
2458
2459         /**
2460          * @ngdoc object
2461          * @name angular.version
2462          * @module ng
2463          * @description
2464          * An object that contains information about the current AngularJS version.
2465          *
2466          * This object has the following properties:
2467          *
2468          * - `full` – `{string}` – Full version string, such as "0.9.18".
2469          * - `major` – `{number}` – Major version number, such as "0".
2470          * - `minor` – `{number}` – Minor version number, such as "9".
2471          * - `dot` – `{number}` – Dot version number, such as "18".
2472          * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat".
2473          */
2474         var version = {
2475           full: '1.4.8',    // all of these placeholder strings will be replaced by grunt's
2476           major: 1,    // package task
2477           minor: 4,
2478           dot: 8,
2479           codeName: 'ice-manipulation'
2480         };
2481
2482
2483         function publishExternalAPI(angular) {
2484           extend(angular, {
2485             'bootstrap': bootstrap,
2486             'copy': copy,
2487             'extend': extend,
2488             'merge': merge,
2489             'equals': equals,
2490             'element': jqLite,
2491             'forEach': forEach,
2492             'injector': createInjector,
2493             'noop': noop,
2494             'bind': bind,
2495             'toJson': toJson,
2496             'fromJson': fromJson,
2497             'identity': identity,
2498             'isUndefined': isUndefined,
2499             'isDefined': isDefined,
2500             'isString': isString,
2501             'isFunction': isFunction,
2502             'isObject': isObject,
2503             'isNumber': isNumber,
2504             'isElement': isElement,
2505             'isArray': isArray,
2506             'version': version,
2507             'isDate': isDate,
2508             'lowercase': lowercase,
2509             'uppercase': uppercase,
2510             'callbacks': {counter: 0},
2511             'getTestability': getTestability,
2512             '$$minErr': minErr,
2513             '$$csp': csp,
2514             'reloadWithDebugInfo': reloadWithDebugInfo
2515           });
2516
2517           angularModule = setupModuleLoader(window);
2518
2519           angularModule('ng', ['ngLocale'], ['$provide',
2520             function ngModule($provide) {
2521               // $$sanitizeUriProvider needs to be before $compileProvider as it is used by it.
2522               $provide.provider({
2523                 $$sanitizeUri: $$SanitizeUriProvider
2524               });
2525               $provide.provider('$compile', $CompileProvider).
2526                 directive({
2527                     a: htmlAnchorDirective,
2528                     input: inputDirective,
2529                     textarea: inputDirective,
2530                     form: formDirective,
2531                     script: scriptDirective,
2532                     select: selectDirective,
2533                     style: styleDirective,
2534                     option: optionDirective,
2535                     ngBind: ngBindDirective,
2536                     ngBindHtml: ngBindHtmlDirective,
2537                     ngBindTemplate: ngBindTemplateDirective,
2538                     ngClass: ngClassDirective,
2539                     ngClassEven: ngClassEvenDirective,
2540                     ngClassOdd: ngClassOddDirective,
2541                     ngCloak: ngCloakDirective,
2542                     ngController: ngControllerDirective,
2543                     ngForm: ngFormDirective,
2544                     ngHide: ngHideDirective,
2545                     ngIf: ngIfDirective,
2546                     ngInclude: ngIncludeDirective,
2547                     ngInit: ngInitDirective,
2548                     ngNonBindable: ngNonBindableDirective,
2549                     ngPluralize: ngPluralizeDirective,
2550                     ngRepeat: ngRepeatDirective,
2551                     ngShow: ngShowDirective,
2552                     ngStyle: ngStyleDirective,
2553                     ngSwitch: ngSwitchDirective,
2554                     ngSwitchWhen: ngSwitchWhenDirective,
2555                     ngSwitchDefault: ngSwitchDefaultDirective,
2556                     ngOptions: ngOptionsDirective,
2557                     ngTransclude: ngTranscludeDirective,
2558                     ngModel: ngModelDirective,
2559                     ngList: ngListDirective,
2560                     ngChange: ngChangeDirective,
2561                     pattern: patternDirective,
2562                     ngPattern: patternDirective,
2563                     required: requiredDirective,
2564                     ngRequired: requiredDirective,
2565                     minlength: minlengthDirective,
2566                     ngMinlength: minlengthDirective,
2567                     maxlength: maxlengthDirective,
2568                     ngMaxlength: maxlengthDirective,
2569                     ngValue: ngValueDirective,
2570                     ngModelOptions: ngModelOptionsDirective
2571                 }).
2572                 directive({
2573                   ngInclude: ngIncludeFillContentDirective
2574                 }).
2575                 directive(ngAttributeAliasDirectives).
2576                 directive(ngEventDirectives);
2577               $provide.provider({
2578                 $anchorScroll: $AnchorScrollProvider,
2579                 $animate: $AnimateProvider,
2580                 $animateCss: $CoreAnimateCssProvider,
2581                 $$animateQueue: $$CoreAnimateQueueProvider,
2582                 $$AnimateRunner: $$CoreAnimateRunnerProvider,
2583                 $browser: $BrowserProvider,
2584                 $cacheFactory: $CacheFactoryProvider,
2585                 $controller: $ControllerProvider,
2586                 $document: $DocumentProvider,
2587                 $exceptionHandler: $ExceptionHandlerProvider,
2588                 $filter: $FilterProvider,
2589                 $$forceReflow: $$ForceReflowProvider,
2590                 $interpolate: $InterpolateProvider,
2591                 $interval: $IntervalProvider,
2592                 $http: $HttpProvider,
2593                 $httpParamSerializer: $HttpParamSerializerProvider,
2594                 $httpParamSerializerJQLike: $HttpParamSerializerJQLikeProvider,
2595                 $httpBackend: $HttpBackendProvider,
2596                 $xhrFactory: $xhrFactoryProvider,
2597                 $location: $LocationProvider,
2598                 $log: $LogProvider,
2599                 $parse: $ParseProvider,
2600                 $rootScope: $RootScopeProvider,
2601                 $q: $QProvider,
2602                 $$q: $$QProvider,
2603                 $sce: $SceProvider,
2604                 $sceDelegate: $SceDelegateProvider,
2605                 $sniffer: $SnifferProvider,
2606                 $templateCache: $TemplateCacheProvider,
2607                 $templateRequest: $TemplateRequestProvider,
2608                 $$testability: $$TestabilityProvider,
2609                 $timeout: $TimeoutProvider,
2610                 $window: $WindowProvider,
2611                 $$rAF: $$RAFProvider,
2612                 $$jqLite: $$jqLiteProvider,
2613                 $$HashMap: $$HashMapProvider,
2614                 $$cookieReader: $$CookieReaderProvider
2615               });
2616             }
2617           ]);
2618         }
2619
2620         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2621          *     Any commits to this file should be reviewed with security in mind.  *
2622          *   Changes to this file can potentially create security vulnerabilities. *
2623          *          An approval from 2 Core members with history of modifying      *
2624          *                         this file is required.                          *
2625          *                                                                         *
2626          *  Does the change somehow allow for arbitrary javascript to be executed? *
2627          *    Or allows for someone to change the prototype of built-in objects?   *
2628          *     Or gives undesired access to variables likes document or window?    *
2629          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2630
2631         /* global JQLitePrototype: true,
2632           addEventListenerFn: true,
2633           removeEventListenerFn: true,
2634           BOOLEAN_ATTR: true,
2635           ALIASED_ATTR: true,
2636         */
2637
2638         //////////////////////////////////
2639         //JQLite
2640         //////////////////////////////////
2641
2642         /**
2643          * @ngdoc function
2644          * @name angular.element
2645          * @module ng
2646          * @kind function
2647          *
2648          * @description
2649          * Wraps a raw DOM element or HTML string as a [jQuery](http://jquery.com) element.
2650          *
2651          * If jQuery is available, `angular.element` is an alias for the
2652          * [jQuery](http://api.jquery.com/jQuery/) function. If jQuery is not available, `angular.element`
2653          * delegates to Angular's built-in subset of jQuery, called "jQuery lite" or "jqLite."
2654          *
2655          * <div class="alert alert-success">jqLite is a tiny, API-compatible subset of jQuery that allows
2656          * Angular to manipulate the DOM in a cross-browser compatible way. **jqLite** implements only the most
2657          * commonly needed functionality with the goal of having a very small footprint.</div>
2658          *
2659          * To use `jQuery`, simply ensure it is loaded before the `angular.js` file.
2660          *
2661          * <div class="alert">**Note:** all element references in Angular are always wrapped with jQuery or
2662          * jqLite; they are never raw DOM references.</div>
2663          *
2664          * ## Angular's jqLite
2665          * jqLite provides only the following jQuery methods:
2666          *
2667          * - [`addClass()`](http://api.jquery.com/addClass/)
2668          * - [`after()`](http://api.jquery.com/after/)
2669          * - [`append()`](http://api.jquery.com/append/)
2670          * - [`attr()`](http://api.jquery.com/attr/) - Does not support functions as parameters
2671          * - [`bind()`](http://api.jquery.com/bind/) - Does not support namespaces, selectors or eventData
2672          * - [`children()`](http://api.jquery.com/children/) - Does not support selectors
2673          * - [`clone()`](http://api.jquery.com/clone/)
2674          * - [`contents()`](http://api.jquery.com/contents/)
2675          * - [`css()`](http://api.jquery.com/css/) - Only retrieves inline-styles, does not call `getComputedStyle()`. As a setter, does not convert numbers to strings or append 'px'.
2676          * - [`data()`](http://api.jquery.com/data/)
2677          * - [`detach()`](http://api.jquery.com/detach/)
2678          * - [`empty()`](http://api.jquery.com/empty/)
2679          * - [`eq()`](http://api.jquery.com/eq/)
2680          * - [`find()`](http://api.jquery.com/find/) - Limited to lookups by tag name
2681          * - [`hasClass()`](http://api.jquery.com/hasClass/)
2682          * - [`html()`](http://api.jquery.com/html/)
2683          * - [`next()`](http://api.jquery.com/next/) - Does not support selectors
2684          * - [`on()`](http://api.jquery.com/on/) - Does not support namespaces, selectors or eventData
2685          * - [`off()`](http://api.jquery.com/off/) - Does not support namespaces, selectors or event object as parameter
2686          * - [`one()`](http://api.jquery.com/one/) - Does not support namespaces or selectors
2687          * - [`parent()`](http://api.jquery.com/parent/) - Does not support selectors
2688          * - [`prepend()`](http://api.jquery.com/prepend/)
2689          * - [`prop()`](http://api.jquery.com/prop/)
2690          * - [`ready()`](http://api.jquery.com/ready/)
2691          * - [`remove()`](http://api.jquery.com/remove/)
2692          * - [`removeAttr()`](http://api.jquery.com/removeAttr/)
2693          * - [`removeClass()`](http://api.jquery.com/removeClass/)
2694          * - [`removeData()`](http://api.jquery.com/removeData/)
2695          * - [`replaceWith()`](http://api.jquery.com/replaceWith/)
2696          * - [`text()`](http://api.jquery.com/text/)
2697          * - [`toggleClass()`](http://api.jquery.com/toggleClass/)
2698          * - [`triggerHandler()`](http://api.jquery.com/triggerHandler/) - Passes a dummy event object to handlers.
2699          * - [`unbind()`](http://api.jquery.com/unbind/) - Does not support namespaces or event object as parameter
2700          * - [`val()`](http://api.jquery.com/val/)
2701          * - [`wrap()`](http://api.jquery.com/wrap/)
2702          *
2703          * ## jQuery/jqLite Extras
2704          * Angular also provides the following additional methods and events to both jQuery and jqLite:
2705          *
2706          * ### Events
2707          * - `$destroy` - AngularJS intercepts all jqLite/jQuery's DOM destruction apis and fires this event
2708          *    on all DOM nodes being removed.  This can be used to clean up any 3rd party bindings to the DOM
2709          *    element before it is removed.
2710          *
2711          * ### Methods
2712          * - `controller(name)` - retrieves the controller of the current element or its parent. By default
2713          *   retrieves controller associated with the `ngController` directive. If `name` is provided as
2714          *   camelCase directive name, then the controller for this directive will be retrieved (e.g.
2715          *   `'ngModel'`).
2716          * - `injector()` - retrieves the injector of the current element or its parent.
2717          * - `scope()` - retrieves the {@link ng.$rootScope.Scope scope} of the current
2718          *   element or its parent. Requires {@link guide/production#disabling-debug-data Debug Data} to
2719          *   be enabled.
2720          * - `isolateScope()` - retrieves an isolate {@link ng.$rootScope.Scope scope} if one is attached directly to the
2721          *   current element. This getter should be used only on elements that contain a directive which starts a new isolate
2722          *   scope. Calling `scope()` on this element always returns the original non-isolate scope.
2723          *   Requires {@link guide/production#disabling-debug-data Debug Data} to be enabled.
2724          * - `inheritedData()` - same as `data()`, but walks up the DOM until a value is found or the top
2725          *   parent element is reached.
2726          *
2727          * @param {string|DOMElement} element HTML string or DOMElement to be wrapped into jQuery.
2728          * @returns {Object} jQuery object.
2729          */
2730
2731         JQLite.expando = 'ng339';
2732
2733         var jqCache = JQLite.cache = {},
2734             jqId = 1,
2735             addEventListenerFn = function(element, type, fn) {
2736               element.addEventListener(type, fn, false);
2737             },
2738             removeEventListenerFn = function(element, type, fn) {
2739               element.removeEventListener(type, fn, false);
2740             };
2741
2742         /*
2743          * !!! This is an undocumented "private" function !!!
2744          */
2745         JQLite._data = function(node) {
2746           //jQuery always returns an object on cache miss
2747           return this.cache[node[this.expando]] || {};
2748         };
2749
2750         function jqNextId() { return ++jqId; }
2751
2752
2753         var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
2754         var MOZ_HACK_REGEXP = /^moz([A-Z])/;
2755         var MOUSE_EVENT_MAP= { mouseleave: "mouseout", mouseenter: "mouseover"};
2756         var jqLiteMinErr = minErr('jqLite');
2757
2758         /**
2759          * Converts snake_case to camelCase.
2760          * Also there is special case for Moz prefix starting with upper case letter.
2761          * @param name Name to normalize
2762          */
2763         function camelCase(name) {
2764           return name.
2765             replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
2766               return offset ? letter.toUpperCase() : letter;
2767             }).
2768             replace(MOZ_HACK_REGEXP, 'Moz$1');
2769         }
2770
2771         var SINGLE_TAG_REGEXP = /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/;
2772         var HTML_REGEXP = /<|&#?\w+;/;
2773         var TAG_NAME_REGEXP = /<([\w:-]+)/;
2774         var XHTML_TAG_REGEXP = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi;
2775
2776         var wrapMap = {
2777           'option': [1, '<select multiple="multiple">', '</select>'],
2778
2779           'thead': [1, '<table>', '</table>'],
2780           'col': [2, '<table><colgroup>', '</colgroup></table>'],
2781           'tr': [2, '<table><tbody>', '</tbody></table>'],
2782           'td': [3, '<table><tbody><tr>', '</tr></tbody></table>'],
2783           '_default': [0, "", ""]
2784         };
2785
2786         wrapMap.optgroup = wrapMap.option;
2787         wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
2788         wrapMap.th = wrapMap.td;
2789
2790
2791         function jqLiteIsTextNode(html) {
2792           return !HTML_REGEXP.test(html);
2793         }
2794
2795         function jqLiteAcceptsData(node) {
2796           // The window object can accept data but has no nodeType
2797           // Otherwise we are only interested in elements (1) and documents (9)
2798           var nodeType = node.nodeType;
2799           return nodeType === NODE_TYPE_ELEMENT || !nodeType || nodeType === NODE_TYPE_DOCUMENT;
2800         }
2801
2802         function jqLiteHasData(node) {
2803           for (var key in jqCache[node.ng339]) {
2804             return true;
2805           }
2806           return false;
2807         }
2808
2809         function jqLiteBuildFragment(html, context) {
2810           var tmp, tag, wrap,
2811               fragment = context.createDocumentFragment(),
2812               nodes = [], i;
2813
2814           if (jqLiteIsTextNode(html)) {
2815             // Convert non-html into a text node
2816             nodes.push(context.createTextNode(html));
2817           } else {
2818             // Convert html into DOM nodes
2819             tmp = tmp || fragment.appendChild(context.createElement("div"));
2820             tag = (TAG_NAME_REGEXP.exec(html) || ["", ""])[1].toLowerCase();
2821             wrap = wrapMap[tag] || wrapMap._default;
2822             tmp.innerHTML = wrap[1] + html.replace(XHTML_TAG_REGEXP, "<$1></$2>") + wrap[2];
2823
2824             // Descend through wrappers to the right content
2825             i = wrap[0];
2826             while (i--) {
2827               tmp = tmp.lastChild;
2828             }
2829
2830             nodes = concat(nodes, tmp.childNodes);
2831
2832             tmp = fragment.firstChild;
2833             tmp.textContent = "";
2834           }
2835
2836           // Remove wrapper from fragment
2837           fragment.textContent = "";
2838           fragment.innerHTML = ""; // Clear inner HTML
2839           forEach(nodes, function(node) {
2840             fragment.appendChild(node);
2841           });
2842
2843           return fragment;
2844         }
2845
2846         function jqLiteParseHTML(html, context) {
2847           context = context || document;
2848           var parsed;
2849
2850           if ((parsed = SINGLE_TAG_REGEXP.exec(html))) {
2851             return [context.createElement(parsed[1])];
2852           }
2853
2854           if ((parsed = jqLiteBuildFragment(html, context))) {
2855             return parsed.childNodes;
2856           }
2857
2858           return [];
2859         }
2860
2861
2862         // IE9-11 has no method "contains" in SVG element and in Node.prototype. Bug #10259.
2863         var jqLiteContains = Node.prototype.contains || function(arg) {
2864           // jshint bitwise: false
2865           return !!(this.compareDocumentPosition(arg) & 16);
2866           // jshint bitwise: true
2867         };
2868
2869         /////////////////////////////////////////////
2870         function JQLite(element) {
2871           if (element instanceof JQLite) {
2872             return element;
2873           }
2874
2875           var argIsString;
2876
2877           if (isString(element)) {
2878             element = trim(element);
2879             argIsString = true;
2880           }
2881           if (!(this instanceof JQLite)) {
2882             if (argIsString && element.charAt(0) != '<') {
2883               throw jqLiteMinErr('nosel', 'Looking up elements via selectors is not supported by jqLite! See: http://docs.angularjs.org/api/angular.element');
2884             }
2885             return new JQLite(element);
2886           }
2887
2888           if (argIsString) {
2889             jqLiteAddNodes(this, jqLiteParseHTML(element));
2890           } else {
2891             jqLiteAddNodes(this, element);
2892           }
2893         }
2894
2895         function jqLiteClone(element) {
2896           return element.cloneNode(true);
2897         }
2898
2899         function jqLiteDealoc(element, onlyDescendants) {
2900           if (!onlyDescendants) jqLiteRemoveData(element);
2901
2902           if (element.querySelectorAll) {
2903             var descendants = element.querySelectorAll('*');
2904             for (var i = 0, l = descendants.length; i < l; i++) {
2905               jqLiteRemoveData(descendants[i]);
2906             }
2907           }
2908         }
2909
2910         function jqLiteOff(element, type, fn, unsupported) {
2911           if (isDefined(unsupported)) throw jqLiteMinErr('offargs', 'jqLite#off() does not support the `selector` argument');
2912
2913           var expandoStore = jqLiteExpandoStore(element);
2914           var events = expandoStore && expandoStore.events;
2915           var handle = expandoStore && expandoStore.handle;
2916
2917           if (!handle) return; //no listeners registered
2918
2919           if (!type) {
2920             for (type in events) {
2921               if (type !== '$destroy') {
2922                 removeEventListenerFn(element, type, handle);
2923               }
2924               delete events[type];
2925             }
2926           } else {
2927
2928             var removeHandler = function(type) {
2929               var listenerFns = events[type];
2930               if (isDefined(fn)) {
2931                 arrayRemove(listenerFns || [], fn);
2932               }
2933               if (!(isDefined(fn) && listenerFns && listenerFns.length > 0)) {
2934                 removeEventListenerFn(element, type, handle);
2935                 delete events[type];
2936               }
2937             };
2938
2939             forEach(type.split(' '), function(type) {
2940               removeHandler(type);
2941               if (MOUSE_EVENT_MAP[type]) {
2942                 removeHandler(MOUSE_EVENT_MAP[type]);
2943               }
2944             });
2945           }
2946         }
2947
2948         function jqLiteRemoveData(element, name) {
2949           var expandoId = element.ng339;
2950           var expandoStore = expandoId && jqCache[expandoId];
2951
2952           if (expandoStore) {
2953             if (name) {
2954               delete expandoStore.data[name];
2955               return;
2956             }
2957
2958             if (expandoStore.handle) {
2959               if (expandoStore.events.$destroy) {
2960                 expandoStore.handle({}, '$destroy');
2961               }
2962               jqLiteOff(element);
2963             }
2964             delete jqCache[expandoId];
2965             element.ng339 = undefined; // don't delete DOM expandos. IE and Chrome don't like it
2966           }
2967         }
2968
2969
2970         function jqLiteExpandoStore(element, createIfNecessary) {
2971           var expandoId = element.ng339,
2972               expandoStore = expandoId && jqCache[expandoId];
2973
2974           if (createIfNecessary && !expandoStore) {
2975             element.ng339 = expandoId = jqNextId();
2976             expandoStore = jqCache[expandoId] = {events: {}, data: {}, handle: undefined};
2977           }
2978
2979           return expandoStore;
2980         }
2981
2982
2983         function jqLiteData(element, key, value) {
2984           if (jqLiteAcceptsData(element)) {
2985
2986             var isSimpleSetter = isDefined(value);
2987             var isSimpleGetter = !isSimpleSetter && key && !isObject(key);
2988             var massGetter = !key;
2989             var expandoStore = jqLiteExpandoStore(element, !isSimpleGetter);
2990             var data = expandoStore && expandoStore.data;
2991
2992             if (isSimpleSetter) { // data('key', value)
2993               data[key] = value;
2994             } else {
2995               if (massGetter) {  // data()
2996                 return data;
2997               } else {
2998                 if (isSimpleGetter) { // data('key')
2999                   // don't force creation of expandoStore if it doesn't exist yet
3000                   return data && data[key];
3001                 } else { // mass-setter: data({key1: val1, key2: val2})
3002                   extend(data, key);
3003                 }
3004               }
3005             }
3006           }
3007         }
3008
3009         function jqLiteHasClass(element, selector) {
3010           if (!element.getAttribute) return false;
3011           return ((" " + (element.getAttribute('class') || '') + " ").replace(/[\n\t]/g, " ").
3012               indexOf(" " + selector + " ") > -1);
3013         }
3014
3015         function jqLiteRemoveClass(element, cssClasses) {
3016           if (cssClasses && element.setAttribute) {
3017             forEach(cssClasses.split(' '), function(cssClass) {
3018               element.setAttribute('class', trim(
3019                   (" " + (element.getAttribute('class') || '') + " ")
3020                   .replace(/[\n\t]/g, " ")
3021                   .replace(" " + trim(cssClass) + " ", " "))
3022               );
3023             });
3024           }
3025         }
3026
3027         function jqLiteAddClass(element, cssClasses) {
3028           if (cssClasses && element.setAttribute) {
3029             var existingClasses = (' ' + (element.getAttribute('class') || '') + ' ')
3030                                     .replace(/[\n\t]/g, " ");
3031
3032             forEach(cssClasses.split(' '), function(cssClass) {
3033               cssClass = trim(cssClass);
3034               if (existingClasses.indexOf(' ' + cssClass + ' ') === -1) {
3035                 existingClasses += cssClass + ' ';
3036               }
3037             });
3038
3039             element.setAttribute('class', trim(existingClasses));
3040           }
3041         }
3042
3043
3044         function jqLiteAddNodes(root, elements) {
3045           // THIS CODE IS VERY HOT. Don't make changes without benchmarking.
3046
3047           if (elements) {
3048
3049             // if a Node (the most common case)
3050             if (elements.nodeType) {
3051               root[root.length++] = elements;
3052             } else {
3053               var length = elements.length;
3054
3055               // if an Array or NodeList and not a Window
3056               if (typeof length === 'number' && elements.window !== elements) {
3057                 if (length) {
3058                   for (var i = 0; i < length; i++) {
3059                     root[root.length++] = elements[i];
3060                   }
3061                 }
3062               } else {
3063                 root[root.length++] = elements;
3064               }
3065             }
3066           }
3067         }
3068
3069
3070         function jqLiteController(element, name) {
3071           return jqLiteInheritedData(element, '$' + (name || 'ngController') + 'Controller');
3072         }
3073
3074         function jqLiteInheritedData(element, name, value) {
3075           // if element is the document object work with the html element instead
3076           // this makes $(document).scope() possible
3077           if (element.nodeType == NODE_TYPE_DOCUMENT) {
3078             element = element.documentElement;
3079           }
3080           var names = isArray(name) ? name : [name];
3081
3082           while (element) {
3083             for (var i = 0, ii = names.length; i < ii; i++) {
3084               if (isDefined(value = jqLite.data(element, names[i]))) return value;
3085             }
3086
3087             // If dealing with a document fragment node with a host element, and no parent, use the host
3088             // element as the parent. This enables directives within a Shadow DOM or polyfilled Shadow DOM
3089             // to lookup parent controllers.
3090             element = element.parentNode || (element.nodeType === NODE_TYPE_DOCUMENT_FRAGMENT && element.host);
3091           }
3092         }
3093
3094         function jqLiteEmpty(element) {
3095           jqLiteDealoc(element, true);
3096           while (element.firstChild) {
3097             element.removeChild(element.firstChild);
3098           }
3099         }
3100
3101         function jqLiteRemove(element, keepData) {
3102           if (!keepData) jqLiteDealoc(element);
3103           var parent = element.parentNode;
3104           if (parent) parent.removeChild(element);
3105         }
3106
3107
3108         function jqLiteDocumentLoaded(action, win) {
3109           win = win || window;
3110           if (win.document.readyState === 'complete') {
3111             // Force the action to be run async for consistent behaviour
3112             // from the action's point of view
3113             // i.e. it will definitely not be in a $apply
3114             win.setTimeout(action);
3115           } else {
3116             // No need to unbind this handler as load is only ever called once
3117             jqLite(win).on('load', action);
3118           }
3119         }
3120
3121         //////////////////////////////////////////
3122         // Functions which are declared directly.
3123         //////////////////////////////////////////
3124         var JQLitePrototype = JQLite.prototype = {
3125           ready: function(fn) {
3126             var fired = false;
3127
3128             function trigger() {
3129               if (fired) return;
3130               fired = true;
3131               fn();
3132             }
3133
3134             // check if document is already loaded
3135             if (document.readyState === 'complete') {
3136               setTimeout(trigger);
3137             } else {
3138               this.on('DOMContentLoaded', trigger); // works for modern browsers and IE9
3139               // we can not use jqLite since we are not done loading and jQuery could be loaded later.
3140               // jshint -W064
3141               JQLite(window).on('load', trigger); // fallback to window.onload for others
3142               // jshint +W064
3143             }
3144           },
3145           toString: function() {
3146             var value = [];
3147             forEach(this, function(e) { value.push('' + e);});
3148             return '[' + value.join(', ') + ']';
3149           },
3150
3151           eq: function(index) {
3152               return (index >= 0) ? jqLite(this[index]) : jqLite(this[this.length + index]);
3153           },
3154
3155           length: 0,
3156           push: push,
3157           sort: [].sort,
3158           splice: [].splice
3159         };
3160
3161         //////////////////////////////////////////
3162         // Functions iterating getter/setters.
3163         // these functions return self on setter and
3164         // value on get.
3165         //////////////////////////////////////////
3166         var BOOLEAN_ATTR = {};
3167         forEach('multiple,selected,checked,disabled,readOnly,required,open'.split(','), function(value) {
3168           BOOLEAN_ATTR[lowercase(value)] = value;
3169         });
3170         var BOOLEAN_ELEMENTS = {};
3171         forEach('input,select,option,textarea,button,form,details'.split(','), function(value) {
3172           BOOLEAN_ELEMENTS[value] = true;
3173         });
3174         var ALIASED_ATTR = {
3175           'ngMinlength': 'minlength',
3176           'ngMaxlength': 'maxlength',
3177           'ngMin': 'min',
3178           'ngMax': 'max',
3179           'ngPattern': 'pattern'
3180         };
3181
3182         function getBooleanAttrName(element, name) {
3183           // check dom last since we will most likely fail on name
3184           var booleanAttr = BOOLEAN_ATTR[name.toLowerCase()];
3185
3186           // booleanAttr is here twice to minimize DOM access
3187           return booleanAttr && BOOLEAN_ELEMENTS[nodeName_(element)] && booleanAttr;
3188         }
3189
3190         function getAliasedAttrName(name) {
3191           return ALIASED_ATTR[name];
3192         }
3193
3194         forEach({
3195           data: jqLiteData,
3196           removeData: jqLiteRemoveData,
3197           hasData: jqLiteHasData
3198         }, function(fn, name) {
3199           JQLite[name] = fn;
3200         });
3201
3202         forEach({
3203           data: jqLiteData,
3204           inheritedData: jqLiteInheritedData,
3205
3206           scope: function(element) {
3207             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3208             return jqLite.data(element, '$scope') || jqLiteInheritedData(element.parentNode || element, ['$isolateScope', '$scope']);
3209           },
3210
3211           isolateScope: function(element) {
3212             // Can't use jqLiteData here directly so we stay compatible with jQuery!
3213             return jqLite.data(element, '$isolateScope') || jqLite.data(element, '$isolateScopeNoTemplate');
3214           },
3215
3216           controller: jqLiteController,
3217
3218           injector: function(element) {
3219             return jqLiteInheritedData(element, '$injector');
3220           },
3221
3222           removeAttr: function(element, name) {
3223             element.removeAttribute(name);
3224           },
3225
3226           hasClass: jqLiteHasClass,
3227
3228           css: function(element, name, value) {
3229             name = camelCase(name);
3230
3231             if (isDefined(value)) {
3232               element.style[name] = value;
3233             } else {
3234               return element.style[name];
3235             }
3236           },
3237
3238           attr: function(element, name, value) {
3239             var nodeType = element.nodeType;
3240             if (nodeType === NODE_TYPE_TEXT || nodeType === NODE_TYPE_ATTRIBUTE || nodeType === NODE_TYPE_COMMENT) {
3241               return;
3242             }
3243             var lowercasedName = lowercase(name);
3244             if (BOOLEAN_ATTR[lowercasedName]) {
3245               if (isDefined(value)) {
3246                 if (!!value) {
3247                   element[name] = true;
3248                   element.setAttribute(name, lowercasedName);
3249                 } else {
3250                   element[name] = false;
3251                   element.removeAttribute(lowercasedName);
3252                 }
3253               } else {
3254                 return (element[name] ||
3255                          (element.attributes.getNamedItem(name) || noop).specified)
3256                        ? lowercasedName
3257                        : undefined;
3258               }
3259             } else if (isDefined(value)) {
3260               element.setAttribute(name, value);
3261             } else if (element.getAttribute) {
3262               // the extra argument "2" is to get the right thing for a.href in IE, see jQuery code
3263               // some elements (e.g. Document) don't have get attribute, so return undefined
3264               var ret = element.getAttribute(name, 2);
3265               // normalize non-existing attributes to undefined (as jQuery)
3266               return ret === null ? undefined : ret;
3267             }
3268           },
3269
3270           prop: function(element, name, value) {
3271             if (isDefined(value)) {
3272               element[name] = value;
3273             } else {
3274               return element[name];
3275             }
3276           },
3277
3278           text: (function() {
3279             getText.$dv = '';
3280             return getText;
3281
3282             function getText(element, value) {
3283               if (isUndefined(value)) {
3284                 var nodeType = element.nodeType;
3285                 return (nodeType === NODE_TYPE_ELEMENT || nodeType === NODE_TYPE_TEXT) ? element.textContent : '';
3286               }
3287               element.textContent = value;
3288             }
3289           })(),
3290
3291           val: function(element, value) {
3292             if (isUndefined(value)) {
3293               if (element.multiple && nodeName_(element) === 'select') {
3294                 var result = [];
3295                 forEach(element.options, function(option) {
3296                   if (option.selected) {
3297                     result.push(option.value || option.text);
3298                   }
3299                 });
3300                 return result.length === 0 ? null : result;
3301               }
3302               return element.value;
3303             }
3304             element.value = value;
3305           },
3306
3307           html: function(element, value) {
3308             if (isUndefined(value)) {
3309               return element.innerHTML;
3310             }
3311             jqLiteDealoc(element, true);
3312             element.innerHTML = value;
3313           },
3314
3315           empty: jqLiteEmpty
3316         }, function(fn, name) {
3317           /**
3318            * Properties: writes return selection, reads return first value
3319            */
3320           JQLite.prototype[name] = function(arg1, arg2) {
3321             var i, key;
3322             var nodeCount = this.length;
3323
3324             // jqLiteHasClass has only two arguments, but is a getter-only fn, so we need to special-case it
3325             // in a way that survives minification.
3326             // jqLiteEmpty takes no arguments but is a setter.
3327             if (fn !== jqLiteEmpty &&
3328                 (isUndefined((fn.length == 2 && (fn !== jqLiteHasClass && fn !== jqLiteController)) ? arg1 : arg2))) {
3329               if (isObject(arg1)) {
3330
3331                 // we are a write, but the object properties are the key/values
3332                 for (i = 0; i < nodeCount; i++) {
3333                   if (fn === jqLiteData) {
3334                     // data() takes the whole object in jQuery
3335                     fn(this[i], arg1);
3336                   } else {
3337                     for (key in arg1) {
3338                       fn(this[i], key, arg1[key]);
3339                     }
3340                   }
3341                 }
3342                 // return self for chaining
3343                 return this;
3344               } else {
3345                 // we are a read, so read the first child.
3346                 // TODO: do we still need this?
3347                 var value = fn.$dv;
3348                 // Only if we have $dv do we iterate over all, otherwise it is just the first element.
3349                 var jj = (isUndefined(value)) ? Math.min(nodeCount, 1) : nodeCount;
3350                 for (var j = 0; j < jj; j++) {
3351                   var nodeValue = fn(this[j], arg1, arg2);
3352                   value = value ? value + nodeValue : nodeValue;
3353                 }
3354                 return value;
3355               }
3356             } else {
3357               // we are a write, so apply to all children
3358               for (i = 0; i < nodeCount; i++) {
3359                 fn(this[i], arg1, arg2);
3360               }
3361               // return self for chaining
3362               return this;
3363             }
3364           };
3365         });
3366
3367         function createEventHandler(element, events) {
3368           var eventHandler = function(event, type) {
3369             // jQuery specific api
3370             event.isDefaultPrevented = function() {
3371               return event.defaultPrevented;
3372             };
3373
3374             var eventFns = events[type || event.type];
3375             var eventFnsLength = eventFns ? eventFns.length : 0;
3376
3377             if (!eventFnsLength) return;
3378
3379             if (isUndefined(event.immediatePropagationStopped)) {
3380               var originalStopImmediatePropagation = event.stopImmediatePropagation;
3381               event.stopImmediatePropagation = function() {
3382                 event.immediatePropagationStopped = true;
3383
3384                 if (event.stopPropagation) {
3385                   event.stopPropagation();
3386                 }
3387
3388                 if (originalStopImmediatePropagation) {
3389                   originalStopImmediatePropagation.call(event);
3390                 }
3391               };
3392             }
3393
3394             event.isImmediatePropagationStopped = function() {
3395               return event.immediatePropagationStopped === true;
3396             };
3397
3398             // Some events have special handlers that wrap the real handler
3399             var handlerWrapper = eventFns.specialHandlerWrapper || defaultHandlerWrapper;
3400
3401             // Copy event handlers in case event handlers array is modified during execution.
3402             if ((eventFnsLength > 1)) {
3403               eventFns = shallowCopy(eventFns);
3404             }
3405
3406             for (var i = 0; i < eventFnsLength; i++) {
3407               if (!event.isImmediatePropagationStopped()) {
3408                 handlerWrapper(element, event, eventFns[i]);
3409               }
3410             }
3411           };
3412
3413           // TODO: this is a hack for angularMocks/clearDataCache that makes it possible to deregister all
3414           //       events on `element`
3415           eventHandler.elem = element;
3416           return eventHandler;
3417         }
3418
3419         function defaultHandlerWrapper(element, event, handler) {
3420           handler.call(element, event);
3421         }
3422
3423         function specialMouseHandlerWrapper(target, event, handler) {
3424           // Refer to jQuery's implementation of mouseenter & mouseleave
3425           // Read about mouseenter and mouseleave:
3426           // http://www.quirksmode.org/js/events_mouse.html#link8
3427           var related = event.relatedTarget;
3428           // For mousenter/leave call the handler if related is outside the target.
3429           // NB: No relatedTarget if the mouse left/entered the browser window
3430           if (!related || (related !== target && !jqLiteContains.call(target, related))) {
3431             handler.call(target, event);
3432           }
3433         }
3434
3435         //////////////////////////////////////////
3436         // Functions iterating traversal.
3437         // These functions chain results into a single
3438         // selector.
3439         //////////////////////////////////////////
3440         forEach({
3441           removeData: jqLiteRemoveData,
3442
3443           on: function jqLiteOn(element, type, fn, unsupported) {
3444             if (isDefined(unsupported)) throw jqLiteMinErr('onargs', 'jqLite#on() does not support the `selector` or `eventData` parameters');
3445
3446             // Do not add event handlers to non-elements because they will not be cleaned up.
3447             if (!jqLiteAcceptsData(element)) {
3448               return;
3449             }
3450
3451             var expandoStore = jqLiteExpandoStore(element, true);
3452             var events = expandoStore.events;
3453             var handle = expandoStore.handle;
3454
3455             if (!handle) {
3456               handle = expandoStore.handle = createEventHandler(element, events);
3457             }
3458
3459             // http://jsperf.com/string-indexof-vs-split
3460             var types = type.indexOf(' ') >= 0 ? type.split(' ') : [type];
3461             var i = types.length;
3462
3463             var addHandler = function(type, specialHandlerWrapper, noEventListener) {
3464               var eventFns = events[type];
3465
3466               if (!eventFns) {
3467                 eventFns = events[type] = [];
3468                 eventFns.specialHandlerWrapper = specialHandlerWrapper;
3469                 if (type !== '$destroy' && !noEventListener) {
3470                   addEventListenerFn(element, type, handle);
3471                 }
3472               }
3473
3474               eventFns.push(fn);
3475             };
3476
3477             while (i--) {
3478               type = types[i];
3479               if (MOUSE_EVENT_MAP[type]) {
3480                 addHandler(MOUSE_EVENT_MAP[type], specialMouseHandlerWrapper);
3481                 addHandler(type, undefined, true);
3482               } else {
3483                 addHandler(type);
3484               }
3485             }
3486           },
3487
3488           off: jqLiteOff,
3489
3490           one: function(element, type, fn) {
3491             element = jqLite(element);
3492
3493             //add the listener twice so that when it is called
3494             //you can remove the original function and still be
3495             //able to call element.off(ev, fn) normally
3496             element.on(type, function onFn() {
3497               element.off(type, fn);
3498               element.off(type, onFn);
3499             });
3500             element.on(type, fn);
3501           },
3502
3503           replaceWith: function(element, replaceNode) {
3504             var index, parent = element.parentNode;
3505             jqLiteDealoc(element);
3506             forEach(new JQLite(replaceNode), function(node) {
3507               if (index) {
3508                 parent.insertBefore(node, index.nextSibling);
3509               } else {
3510                 parent.replaceChild(node, element);
3511               }
3512               index = node;
3513             });
3514           },
3515
3516           children: function(element) {
3517             var children = [];
3518             forEach(element.childNodes, function(element) {
3519               if (element.nodeType === NODE_TYPE_ELEMENT) {
3520                 children.push(element);
3521               }
3522             });
3523             return children;
3524           },
3525
3526           contents: function(element) {
3527             return element.contentDocument || element.childNodes || [];
3528           },
3529
3530           append: function(element, node) {
3531             var nodeType = element.nodeType;
3532             if (nodeType !== NODE_TYPE_ELEMENT && nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT) return;
3533
3534             node = new JQLite(node);
3535
3536             for (var i = 0, ii = node.length; i < ii; i++) {
3537               var child = node[i];
3538               element.appendChild(child);
3539             }
3540           },
3541
3542           prepend: function(element, node) {
3543             if (element.nodeType === NODE_TYPE_ELEMENT) {
3544               var index = element.firstChild;
3545               forEach(new JQLite(node), function(child) {
3546                 element.insertBefore(child, index);
3547               });
3548             }
3549           },
3550
3551           wrap: function(element, wrapNode) {
3552             wrapNode = jqLite(wrapNode).eq(0).clone()[0];
3553             var parent = element.parentNode;
3554             if (parent) {
3555               parent.replaceChild(wrapNode, element);
3556             }
3557             wrapNode.appendChild(element);
3558           },
3559
3560           remove: jqLiteRemove,
3561
3562           detach: function(element) {
3563             jqLiteRemove(element, true);
3564           },
3565
3566           after: function(element, newElement) {
3567             var index = element, parent = element.parentNode;
3568             newElement = new JQLite(newElement);
3569
3570             for (var i = 0, ii = newElement.length; i < ii; i++) {
3571               var node = newElement[i];
3572               parent.insertBefore(node, index.nextSibling);
3573               index = node;
3574             }
3575           },
3576
3577           addClass: jqLiteAddClass,
3578           removeClass: jqLiteRemoveClass,
3579
3580           toggleClass: function(element, selector, condition) {
3581             if (selector) {
3582               forEach(selector.split(' '), function(className) {
3583                 var classCondition = condition;
3584                 if (isUndefined(classCondition)) {
3585                   classCondition = !jqLiteHasClass(element, className);
3586                 }
3587                 (classCondition ? jqLiteAddClass : jqLiteRemoveClass)(element, className);
3588               });
3589             }
3590           },
3591
3592           parent: function(element) {
3593             var parent = element.parentNode;
3594             return parent && parent.nodeType !== NODE_TYPE_DOCUMENT_FRAGMENT ? parent : null;
3595           },
3596
3597           next: function(element) {
3598             return element.nextElementSibling;
3599           },
3600
3601           find: function(element, selector) {
3602             if (element.getElementsByTagName) {
3603               return element.getElementsByTagName(selector);
3604             } else {
3605               return [];
3606             }
3607           },
3608
3609           clone: jqLiteClone,
3610
3611           triggerHandler: function(element, event, extraParameters) {
3612
3613             var dummyEvent, eventFnsCopy, handlerArgs;
3614             var eventName = event.type || event;
3615             var expandoStore = jqLiteExpandoStore(element);
3616             var events = expandoStore && expandoStore.events;
3617             var eventFns = events && events[eventName];
3618
3619             if (eventFns) {
3620               // Create a dummy event to pass to the handlers
3621               dummyEvent = {
3622                 preventDefault: function() { this.defaultPrevented = true; },
3623                 isDefaultPrevented: function() { return this.defaultPrevented === true; },
3624                 stopImmediatePropagation: function() { this.immediatePropagationStopped = true; },
3625                 isImmediatePropagationStopped: function() { return this.immediatePropagationStopped === true; },
3626                 stopPropagation: noop,
3627                 type: eventName,
3628                 target: element
3629               };
3630
3631               // If a custom event was provided then extend our dummy event with it
3632               if (event.type) {
3633                 dummyEvent = extend(dummyEvent, event);
3634               }
3635
3636               // Copy event handlers in case event handlers array is modified during execution.
3637               eventFnsCopy = shallowCopy(eventFns);
3638               handlerArgs = extraParameters ? [dummyEvent].concat(extraParameters) : [dummyEvent];
3639
3640               forEach(eventFnsCopy, function(fn) {
3641                 if (!dummyEvent.isImmediatePropagationStopped()) {
3642                   fn.apply(element, handlerArgs);
3643                 }
3644               });
3645             }
3646           }
3647         }, function(fn, name) {
3648           /**
3649            * chaining functions
3650            */
3651           JQLite.prototype[name] = function(arg1, arg2, arg3) {
3652             var value;
3653
3654             for (var i = 0, ii = this.length; i < ii; i++) {
3655               if (isUndefined(value)) {
3656                 value = fn(this[i], arg1, arg2, arg3);
3657                 if (isDefined(value)) {
3658                   // any function which returns a value needs to be wrapped
3659                   value = jqLite(value);
3660                 }
3661               } else {
3662                 jqLiteAddNodes(value, fn(this[i], arg1, arg2, arg3));
3663               }
3664             }
3665             return isDefined(value) ? value : this;
3666           };
3667
3668           // bind legacy bind/unbind to on/off
3669           JQLite.prototype.bind = JQLite.prototype.on;
3670           JQLite.prototype.unbind = JQLite.prototype.off;
3671         });
3672
3673
3674         // Provider for private $$jqLite service
3675         function $$jqLiteProvider() {
3676           this.$get = function $$jqLite() {
3677             return extend(JQLite, {
3678               hasClass: function(node, classes) {
3679                 if (node.attr) node = node[0];
3680                 return jqLiteHasClass(node, classes);
3681               },
3682               addClass: function(node, classes) {
3683                 if (node.attr) node = node[0];
3684                 return jqLiteAddClass(node, classes);
3685               },
3686               removeClass: function(node, classes) {
3687                 if (node.attr) node = node[0];
3688                 return jqLiteRemoveClass(node, classes);
3689               }
3690             });
3691           };
3692         }
3693
3694         /**
3695          * Computes a hash of an 'obj'.
3696          * Hash of a:
3697          *  string is string
3698          *  number is number as string
3699          *  object is either result of calling $$hashKey function on the object or uniquely generated id,
3700          *         that is also assigned to the $$hashKey property of the object.
3701          *
3702          * @param obj
3703          * @returns {string} hash string such that the same input will have the same hash string.
3704          *         The resulting string key is in 'type:hashKey' format.
3705          */
3706         function hashKey(obj, nextUidFn) {
3707           var key = obj && obj.$$hashKey;
3708
3709           if (key) {
3710             if (typeof key === 'function') {
3711               key = obj.$$hashKey();
3712             }
3713             return key;
3714           }
3715
3716           var objType = typeof obj;
3717           if (objType == 'function' || (objType == 'object' && obj !== null)) {
3718             key = obj.$$hashKey = objType + ':' + (nextUidFn || nextUid)();
3719           } else {
3720             key = objType + ':' + obj;
3721           }
3722
3723           return key;
3724         }
3725
3726         /**
3727          * HashMap which can use objects as keys
3728          */
3729         function HashMap(array, isolatedUid) {
3730           if (isolatedUid) {
3731             var uid = 0;
3732             this.nextUid = function() {
3733               return ++uid;
3734             };
3735           }
3736           forEach(array, this.put, this);
3737         }
3738         HashMap.prototype = {
3739           /**
3740            * Store key value pair
3741            * @param key key to store can be any type
3742            * @param value value to store can be any type
3743            */
3744           put: function(key, value) {
3745             this[hashKey(key, this.nextUid)] = value;
3746           },
3747
3748           /**
3749            * @param key
3750            * @returns {Object} the value for the key
3751            */
3752           get: function(key) {
3753             return this[hashKey(key, this.nextUid)];
3754           },
3755
3756           /**
3757            * Remove the key/value pair
3758            * @param key
3759            */
3760           remove: function(key) {
3761             var value = this[key = hashKey(key, this.nextUid)];
3762             delete this[key];
3763             return value;
3764           }
3765         };
3766
3767         var $$HashMapProvider = [function() {
3768           this.$get = [function() {
3769             return HashMap;
3770           }];
3771         }];
3772
3773         /**
3774          * @ngdoc function
3775          * @module ng
3776          * @name angular.injector
3777          * @kind function
3778          *
3779          * @description
3780          * Creates an injector object that can be used for retrieving services as well as for
3781          * dependency injection (see {@link guide/di dependency injection}).
3782          *
3783          * @param {Array.<string|Function>} modules A list of module functions or their aliases. See
3784          *     {@link angular.module}. The `ng` module must be explicitly added.
3785          * @param {boolean=} [strictDi=false] Whether the injector should be in strict mode, which
3786          *     disallows argument name annotation inference.
3787          * @returns {injector} Injector object. See {@link auto.$injector $injector}.
3788          *
3789          * @example
3790          * Typical usage
3791          * ```js
3792          *   // create an injector
3793          *   var $injector = angular.injector(['ng']);
3794          *
3795          *   // use the injector to kick off your application
3796          *   // use the type inference to auto inject arguments, or use implicit injection
3797          *   $injector.invoke(function($rootScope, $compile, $document) {
3798          *     $compile($document)($rootScope);
3799          *     $rootScope.$digest();
3800          *   });
3801          * ```
3802          *
3803          * Sometimes you want to get access to the injector of a currently running Angular app
3804          * from outside Angular. Perhaps, you want to inject and compile some markup after the
3805          * application has been bootstrapped. You can do this using the extra `injector()` added
3806          * to JQuery/jqLite elements. See {@link angular.element}.
3807          *
3808          * *This is fairly rare but could be the case if a third party library is injecting the
3809          * markup.*
3810          *
3811          * In the following example a new block of HTML containing a `ng-controller`
3812          * directive is added to the end of the document body by JQuery. We then compile and link
3813          * it into the current AngularJS scope.
3814          *
3815          * ```js
3816          * var $div = $('<div ng-controller="MyCtrl">{{content.label}}</div>');
3817          * $(document.body).append($div);
3818          *
3819          * angular.element(document).injector().invoke(function($compile) {
3820          *   var scope = angular.element($div).scope();
3821          *   $compile($div)(scope);
3822          * });
3823          * ```
3824          */
3825
3826
3827         /**
3828          * @ngdoc module
3829          * @name auto
3830          * @description
3831          *
3832          * Implicit module which gets automatically added to each {@link auto.$injector $injector}.
3833          */
3834
3835         var FN_ARGS = /^[^\(]*\(\s*([^\)]*)\)/m;
3836         var FN_ARG_SPLIT = /,/;
3837         var FN_ARG = /^\s*(_?)(\S+?)\1\s*$/;
3838         var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
3839         var $injectorMinErr = minErr('$injector');
3840
3841         function anonFn(fn) {
3842           // For anonymous functions, showing at the very least the function signature can help in
3843           // debugging.
3844           var fnText = fn.toString().replace(STRIP_COMMENTS, ''),
3845               args = fnText.match(FN_ARGS);
3846           if (args) {
3847             return 'function(' + (args[1] || '').replace(/[\s\r\n]+/, ' ') + ')';
3848           }
3849           return 'fn';
3850         }
3851
3852         function annotate(fn, strictDi, name) {
3853           var $inject,
3854               fnText,
3855               argDecl,
3856               last;
3857
3858           if (typeof fn === 'function') {
3859             if (!($inject = fn.$inject)) {
3860               $inject = [];
3861               if (fn.length) {
3862                 if (strictDi) {
3863                   if (!isString(name) || !name) {
3864                     name = fn.name || anonFn(fn);
3865                   }
3866                   throw $injectorMinErr('strictdi',
3867                     '{0} is not using explicit annotation and cannot be invoked in strict mode', name);
3868                 }
3869                 fnText = fn.toString().replace(STRIP_COMMENTS, '');
3870                 argDecl = fnText.match(FN_ARGS);
3871                 forEach(argDecl[1].split(FN_ARG_SPLIT), function(arg) {
3872                   arg.replace(FN_ARG, function(all, underscore, name) {
3873                     $inject.push(name);
3874                   });
3875                 });
3876               }
3877               fn.$inject = $inject;
3878             }
3879           } else if (isArray(fn)) {
3880             last = fn.length - 1;
3881             assertArgFn(fn[last], 'fn');
3882             $inject = fn.slice(0, last);
3883           } else {
3884             assertArgFn(fn, 'fn', true);
3885           }
3886           return $inject;
3887         }
3888
3889         ///////////////////////////////////////
3890
3891         /**
3892          * @ngdoc service
3893          * @name $injector
3894          *
3895          * @description
3896          *
3897          * `$injector` is used to retrieve object instances as defined by
3898          * {@link auto.$provide provider}, instantiate types, invoke methods,
3899          * and load modules.
3900          *
3901          * The following always holds true:
3902          *
3903          * ```js
3904          *   var $injector = angular.injector();
3905          *   expect($injector.get('$injector')).toBe($injector);
3906          *   expect($injector.invoke(function($injector) {
3907          *     return $injector;
3908          *   })).toBe($injector);
3909          * ```
3910          *
3911          * # Injection Function Annotation
3912          *
3913          * JavaScript does not have annotations, and annotations are needed for dependency injection. The
3914          * following are all valid ways of annotating function with injection arguments and are equivalent.
3915          *
3916          * ```js
3917          *   // inferred (only works if code not minified/obfuscated)
3918          *   $injector.invoke(function(serviceA){});
3919          *
3920          *   // annotated
3921          *   function explicit(serviceA) {};
3922          *   explicit.$inject = ['serviceA'];
3923          *   $injector.invoke(explicit);
3924          *
3925          *   // inline
3926          *   $injector.invoke(['serviceA', function(serviceA){}]);
3927          * ```
3928          *
3929          * ## Inference
3930          *
3931          * In JavaScript calling `toString()` on a function returns the function definition. The definition
3932          * can then be parsed and the function arguments can be extracted. This method of discovering
3933          * annotations is disallowed when the injector is in strict mode.
3934          * *NOTE:* This does not work with minification, and obfuscation tools since these tools change the
3935          * argument names.
3936          *
3937          * ## `$inject` Annotation
3938          * By adding an `$inject` property onto a function the injection parameters can be specified.
3939          *
3940          * ## Inline
3941          * As an array of injection names, where the last item in the array is the function to call.
3942          */
3943
3944         /**
3945          * @ngdoc method
3946          * @name $injector#get
3947          *
3948          * @description
3949          * Return an instance of the service.
3950          *
3951          * @param {string} name The name of the instance to retrieve.
3952          * @param {string=} caller An optional string to provide the origin of the function call for error messages.
3953          * @return {*} The instance.
3954          */
3955
3956         /**
3957          * @ngdoc method
3958          * @name $injector#invoke
3959          *
3960          * @description
3961          * Invoke the method and supply the method arguments from the `$injector`.
3962          *
3963          * @param {Function|Array.<string|Function>} fn The injectable function to invoke. Function parameters are
3964          *   injected according to the {@link guide/di $inject Annotation} rules.
3965          * @param {Object=} self The `this` for the invoked method.
3966          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3967          *                         object first, before the `$injector` is consulted.
3968          * @returns {*} the value returned by the invoked `fn` function.
3969          */
3970
3971         /**
3972          * @ngdoc method
3973          * @name $injector#has
3974          *
3975          * @description
3976          * Allows the user to query if the particular service exists.
3977          *
3978          * @param {string} name Name of the service to query.
3979          * @returns {boolean} `true` if injector has given service.
3980          */
3981
3982         /**
3983          * @ngdoc method
3984          * @name $injector#instantiate
3985          * @description
3986          * Create a new instance of JS type. The method takes a constructor function, invokes the new
3987          * operator, and supplies all of the arguments to the constructor function as specified by the
3988          * constructor annotation.
3989          *
3990          * @param {Function} Type Annotated constructor function.
3991          * @param {Object=} locals Optional object. If preset then any argument names are read from this
3992          * object first, before the `$injector` is consulted.
3993          * @returns {Object} new instance of `Type`.
3994          */
3995
3996         /**
3997          * @ngdoc method
3998          * @name $injector#annotate
3999          *
4000          * @description
4001          * Returns an array of service names which the function is requesting for injection. This API is
4002          * used by the injector to determine which services need to be injected into the function when the
4003          * function is invoked. There are three ways in which the function can be annotated with the needed
4004          * dependencies.
4005          *
4006          * # Argument names
4007          *
4008          * The simplest form is to extract the dependencies from the arguments of the function. This is done
4009          * by converting the function into a string using `toString()` method and extracting the argument
4010          * names.
4011          * ```js
4012          *   // Given
4013          *   function MyController($scope, $route) {
4014          *     // ...
4015          *   }
4016          *
4017          *   // Then
4018          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4019          * ```
4020          *
4021          * You can disallow this method by using strict injection mode.
4022          *
4023          * This method does not work with code minification / obfuscation. For this reason the following
4024          * annotation strategies are supported.
4025          *
4026          * # The `$inject` property
4027          *
4028          * If a function has an `$inject` property and its value is an array of strings, then the strings
4029          * represent names of services to be injected into the function.
4030          * ```js
4031          *   // Given
4032          *   var MyController = function(obfuscatedScope, obfuscatedRoute) {
4033          *     // ...
4034          *   }
4035          *   // Define function dependencies
4036          *   MyController['$inject'] = ['$scope', '$route'];
4037          *
4038          *   // Then
4039          *   expect(injector.annotate(MyController)).toEqual(['$scope', '$route']);
4040          * ```
4041          *
4042          * # The array notation
4043          *
4044          * It is often desirable to inline Injected functions and that's when setting the `$inject` property
4045          * is very inconvenient. In these situations using the array notation to specify the dependencies in
4046          * a way that survives minification is a better choice:
4047          *
4048          * ```js
4049          *   // We wish to write this (not minification / obfuscation safe)
4050          *   injector.invoke(function($compile, $rootScope) {
4051          *     // ...
4052          *   });
4053          *
4054          *   // We are forced to write break inlining
4055          *   var tmpFn = function(obfuscatedCompile, obfuscatedRootScope) {
4056          *     // ...
4057          *   };
4058          *   tmpFn.$inject = ['$compile', '$rootScope'];
4059          *   injector.invoke(tmpFn);
4060          *
4061          *   // To better support inline function the inline annotation is supported
4062          *   injector.invoke(['$compile', '$rootScope', function(obfCompile, obfRootScope) {
4063          *     // ...
4064          *   }]);
4065          *
4066          *   // Therefore
4067          *   expect(injector.annotate(
4068          *      ['$compile', '$rootScope', function(obfus_$compile, obfus_$rootScope) {}])
4069          *    ).toEqual(['$compile', '$rootScope']);
4070          * ```
4071          *
4072          * @param {Function|Array.<string|Function>} fn Function for which dependent service names need to
4073          * be retrieved as described above.
4074          *
4075          * @param {boolean=} [strictDi=false] Disallow argument name annotation inference.
4076          *
4077          * @returns {Array.<string>} The names of the services which the function requires.
4078          */
4079
4080
4081
4082
4083         /**
4084          * @ngdoc service
4085          * @name $provide
4086          *
4087          * @description
4088          *
4089          * The {@link auto.$provide $provide} service has a number of methods for registering components
4090          * with the {@link auto.$injector $injector}. Many of these functions are also exposed on
4091          * {@link angular.Module}.
4092          *
4093          * An Angular **service** is a singleton object created by a **service factory**.  These **service
4094          * factories** are functions which, in turn, are created by a **service provider**.
4095          * The **service providers** are constructor functions. When instantiated they must contain a
4096          * property called `$get`, which holds the **service factory** function.
4097          *
4098          * When you request a service, the {@link auto.$injector $injector} is responsible for finding the
4099          * correct **service provider**, instantiating it and then calling its `$get` **service factory**
4100          * function to get the instance of the **service**.
4101          *
4102          * Often services have no configuration options and there is no need to add methods to the service
4103          * provider.  The provider will be no more than a constructor function with a `$get` property. For
4104          * these cases the {@link auto.$provide $provide} service has additional helper methods to register
4105          * services without specifying a provider.
4106          *
4107          * * {@link auto.$provide#provider provider(provider)} - registers a **service provider** with the
4108          *     {@link auto.$injector $injector}
4109          * * {@link auto.$provide#constant constant(obj)} - registers a value/object that can be accessed by
4110          *     providers and services.
4111          * * {@link auto.$provide#value value(obj)} - registers a value/object that can only be accessed by
4112          *     services, not providers.
4113          * * {@link auto.$provide#factory factory(fn)} - registers a service **factory function**, `fn`,
4114          *     that will be wrapped in a **service provider** object, whose `$get` property will contain the
4115          *     given factory function.
4116          * * {@link auto.$provide#service service(class)} - registers a **constructor function**, `class`
4117          *     that will be wrapped in a **service provider** object, whose `$get` property will instantiate
4118          *      a new object using the given constructor function.
4119          *
4120          * See the individual methods for more information and examples.
4121          */
4122
4123         /**
4124          * @ngdoc method
4125          * @name $provide#provider
4126          * @description
4127          *
4128          * Register a **provider function** with the {@link auto.$injector $injector}. Provider functions
4129          * are constructor functions, whose instances are responsible for "providing" a factory for a
4130          * service.
4131          *
4132          * Service provider names start with the name of the service they provide followed by `Provider`.
4133          * For example, the {@link ng.$log $log} service has a provider called
4134          * {@link ng.$logProvider $logProvider}.
4135          *
4136          * Service provider objects can have additional methods which allow configuration of the provider
4137          * and its service. Importantly, you can configure what kind of service is created by the `$get`
4138          * method, or how that service will act. For example, the {@link ng.$logProvider $logProvider} has a
4139          * method {@link ng.$logProvider#debugEnabled debugEnabled}
4140          * which lets you specify whether the {@link ng.$log $log} service will log debug messages to the
4141          * console or not.
4142          *
4143          * @param {string} name The name of the instance. NOTE: the provider will be available under `name +
4144                                 'Provider'` key.
4145          * @param {(Object|function())} provider If the provider is:
4146          *
4147          *   - `Object`: then it should have a `$get` method. The `$get` method will be invoked using
4148          *     {@link auto.$injector#invoke $injector.invoke()} when an instance needs to be created.
4149          *   - `Constructor`: a new instance of the provider will be created using
4150          *     {@link auto.$injector#instantiate $injector.instantiate()}, then treated as `object`.
4151          *
4152          * @returns {Object} registered provider instance
4153
4154          * @example
4155          *
4156          * The following example shows how to create a simple event tracking service and register it using
4157          * {@link auto.$provide#provider $provide.provider()}.
4158          *
4159          * ```js
4160          *  // Define the eventTracker provider
4161          *  function EventTrackerProvider() {
4162          *    var trackingUrl = '/track';
4163          *
4164          *    // A provider method for configuring where the tracked events should been saved
4165          *    this.setTrackingUrl = function(url) {
4166          *      trackingUrl = url;
4167          *    };
4168          *
4169          *    // The service factory function
4170          *    this.$get = ['$http', function($http) {
4171          *      var trackedEvents = {};
4172          *      return {
4173          *        // Call this to track an event
4174          *        event: function(event) {
4175          *          var count = trackedEvents[event] || 0;
4176          *          count += 1;
4177          *          trackedEvents[event] = count;
4178          *          return count;
4179          *        },
4180          *        // Call this to save the tracked events to the trackingUrl
4181          *        save: function() {
4182          *          $http.post(trackingUrl, trackedEvents);
4183          *        }
4184          *      };
4185          *    }];
4186          *  }
4187          *
4188          *  describe('eventTracker', function() {
4189          *    var postSpy;
4190          *
4191          *    beforeEach(module(function($provide) {
4192          *      // Register the eventTracker provider
4193          *      $provide.provider('eventTracker', EventTrackerProvider);
4194          *    }));
4195          *
4196          *    beforeEach(module(function(eventTrackerProvider) {
4197          *      // Configure eventTracker provider
4198          *      eventTrackerProvider.setTrackingUrl('/custom-track');
4199          *    }));
4200          *
4201          *    it('tracks events', inject(function(eventTracker) {
4202          *      expect(eventTracker.event('login')).toEqual(1);
4203          *      expect(eventTracker.event('login')).toEqual(2);
4204          *    }));
4205          *
4206          *    it('saves to the tracking url', inject(function(eventTracker, $http) {
4207          *      postSpy = spyOn($http, 'post');
4208          *      eventTracker.event('login');
4209          *      eventTracker.save();
4210          *      expect(postSpy).toHaveBeenCalled();
4211          *      expect(postSpy.mostRecentCall.args[0]).not.toEqual('/track');
4212          *      expect(postSpy.mostRecentCall.args[0]).toEqual('/custom-track');
4213          *      expect(postSpy.mostRecentCall.args[1]).toEqual({ 'login': 1 });
4214          *    }));
4215          *  });
4216          * ```
4217          */
4218
4219         /**
4220          * @ngdoc method
4221          * @name $provide#factory
4222          * @description
4223          *
4224          * Register a **service factory**, which will be called to return the service instance.
4225          * This is short for registering a service where its provider consists of only a `$get` property,
4226          * which is the given service factory function.
4227          * You should use {@link auto.$provide#factory $provide.factory(getFn)} if you do not need to
4228          * configure your service in a provider.
4229          *
4230          * @param {string} name The name of the instance.
4231          * @param {Function|Array.<string|Function>} $getFn The injectable $getFn for the instance creation.
4232          *                      Internally this is a short hand for `$provide.provider(name, {$get: $getFn})`.
4233          * @returns {Object} registered provider instance
4234          *
4235          * @example
4236          * Here is an example of registering a service
4237          * ```js
4238          *   $provide.factory('ping', ['$http', function($http) {
4239          *     return function ping() {
4240          *       return $http.send('/ping');
4241          *     };
4242          *   }]);
4243          * ```
4244          * You would then inject and use this service like this:
4245          * ```js
4246          *   someModule.controller('Ctrl', ['ping', function(ping) {
4247          *     ping();
4248          *   }]);
4249          * ```
4250          */
4251
4252
4253         /**
4254          * @ngdoc method
4255          * @name $provide#service
4256          * @description
4257          *
4258          * Register a **service constructor**, which will be invoked with `new` to create the service
4259          * instance.
4260          * This is short for registering a service where its provider's `$get` property is the service
4261          * constructor function that will be used to instantiate the service instance.
4262          *
4263          * You should use {@link auto.$provide#service $provide.service(class)} if you define your service
4264          * as a type/class.
4265          *
4266          * @param {string} name The name of the instance.
4267          * @param {Function|Array.<string|Function>} constructor An injectable class (constructor function)
4268          *     that will be instantiated.
4269          * @returns {Object} registered provider instance
4270          *
4271          * @example
4272          * Here is an example of registering a service using
4273          * {@link auto.$provide#service $provide.service(class)}.
4274          * ```js
4275          *   var Ping = function($http) {
4276          *     this.$http = $http;
4277          *   };
4278          *
4279          *   Ping.$inject = ['$http'];
4280          *
4281          *   Ping.prototype.send = function() {
4282          *     return this.$http.get('/ping');
4283          *   };
4284          *   $provide.service('ping', Ping);
4285          * ```
4286          * You would then inject and use this service like this:
4287          * ```js
4288          *   someModule.controller('Ctrl', ['ping', function(ping) {
4289          *     ping.send();
4290          *   }]);
4291          * ```
4292          */
4293
4294
4295         /**
4296          * @ngdoc method
4297          * @name $provide#value
4298          * @description
4299          *
4300          * Register a **value service** with the {@link auto.$injector $injector}, such as a string, a
4301          * number, an array, an object or a function.  This is short for registering a service where its
4302          * provider's `$get` property is a factory function that takes no arguments and returns the **value
4303          * service**.
4304          *
4305          * Value services are similar to constant services, except that they cannot be injected into a
4306          * module configuration function (see {@link angular.Module#config}) but they can be overridden by
4307          * an Angular
4308          * {@link auto.$provide#decorator decorator}.
4309          *
4310          * @param {string} name The name of the instance.
4311          * @param {*} value The value.
4312          * @returns {Object} registered provider instance
4313          *
4314          * @example
4315          * Here are some examples of creating value services.
4316          * ```js
4317          *   $provide.value('ADMIN_USER', 'admin');
4318          *
4319          *   $provide.value('RoleLookup', { admin: 0, writer: 1, reader: 2 });
4320          *
4321          *   $provide.value('halfOf', function(value) {
4322          *     return value / 2;
4323          *   });
4324          * ```
4325          */
4326
4327
4328         /**
4329          * @ngdoc method
4330          * @name $provide#constant
4331          * @description
4332          *
4333          * Register a **constant service**, such as a string, a number, an array, an object or a function,
4334          * with the {@link auto.$injector $injector}. Unlike {@link auto.$provide#value value} it can be
4335          * injected into a module configuration function (see {@link angular.Module#config}) and it cannot
4336          * be overridden by an Angular {@link auto.$provide#decorator decorator}.
4337          *
4338          * @param {string} name The name of the constant.
4339          * @param {*} value The constant value.
4340          * @returns {Object} registered instance
4341          *
4342          * @example
4343          * Here a some examples of creating constants:
4344          * ```js
4345          *   $provide.constant('SHARD_HEIGHT', 306);
4346          *
4347          *   $provide.constant('MY_COLOURS', ['red', 'blue', 'grey']);
4348          *
4349          *   $provide.constant('double', function(value) {
4350          *     return value * 2;
4351          *   });
4352          * ```
4353          */
4354
4355
4356         /**
4357          * @ngdoc method
4358          * @name $provide#decorator
4359          * @description
4360          *
4361          * Register a **service decorator** with the {@link auto.$injector $injector}. A service decorator
4362          * intercepts the creation of a service, allowing it to override or modify the behaviour of the
4363          * service. The object returned by the decorator may be the original service, or a new service
4364          * object which replaces or wraps and delegates to the original service.
4365          *
4366          * @param {string} name The name of the service to decorate.
4367          * @param {Function|Array.<string|Function>} decorator This function will be invoked when the service needs to be
4368          *    instantiated and should return the decorated service instance. The function is called using
4369          *    the {@link auto.$injector#invoke injector.invoke} method and is therefore fully injectable.
4370          *    Local injection arguments:
4371          *
4372          *    * `$delegate` - The original service instance, which can be monkey patched, configured,
4373          *      decorated or delegated to.
4374          *
4375          * @example
4376          * Here we decorate the {@link ng.$log $log} service to convert warnings to errors by intercepting
4377          * calls to {@link ng.$log#error $log.warn()}.
4378          * ```js
4379          *   $provide.decorator('$log', ['$delegate', function($delegate) {
4380          *     $delegate.warn = $delegate.error;
4381          *     return $delegate;
4382          *   }]);
4383          * ```
4384          */
4385
4386
4387         function createInjector(modulesToLoad, strictDi) {
4388           strictDi = (strictDi === true);
4389           var INSTANTIATING = {},
4390               providerSuffix = 'Provider',
4391               path = [],
4392               loadedModules = new HashMap([], true),
4393               providerCache = {
4394                 $provide: {
4395                     provider: supportObject(provider),
4396                     factory: supportObject(factory),
4397                     service: supportObject(service),
4398                     value: supportObject(value),
4399                     constant: supportObject(constant),
4400                     decorator: decorator
4401                   }
4402               },
4403               providerInjector = (providerCache.$injector =
4404                   createInternalInjector(providerCache, function(serviceName, caller) {
4405                     if (angular.isString(caller)) {
4406                       path.push(caller);
4407                     }
4408                     throw $injectorMinErr('unpr', "Unknown provider: {0}", path.join(' <- '));
4409                   })),
4410               instanceCache = {},
4411               instanceInjector = (instanceCache.$injector =
4412                   createInternalInjector(instanceCache, function(serviceName, caller) {
4413                     var provider = providerInjector.get(serviceName + providerSuffix, caller);
4414                     return instanceInjector.invoke(provider.$get, provider, undefined, serviceName);
4415                   }));
4416
4417
4418           forEach(loadModules(modulesToLoad), function(fn) { if (fn) instanceInjector.invoke(fn); });
4419
4420           return instanceInjector;
4421
4422           ////////////////////////////////////
4423           // $provider
4424           ////////////////////////////////////
4425
4426           function supportObject(delegate) {
4427             return function(key, value) {
4428               if (isObject(key)) {
4429                 forEach(key, reverseParams(delegate));
4430               } else {
4431                 return delegate(key, value);
4432               }
4433             };
4434           }
4435
4436           function provider(name, provider_) {
4437             assertNotHasOwnProperty(name, 'service');
4438             if (isFunction(provider_) || isArray(provider_)) {
4439               provider_ = providerInjector.instantiate(provider_);
4440             }
4441             if (!provider_.$get) {
4442               throw $injectorMinErr('pget', "Provider '{0}' must define $get factory method.", name);
4443             }
4444             return providerCache[name + providerSuffix] = provider_;
4445           }
4446
4447           function enforceReturnValue(name, factory) {
4448             return function enforcedReturnValue() {
4449               var result = instanceInjector.invoke(factory, this);
4450               if (isUndefined(result)) {
4451                 throw $injectorMinErr('undef', "Provider '{0}' must return a value from $get factory method.", name);
4452               }
4453               return result;
4454             };
4455           }
4456
4457           function factory(name, factoryFn, enforce) {
4458             return provider(name, {
4459               $get: enforce !== false ? enforceReturnValue(name, factoryFn) : factoryFn
4460             });
4461           }
4462
4463           function service(name, constructor) {
4464             return factory(name, ['$injector', function($injector) {
4465               return $injector.instantiate(constructor);
4466             }]);
4467           }
4468
4469           function value(name, val) { return factory(name, valueFn(val), false); }
4470
4471           function constant(name, value) {
4472             assertNotHasOwnProperty(name, 'constant');
4473             providerCache[name] = value;
4474             instanceCache[name] = value;
4475           }
4476
4477           function decorator(serviceName, decorFn) {
4478             var origProvider = providerInjector.get(serviceName + providerSuffix),
4479                 orig$get = origProvider.$get;
4480
4481             origProvider.$get = function() {
4482               var origInstance = instanceInjector.invoke(orig$get, origProvider);
4483               return instanceInjector.invoke(decorFn, null, {$delegate: origInstance});
4484             };
4485           }
4486
4487           ////////////////////////////////////
4488           // Module Loading
4489           ////////////////////////////////////
4490           function loadModules(modulesToLoad) {
4491             assertArg(isUndefined(modulesToLoad) || isArray(modulesToLoad), 'modulesToLoad', 'not an array');
4492             var runBlocks = [], moduleFn;
4493             forEach(modulesToLoad, function(module) {
4494               if (loadedModules.get(module)) return;
4495               loadedModules.put(module, true);
4496
4497               function runInvokeQueue(queue) {
4498                 var i, ii;
4499                 for (i = 0, ii = queue.length; i < ii; i++) {
4500                   var invokeArgs = queue[i],
4501                       provider = providerInjector.get(invokeArgs[0]);
4502
4503                   provider[invokeArgs[1]].apply(provider, invokeArgs[2]);
4504                 }
4505               }
4506
4507               try {
4508                 if (isString(module)) {
4509                   moduleFn = angularModule(module);
4510                   runBlocks = runBlocks.concat(loadModules(moduleFn.requires)).concat(moduleFn._runBlocks);
4511                   runInvokeQueue(moduleFn._invokeQueue);
4512                   runInvokeQueue(moduleFn._configBlocks);
4513                 } else if (isFunction(module)) {
4514                     runBlocks.push(providerInjector.invoke(module));
4515                 } else if (isArray(module)) {
4516                     runBlocks.push(providerInjector.invoke(module));
4517                 } else {
4518                   assertArgFn(module, 'module');
4519                 }
4520               } catch (e) {
4521                 if (isArray(module)) {
4522                   module = module[module.length - 1];
4523                 }
4524                 if (e.message && e.stack && e.stack.indexOf(e.message) == -1) {
4525                   // Safari & FF's stack traces don't contain error.message content
4526                   // unlike those of Chrome and IE
4527                   // So if stack doesn't contain message, we create a new string that contains both.
4528                   // Since error.stack is read-only in Safari, I'm overriding e and not e.stack here.
4529                   /* jshint -W022 */
4530                   e = e.message + '\n' + e.stack;
4531                 }
4532                 throw $injectorMinErr('modulerr', "Failed to instantiate module {0} due to:\n{1}",
4533                           module, e.stack || e.message || e);
4534               }
4535             });
4536             return runBlocks;
4537           }
4538
4539           ////////////////////////////////////
4540           // internal Injector
4541           ////////////////////////////////////
4542
4543           function createInternalInjector(cache, factory) {
4544
4545             function getService(serviceName, caller) {
4546               if (cache.hasOwnProperty(serviceName)) {
4547                 if (cache[serviceName] === INSTANTIATING) {
4548                   throw $injectorMinErr('cdep', 'Circular dependency found: {0}',
4549                             serviceName + ' <- ' + path.join(' <- '));
4550                 }
4551                 return cache[serviceName];
4552               } else {
4553                 try {
4554                   path.unshift(serviceName);
4555                   cache[serviceName] = INSTANTIATING;
4556                   return cache[serviceName] = factory(serviceName, caller);
4557                 } catch (err) {
4558                   if (cache[serviceName] === INSTANTIATING) {
4559                     delete cache[serviceName];
4560                   }
4561                   throw err;
4562                 } finally {
4563                   path.shift();
4564                 }
4565               }
4566             }
4567
4568             function invoke(fn, self, locals, serviceName) {
4569               if (typeof locals === 'string') {
4570                 serviceName = locals;
4571                 locals = null;
4572               }
4573
4574               var args = [],
4575                   $inject = createInjector.$$annotate(fn, strictDi, serviceName),
4576                   length, i,
4577                   key;
4578
4579               for (i = 0, length = $inject.length; i < length; i++) {
4580                 key = $inject[i];
4581                 if (typeof key !== 'string') {
4582                   throw $injectorMinErr('itkn',
4583                           'Incorrect injection token! Expected service name as string, got {0}', key);
4584                 }
4585                 args.push(
4586                   locals && locals.hasOwnProperty(key)
4587                   ? locals[key]
4588                   : getService(key, serviceName)
4589                 );
4590               }
4591               if (isArray(fn)) {
4592                 fn = fn[length];
4593               }
4594
4595               // http://jsperf.com/angularjs-invoke-apply-vs-switch
4596               // #5388
4597               return fn.apply(self, args);
4598             }
4599
4600             function instantiate(Type, locals, serviceName) {
4601               // Check if Type is annotated and use just the given function at n-1 as parameter
4602               // e.g. someModule.factory('greeter', ['$window', function(renamed$window) {}]);
4603               // Object creation: http://jsperf.com/create-constructor/2
4604               var instance = Object.create((isArray(Type) ? Type[Type.length - 1] : Type).prototype || null);
4605               var returnedValue = invoke(Type, instance, locals, serviceName);
4606
4607               return isObject(returnedValue) || isFunction(returnedValue) ? returnedValue : instance;
4608             }
4609
4610             return {
4611               invoke: invoke,
4612               instantiate: instantiate,
4613               get: getService,
4614               annotate: createInjector.$$annotate,
4615               has: function(name) {
4616                 return providerCache.hasOwnProperty(name + providerSuffix) || cache.hasOwnProperty(name);
4617               }
4618             };
4619           }
4620         }
4621
4622         createInjector.$$annotate = annotate;
4623
4624         /**
4625          * @ngdoc provider
4626          * @name $anchorScrollProvider
4627          *
4628          * @description
4629          * Use `$anchorScrollProvider` to disable automatic scrolling whenever
4630          * {@link ng.$location#hash $location.hash()} changes.
4631          */
4632         function $AnchorScrollProvider() {
4633
4634           var autoScrollingEnabled = true;
4635
4636           /**
4637            * @ngdoc method
4638            * @name $anchorScrollProvider#disableAutoScrolling
4639            *
4640            * @description
4641            * By default, {@link ng.$anchorScroll $anchorScroll()} will automatically detect changes to
4642            * {@link ng.$location#hash $location.hash()} and scroll to the element matching the new hash.<br />
4643            * Use this method to disable automatic scrolling.
4644            *
4645            * If automatic scrolling is disabled, one must explicitly call
4646            * {@link ng.$anchorScroll $anchorScroll()} in order to scroll to the element related to the
4647            * current hash.
4648            */
4649           this.disableAutoScrolling = function() {
4650             autoScrollingEnabled = false;
4651           };
4652
4653           /**
4654            * @ngdoc service
4655            * @name $anchorScroll
4656            * @kind function
4657            * @requires $window
4658            * @requires $location
4659            * @requires $rootScope
4660            *
4661            * @description
4662            * When called, it scrolls to the element related to the specified `hash` or (if omitted) to the
4663            * current value of {@link ng.$location#hash $location.hash()}, according to the rules specified
4664            * in the
4665            * [HTML5 spec](http://www.w3.org/html/wg/drafts/html/master/browsers.html#the-indicated-part-of-the-document).
4666            *
4667            * It also watches the {@link ng.$location#hash $location.hash()} and automatically scrolls to
4668            * match any anchor whenever it changes. This can be disabled by calling
4669            * {@link ng.$anchorScrollProvider#disableAutoScrolling $anchorScrollProvider.disableAutoScrolling()}.
4670            *
4671            * Additionally, you can use its {@link ng.$anchorScroll#yOffset yOffset} property to specify a
4672            * vertical scroll-offset (either fixed or dynamic).
4673            *
4674            * @param {string=} hash The hash specifying the element to scroll to. If omitted, the value of
4675            *                       {@link ng.$location#hash $location.hash()} will be used.
4676            *
4677            * @property {(number|function|jqLite)} yOffset
4678            * If set, specifies a vertical scroll-offset. This is often useful when there are fixed
4679            * positioned elements at the top of the page, such as navbars, headers etc.
4680            *
4681            * `yOffset` can be specified in various ways:
4682            * - **number**: A fixed number of pixels to be used as offset.<br /><br />
4683            * - **function**: A getter function called everytime `$anchorScroll()` is executed. Must return
4684            *   a number representing the offset (in pixels).<br /><br />
4685            * - **jqLite**: A jqLite/jQuery element to be used for specifying the offset. The distance from
4686            *   the top of the page to the element's bottom will be used as offset.<br />
4687            *   **Note**: The element will be taken into account only as long as its `position` is set to
4688            *   `fixed`. This option is useful, when dealing with responsive navbars/headers that adjust
4689            *   their height and/or positioning according to the viewport's size.
4690            *
4691            * <br />
4692            * <div class="alert alert-warning">
4693            * In order for `yOffset` to work properly, scrolling should take place on the document's root and
4694            * not some child element.
4695            * </div>
4696            *
4697            * @example
4698              <example module="anchorScrollExample">
4699                <file name="index.html">
4700                  <div id="scrollArea" ng-controller="ScrollController">
4701                    <a ng-click="gotoBottom()">Go to bottom</a>
4702                    <a id="bottom"></a> You're at the bottom!
4703                  </div>
4704                </file>
4705                <file name="script.js">
4706                  angular.module('anchorScrollExample', [])
4707                    .controller('ScrollController', ['$scope', '$location', '$anchorScroll',
4708                      function ($scope, $location, $anchorScroll) {
4709                        $scope.gotoBottom = function() {
4710                          // set the location.hash to the id of
4711                          // the element you wish to scroll to.
4712                          $location.hash('bottom');
4713
4714                          // call $anchorScroll()
4715                          $anchorScroll();
4716                        };
4717                      }]);
4718                </file>
4719                <file name="style.css">
4720                  #scrollArea {
4721                    height: 280px;
4722                    overflow: auto;
4723                  }
4724
4725                  #bottom {
4726                    display: block;
4727                    margin-top: 2000px;
4728                  }
4729                </file>
4730              </example>
4731            *
4732            * <hr />
4733            * The example below illustrates the use of a vertical scroll-offset (specified as a fixed value).
4734            * See {@link ng.$anchorScroll#yOffset $anchorScroll.yOffset} for more details.
4735            *
4736            * @example
4737              <example module="anchorScrollOffsetExample">
4738                <file name="index.html">
4739                  <div class="fixed-header" ng-controller="headerCtrl">
4740                    <a href="" ng-click="gotoAnchor(x)" ng-repeat="x in [1,2,3,4,5]">
4741                      Go to anchor {{x}}
4742                    </a>
4743                  </div>
4744                  <div id="anchor{{x}}" class="anchor" ng-repeat="x in [1,2,3,4,5]">
4745                    Anchor {{x}} of 5
4746                  </div>
4747                </file>
4748                <file name="script.js">
4749                  angular.module('anchorScrollOffsetExample', [])
4750                    .run(['$anchorScroll', function($anchorScroll) {
4751                      $anchorScroll.yOffset = 50;   // always scroll by 50 extra pixels
4752                    }])
4753                    .controller('headerCtrl', ['$anchorScroll', '$location', '$scope',
4754                      function ($anchorScroll, $location, $scope) {
4755                        $scope.gotoAnchor = function(x) {
4756                          var newHash = 'anchor' + x;
4757                          if ($location.hash() !== newHash) {
4758                            // set the $location.hash to `newHash` and
4759                            // $anchorScroll will automatically scroll to it
4760                            $location.hash('anchor' + x);
4761                          } else {
4762                            // call $anchorScroll() explicitly,
4763                            // since $location.hash hasn't changed
4764                            $anchorScroll();
4765                          }
4766                        };
4767                      }
4768                    ]);
4769                </file>
4770                <file name="style.css">
4771                  body {
4772                    padding-top: 50px;
4773                  }
4774
4775                  .anchor {
4776                    border: 2px dashed DarkOrchid;
4777                    padding: 10px 10px 200px 10px;
4778                  }
4779
4780                  .fixed-header {
4781                    background-color: rgba(0, 0, 0, 0.2);
4782                    height: 50px;
4783                    position: fixed;
4784                    top: 0; left: 0; right: 0;
4785                  }
4786
4787                  .fixed-header > a {
4788                    display: inline-block;
4789                    margin: 5px 15px;
4790                  }
4791                </file>
4792              </example>
4793            */
4794           this.$get = ['$window', '$location', '$rootScope', function($window, $location, $rootScope) {
4795             var document = $window.document;
4796
4797             // Helper function to get first anchor from a NodeList
4798             // (using `Array#some()` instead of `angular#forEach()` since it's more performant
4799             //  and working in all supported browsers.)
4800             function getFirstAnchor(list) {
4801               var result = null;
4802               Array.prototype.some.call(list, function(element) {
4803                 if (nodeName_(element) === 'a') {
4804                   result = element;
4805                   return true;
4806                 }
4807               });
4808               return result;
4809             }
4810
4811             function getYOffset() {
4812
4813               var offset = scroll.yOffset;
4814
4815               if (isFunction(offset)) {
4816                 offset = offset();
4817               } else if (isElement(offset)) {
4818                 var elem = offset[0];
4819                 var style = $window.getComputedStyle(elem);
4820                 if (style.position !== 'fixed') {
4821                   offset = 0;
4822                 } else {
4823                   offset = elem.getBoundingClientRect().bottom;
4824                 }
4825               } else if (!isNumber(offset)) {
4826                 offset = 0;
4827               }
4828
4829               return offset;
4830             }
4831
4832             function scrollTo(elem) {
4833               if (elem) {
4834                 elem.scrollIntoView();
4835
4836                 var offset = getYOffset();
4837
4838                 if (offset) {
4839                   // `offset` is the number of pixels we should scroll UP in order to align `elem` properly.
4840                   // This is true ONLY if the call to `elem.scrollIntoView()` initially aligns `elem` at the
4841                   // top of the viewport.
4842                   //
4843                   // IF the number of pixels from the top of `elem` to the end of the page's content is less
4844                   // than the height of the viewport, then `elem.scrollIntoView()` will align the `elem` some
4845                   // way down the page.
4846                   //
4847                   // This is often the case for elements near the bottom of the page.
4848                   //
4849                   // In such cases we do not need to scroll the whole `offset` up, just the difference between
4850                   // the top of the element and the offset, which is enough to align the top of `elem` at the
4851                   // desired position.
4852                   var elemTop = elem.getBoundingClientRect().top;
4853                   $window.scrollBy(0, elemTop - offset);
4854                 }
4855               } else {
4856                 $window.scrollTo(0, 0);
4857               }
4858             }
4859
4860             function scroll(hash) {
4861               hash = isString(hash) ? hash : $location.hash();
4862               var elm;
4863
4864               // empty hash, scroll to the top of the page
4865               if (!hash) scrollTo(null);
4866
4867               // element with given id
4868               else if ((elm = document.getElementById(hash))) scrollTo(elm);
4869
4870               // first anchor with given name :-D
4871               else if ((elm = getFirstAnchor(document.getElementsByName(hash)))) scrollTo(elm);
4872
4873               // no element and hash == 'top', scroll to the top of the page
4874               else if (hash === 'top') scrollTo(null);
4875             }
4876
4877             // does not scroll when user clicks on anchor link that is currently on
4878             // (no url change, no $location.hash() change), browser native does scroll
4879             if (autoScrollingEnabled) {
4880               $rootScope.$watch(function autoScrollWatch() {return $location.hash();},
4881                 function autoScrollWatchAction(newVal, oldVal) {
4882                   // skip the initial scroll if $location.hash is empty
4883                   if (newVal === oldVal && newVal === '') return;
4884
4885                   jqLiteDocumentLoaded(function() {
4886                     $rootScope.$evalAsync(scroll);
4887                   });
4888                 });
4889             }
4890
4891             return scroll;
4892           }];
4893         }
4894
4895         var $animateMinErr = minErr('$animate');
4896         var ELEMENT_NODE = 1;
4897         var NG_ANIMATE_CLASSNAME = 'ng-animate';
4898
4899         function mergeClasses(a,b) {
4900           if (!a && !b) return '';
4901           if (!a) return b;
4902           if (!b) return a;
4903           if (isArray(a)) a = a.join(' ');
4904           if (isArray(b)) b = b.join(' ');
4905           return a + ' ' + b;
4906         }
4907
4908         function extractElementNode(element) {
4909           for (var i = 0; i < element.length; i++) {
4910             var elm = element[i];
4911             if (elm.nodeType === ELEMENT_NODE) {
4912               return elm;
4913             }
4914           }
4915         }
4916
4917         function splitClasses(classes) {
4918           if (isString(classes)) {
4919             classes = classes.split(' ');
4920           }
4921
4922           // Use createMap() to prevent class assumptions involving property names in
4923           // Object.prototype
4924           var obj = createMap();
4925           forEach(classes, function(klass) {
4926             // sometimes the split leaves empty string values
4927             // incase extra spaces were applied to the options
4928             if (klass.length) {
4929               obj[klass] = true;
4930             }
4931           });
4932           return obj;
4933         }
4934
4935         // if any other type of options value besides an Object value is
4936         // passed into the $animate.method() animation then this helper code
4937         // will be run which will ignore it. While this patch is not the
4938         // greatest solution to this, a lot of existing plugins depend on
4939         // $animate to either call the callback (< 1.2) or return a promise
4940         // that can be changed. This helper function ensures that the options
4941         // are wiped clean incase a callback function is provided.
4942         function prepareAnimateOptions(options) {
4943           return isObject(options)
4944               ? options
4945               : {};
4946         }
4947
4948         var $$CoreAnimateRunnerProvider = function() {
4949           this.$get = ['$q', '$$rAF', function($q, $$rAF) {
4950             function AnimateRunner() {}
4951             AnimateRunner.all = noop;
4952             AnimateRunner.chain = noop;
4953             AnimateRunner.prototype = {
4954               end: noop,
4955               cancel: noop,
4956               resume: noop,
4957               pause: noop,
4958               complete: noop,
4959               then: function(pass, fail) {
4960                 return $q(function(resolve) {
4961                   $$rAF(function() {
4962                     resolve();
4963                   });
4964                 }).then(pass, fail);
4965               }
4966             };
4967             return AnimateRunner;
4968           }];
4969         };
4970
4971         // this is prefixed with Core since it conflicts with
4972         // the animateQueueProvider defined in ngAnimate/animateQueue.js
4973         var $$CoreAnimateQueueProvider = function() {
4974           var postDigestQueue = new HashMap();
4975           var postDigestElements = [];
4976
4977           this.$get = ['$$AnimateRunner', '$rootScope',
4978                function($$AnimateRunner,   $rootScope) {
4979             return {
4980               enabled: noop,
4981               on: noop,
4982               off: noop,
4983               pin: noop,
4984
4985               push: function(element, event, options, domOperation) {
4986                 domOperation        && domOperation();
4987
4988                 options = options || {};
4989                 options.from        && element.css(options.from);
4990                 options.to          && element.css(options.to);
4991
4992                 if (options.addClass || options.removeClass) {
4993                   addRemoveClassesPostDigest(element, options.addClass, options.removeClass);
4994                 }
4995
4996                 return new $$AnimateRunner(); // jshint ignore:line
4997               }
4998             };
4999
5000
5001             function updateData(data, classes, value) {
5002               var changed = false;
5003               if (classes) {
5004                 classes = isString(classes) ? classes.split(' ') :
5005                           isArray(classes) ? classes : [];
5006                 forEach(classes, function(className) {
5007                   if (className) {
5008                     changed = true;
5009                     data[className] = value;
5010                   }
5011                 });
5012               }
5013               return changed;
5014             }
5015
5016             function handleCSSClassChanges() {
5017               forEach(postDigestElements, function(element) {
5018                 var data = postDigestQueue.get(element);
5019                 if (data) {
5020                   var existing = splitClasses(element.attr('class'));
5021                   var toAdd = '';
5022                   var toRemove = '';
5023                   forEach(data, function(status, className) {
5024                     var hasClass = !!existing[className];
5025                     if (status !== hasClass) {
5026                       if (status) {
5027                         toAdd += (toAdd.length ? ' ' : '') + className;
5028                       } else {
5029                         toRemove += (toRemove.length ? ' ' : '') + className;
5030                       }
5031                     }
5032                   });
5033
5034                   forEach(element, function(elm) {
5035                     toAdd    && jqLiteAddClass(elm, toAdd);
5036                     toRemove && jqLiteRemoveClass(elm, toRemove);
5037                   });
5038                   postDigestQueue.remove(element);
5039                 }
5040               });
5041               postDigestElements.length = 0;
5042             }
5043
5044
5045             function addRemoveClassesPostDigest(element, add, remove) {
5046               var data = postDigestQueue.get(element) || {};
5047
5048               var classesAdded = updateData(data, add, true);
5049               var classesRemoved = updateData(data, remove, false);
5050
5051               if (classesAdded || classesRemoved) {
5052
5053                 postDigestQueue.put(element, data);
5054                 postDigestElements.push(element);
5055
5056                 if (postDigestElements.length === 1) {
5057                   $rootScope.$$postDigest(handleCSSClassChanges);
5058                 }
5059               }
5060             }
5061           }];
5062         };
5063
5064         /**
5065          * @ngdoc provider
5066          * @name $animateProvider
5067          *
5068          * @description
5069          * Default implementation of $animate that doesn't perform any animations, instead just
5070          * synchronously performs DOM updates and resolves the returned runner promise.
5071          *
5072          * In order to enable animations the `ngAnimate` module has to be loaded.
5073          *
5074          * To see the functional implementation check out `src/ngAnimate/animate.js`.
5075          */
5076         var $AnimateProvider = ['$provide', function($provide) {
5077           var provider = this;
5078
5079           this.$$registeredAnimations = Object.create(null);
5080
5081            /**
5082            * @ngdoc method
5083            * @name $animateProvider#register
5084            *
5085            * @description
5086            * Registers a new injectable animation factory function. The factory function produces the
5087            * animation object which contains callback functions for each event that is expected to be
5088            * animated.
5089            *
5090            *   * `eventFn`: `function(element, ... , doneFunction, options)`
5091            *   The element to animate, the `doneFunction` and the options fed into the animation. Depending
5092            *   on the type of animation additional arguments will be injected into the animation function. The
5093            *   list below explains the function signatures for the different animation methods:
5094            *
5095            *   - setClass: function(element, addedClasses, removedClasses, doneFunction, options)
5096            *   - addClass: function(element, addedClasses, doneFunction, options)
5097            *   - removeClass: function(element, removedClasses, doneFunction, options)
5098            *   - enter, leave, move: function(element, doneFunction, options)
5099            *   - animate: function(element, fromStyles, toStyles, doneFunction, options)
5100            *
5101            *   Make sure to trigger the `doneFunction` once the animation is fully complete.
5102            *
5103            * ```js
5104            *   return {
5105            *     //enter, leave, move signature
5106            *     eventFn : function(element, done, options) {
5107            *       //code to run the animation
5108            *       //once complete, then run done()
5109            *       return function endFunction(wasCancelled) {
5110            *         //code to cancel the animation
5111            *       }
5112            *     }
5113            *   }
5114            * ```
5115            *
5116            * @param {string} name The name of the animation (this is what the class-based CSS value will be compared to).
5117            * @param {Function} factory The factory function that will be executed to return the animation
5118            *                           object.
5119            */
5120           this.register = function(name, factory) {
5121             if (name && name.charAt(0) !== '.') {
5122               throw $animateMinErr('notcsel', "Expecting class selector starting with '.' got '{0}'.", name);
5123             }
5124
5125             var key = name + '-animation';
5126             provider.$$registeredAnimations[name.substr(1)] = key;
5127             $provide.factory(key, factory);
5128           };
5129
5130           /**
5131            * @ngdoc method
5132            * @name $animateProvider#classNameFilter
5133            *
5134            * @description
5135            * Sets and/or returns the CSS class regular expression that is checked when performing
5136            * an animation. Upon bootstrap the classNameFilter value is not set at all and will
5137            * therefore enable $animate to attempt to perform an animation on any element that is triggered.
5138            * When setting the `classNameFilter` value, animations will only be performed on elements
5139            * that successfully match the filter expression. This in turn can boost performance
5140            * for low-powered devices as well as applications containing a lot of structural operations.
5141            * @param {RegExp=} expression The className expression which will be checked against all animations
5142            * @return {RegExp} The current CSS className expression value. If null then there is no expression value
5143            */
5144           this.classNameFilter = function(expression) {
5145             if (arguments.length === 1) {
5146               this.$$classNameFilter = (expression instanceof RegExp) ? expression : null;
5147               if (this.$$classNameFilter) {
5148                 var reservedRegex = new RegExp("(\\s+|\\/)" + NG_ANIMATE_CLASSNAME + "(\\s+|\\/)");
5149                 if (reservedRegex.test(this.$$classNameFilter.toString())) {
5150                   throw $animateMinErr('nongcls','$animateProvider.classNameFilter(regex) prohibits accepting a regex value which matches/contains the "{0}" CSS class.', NG_ANIMATE_CLASSNAME);
5151
5152                 }
5153               }
5154             }
5155             return this.$$classNameFilter;
5156           };
5157
5158           this.$get = ['$$animateQueue', function($$animateQueue) {
5159             function domInsert(element, parentElement, afterElement) {
5160               // if for some reason the previous element was removed
5161               // from the dom sometime before this code runs then let's
5162               // just stick to using the parent element as the anchor
5163               if (afterElement) {
5164                 var afterNode = extractElementNode(afterElement);
5165                 if (afterNode && !afterNode.parentNode && !afterNode.previousElementSibling) {
5166                   afterElement = null;
5167                 }
5168               }
5169               afterElement ? afterElement.after(element) : parentElement.prepend(element);
5170             }
5171
5172             /**
5173              * @ngdoc service
5174              * @name $animate
5175              * @description The $animate service exposes a series of DOM utility methods that provide support
5176              * for animation hooks. The default behavior is the application of DOM operations, however,
5177              * when an animation is detected (and animations are enabled), $animate will do the heavy lifting
5178              * to ensure that animation runs with the triggered DOM operation.
5179              *
5180              * By default $animate doesn't trigger any animations. This is because the `ngAnimate` module isn't
5181              * included and only when it is active then the animation hooks that `$animate` triggers will be
5182              * functional. Once active then all structural `ng-` directives will trigger animations as they perform
5183              * their DOM-related operations (enter, leave and move). Other directives such as `ngClass`,
5184              * `ngShow`, `ngHide` and `ngMessages` also provide support for animations.
5185              *
5186              * It is recommended that the`$animate` service is always used when executing DOM-related procedures within directives.
5187              *
5188              * To learn more about enabling animation support, click here to visit the
5189              * {@link ngAnimate ngAnimate module page}.
5190              */
5191             return {
5192               // we don't call it directly since non-existant arguments may
5193               // be interpreted as null within the sub enabled function
5194
5195               /**
5196                *
5197                * @ngdoc method
5198                * @name $animate#on
5199                * @kind function
5200                * @description Sets up an event listener to fire whenever the animation event (enter, leave, move, etc...)
5201                *    has fired on the given element or among any of its children. Once the listener is fired, the provided callback
5202                *    is fired with the following params:
5203                *
5204                * ```js
5205                * $animate.on('enter', container,
5206                *    function callback(element, phase) {
5207                *      // cool we detected an enter animation within the container
5208                *    }
5209                * );
5210                * ```
5211                *
5212                * @param {string} event the animation event that will be captured (e.g. enter, leave, move, addClass, removeClass, etc...)
5213                * @param {DOMElement} container the container element that will capture each of the animation events that are fired on itself
5214                *     as well as among its children
5215                * @param {Function} callback the callback function that will be fired when the listener is triggered
5216                *
5217                * The arguments present in the callback function are:
5218                * * `element` - The captured DOM element that the animation was fired on.
5219                * * `phase` - The phase of the animation. The two possible phases are **start** (when the animation starts) and **close** (when it ends).
5220                */
5221               on: $$animateQueue.on,
5222
5223               /**
5224                *
5225                * @ngdoc method
5226                * @name $animate#off
5227                * @kind function
5228                * @description Deregisters an event listener based on the event which has been associated with the provided element. This method
5229                * can be used in three different ways depending on the arguments:
5230                *
5231                * ```js
5232                * // remove all the animation event listeners listening for `enter`
5233                * $animate.off('enter');
5234                *
5235                * // remove all the animation event listeners listening for `enter` on the given element and its children
5236                * $animate.off('enter', container);
5237                *
5238                * // remove the event listener function provided by `listenerFn` that is set
5239                * // to listen for `enter` on the given `element` as well as its children
5240                * $animate.off('enter', container, callback);
5241                * ```
5242                *
5243                * @param {string} event the animation event (e.g. enter, leave, move, addClass, removeClass, etc...)
5244                * @param {DOMElement=} container the container element the event listener was placed on
5245                * @param {Function=} callback the callback function that was registered as the listener
5246                */
5247               off: $$animateQueue.off,
5248
5249               /**
5250                * @ngdoc method
5251                * @name $animate#pin
5252                * @kind function
5253                * @description Associates the provided element with a host parent element to allow the element to be animated even if it exists
5254                *    outside of the DOM structure of the Angular application. By doing so, any animation triggered via `$animate` can be issued on the
5255                *    element despite being outside the realm of the application or within another application. Say for example if the application
5256                *    was bootstrapped on an element that is somewhere inside of the `<body>` tag, but we wanted to allow for an element to be situated
5257                *    as a direct child of `document.body`, then this can be achieved by pinning the element via `$animate.pin(element)`. Keep in mind
5258                *    that calling `$animate.pin(element, parentElement)` will not actually insert into the DOM anywhere; it will just create the association.
5259                *
5260                *    Note that this feature is only active when the `ngAnimate` module is used.
5261                *
5262                * @param {DOMElement} element the external element that will be pinned
5263                * @param {DOMElement} parentElement the host parent element that will be associated with the external element
5264                */
5265               pin: $$animateQueue.pin,
5266
5267               /**
5268                *
5269                * @ngdoc method
5270                * @name $animate#enabled
5271                * @kind function
5272                * @description Used to get and set whether animations are enabled or not on the entire application or on an element and its children. This
5273                * function can be called in four ways:
5274                *
5275                * ```js
5276                * // returns true or false
5277                * $animate.enabled();
5278                *
5279                * // changes the enabled state for all animations
5280                * $animate.enabled(false);
5281                * $animate.enabled(true);
5282                *
5283                * // returns true or false if animations are enabled for an element
5284                * $animate.enabled(element);
5285                *
5286                * // changes the enabled state for an element and its children
5287                * $animate.enabled(element, true);
5288                * $animate.enabled(element, false);
5289                * ```
5290                *
5291                * @param {DOMElement=} element the element that will be considered for checking/setting the enabled state
5292                * @param {boolean=} enabled whether or not the animations will be enabled for the element
5293                *
5294                * @return {boolean} whether or not animations are enabled
5295                */
5296               enabled: $$animateQueue.enabled,
5297
5298               /**
5299                * @ngdoc method
5300                * @name $animate#cancel
5301                * @kind function
5302                * @description Cancels the provided animation.
5303                *
5304                * @param {Promise} animationPromise The animation promise that is returned when an animation is started.
5305                */
5306               cancel: function(runner) {
5307                 runner.end && runner.end();
5308               },
5309
5310               /**
5311                *
5312                * @ngdoc method
5313                * @name $animate#enter
5314                * @kind function
5315                * @description Inserts the element into the DOM either after the `after` element (if provided) or
5316                *   as the first child within the `parent` element and then triggers an animation.
5317                *   A promise is returned that will be resolved during the next digest once the animation
5318                *   has completed.
5319                *
5320                * @param {DOMElement} element the element which will be inserted into the DOM
5321                * @param {DOMElement} parent the parent element which will append the element as
5322                *   a child (so long as the after element is not present)
5323                * @param {DOMElement=} after the sibling element after which the element will be appended
5324                * @param {object=} options an optional collection of options/styles that will be applied to the element
5325                *
5326                * @return {Promise} the animation callback promise
5327                */
5328               enter: function(element, parent, after, options) {
5329                 parent = parent && jqLite(parent);
5330                 after = after && jqLite(after);
5331                 parent = parent || after.parent();
5332                 domInsert(element, parent, after);
5333                 return $$animateQueue.push(element, 'enter', prepareAnimateOptions(options));
5334               },
5335
5336               /**
5337                *
5338                * @ngdoc method
5339                * @name $animate#move
5340                * @kind function
5341                * @description Inserts (moves) the element into its new position in the DOM either after
5342                *   the `after` element (if provided) or as the first child within the `parent` element
5343                *   and then triggers an animation. A promise is returned that will be resolved
5344                *   during the next digest once the animation has completed.
5345                *
5346                * @param {DOMElement} element the element which will be moved into the new DOM position
5347                * @param {DOMElement} parent the parent element which will append the element as
5348                *   a child (so long as the after element is not present)
5349                * @param {DOMElement=} after the sibling element after which the element will be appended
5350                * @param {object=} options an optional collection of options/styles that will be applied to the element
5351                *
5352                * @return {Promise} the animation callback promise
5353                */
5354               move: function(element, parent, after, options) {
5355                 parent = parent && jqLite(parent);
5356                 after = after && jqLite(after);
5357                 parent = parent || after.parent();
5358                 domInsert(element, parent, after);
5359                 return $$animateQueue.push(element, 'move', prepareAnimateOptions(options));
5360               },
5361
5362               /**
5363                * @ngdoc method
5364                * @name $animate#leave
5365                * @kind function
5366                * @description Triggers an animation and then removes the element from the DOM.
5367                * When the function is called a promise is returned that will be resolved during the next
5368                * digest once the animation has completed.
5369                *
5370                * @param {DOMElement} element the element which will be removed from the DOM
5371                * @param {object=} options an optional collection of options/styles that will be applied to the element
5372                *
5373                * @return {Promise} the animation callback promise
5374                */
5375               leave: function(element, options) {
5376                 return $$animateQueue.push(element, 'leave', prepareAnimateOptions(options), function() {
5377                   element.remove();
5378                 });
5379               },
5380
5381               /**
5382                * @ngdoc method
5383                * @name $animate#addClass
5384                * @kind function
5385                *
5386                * @description Triggers an addClass animation surrounding the addition of the provided CSS class(es). Upon
5387                *   execution, the addClass operation will only be handled after the next digest and it will not trigger an
5388                *   animation if element already contains the CSS class or if the class is removed at a later step.
5389                *   Note that class-based animations are treated differently compared to structural animations
5390                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5391                *   depending if CSS or JavaScript animations are used.
5392                *
5393                * @param {DOMElement} element the element which the CSS classes will be applied to
5394                * @param {string} className the CSS class(es) that will be added (multiple classes are separated via spaces)
5395                * @param {object=} options an optional collection of options/styles that will be applied to the element
5396                *
5397                * @return {Promise} the animation callback promise
5398                */
5399               addClass: function(element, className, options) {
5400                 options = prepareAnimateOptions(options);
5401                 options.addClass = mergeClasses(options.addclass, className);
5402                 return $$animateQueue.push(element, 'addClass', options);
5403               },
5404
5405               /**
5406                * @ngdoc method
5407                * @name $animate#removeClass
5408                * @kind function
5409                *
5410                * @description Triggers a removeClass animation surrounding the removal of the provided CSS class(es). Upon
5411                *   execution, the removeClass operation will only be handled after the next digest and it will not trigger an
5412                *   animation if element does not contain the CSS class or if the class is added at a later step.
5413                *   Note that class-based animations are treated differently compared to structural animations
5414                *   (like enter, move and leave) since the CSS classes may be added/removed at different points
5415                *   depending if CSS or JavaScript animations are used.
5416                *
5417                * @param {DOMElement} element the element which the CSS classes will be applied to
5418                * @param {string} className the CSS class(es) that will be removed (multiple classes are separated via spaces)
5419                * @param {object=} options an optional collection of options/styles that will be applied to the element
5420                *
5421                * @return {Promise} the animation callback promise
5422                */
5423               removeClass: function(element, className, options) {
5424                 options = prepareAnimateOptions(options);
5425                 options.removeClass = mergeClasses(options.removeClass, className);
5426                 return $$animateQueue.push(element, 'removeClass', options);
5427               },
5428
5429               /**
5430                * @ngdoc method
5431                * @name $animate#setClass
5432                * @kind function
5433                *
5434                * @description Performs both the addition and removal of a CSS classes on an element and (during the process)
5435                *    triggers an animation surrounding the class addition/removal. Much like `$animate.addClass` and
5436                *    `$animate.removeClass`, `setClass` will only evaluate the classes being added/removed once a digest has
5437                *    passed. Note that class-based animations are treated differently compared to structural animations
5438                *    (like enter, move and leave) since the CSS classes may be added/removed at different points
5439                *    depending if CSS or JavaScript animations are used.
5440                *
5441                * @param {DOMElement} element the element which the CSS classes will be applied to
5442                * @param {string} add the CSS class(es) that will be added (multiple classes are separated via spaces)
5443                * @param {string} remove the CSS class(es) that will be removed (multiple classes are separated via spaces)
5444                * @param {object=} options an optional collection of options/styles that will be applied to the element
5445                *
5446                * @return {Promise} the animation callback promise
5447                */
5448               setClass: function(element, add, remove, options) {
5449                 options = prepareAnimateOptions(options);
5450                 options.addClass = mergeClasses(options.addClass, add);
5451                 options.removeClass = mergeClasses(options.removeClass, remove);
5452                 return $$animateQueue.push(element, 'setClass', options);
5453               },
5454
5455               /**
5456                * @ngdoc method
5457                * @name $animate#animate
5458                * @kind function
5459                *
5460                * @description Performs an inline animation on the element which applies the provided to and from CSS styles to the element.
5461                * If any detected CSS transition, keyframe or JavaScript matches the provided className value then the animation will take
5462                * on the provided styles. For example, if a transition animation is set for the given className then the provided from and
5463                * to styles will be applied alongside the given transition. If a JavaScript animation is detected then the provided styles
5464                * will be given in as function paramters into the `animate` method (or as apart of the `options` parameter).
5465                *
5466                * @param {DOMElement} element the element which the CSS styles will be applied to
5467                * @param {object} from the from (starting) CSS styles that will be applied to the element and across the animation.
5468                * @param {object} to the to (destination) CSS styles that will be applied to the element and across the animation.
5469                * @param {string=} className an optional CSS class that will be applied to the element for the duration of the animation. If
5470                *    this value is left as empty then a CSS class of `ng-inline-animate` will be applied to the element.
5471                *    (Note that if no animation is detected then this value will not be appplied to the element.)
5472                * @param {object=} options an optional collection of options/styles that will be applied to the element
5473                *
5474                * @return {Promise} the animation callback promise
5475                */
5476               animate: function(element, from, to, className, options) {
5477                 options = prepareAnimateOptions(options);
5478                 options.from = options.from ? extend(options.from, from) : from;
5479                 options.to   = options.to   ? extend(options.to, to)     : to;
5480
5481                 className = className || 'ng-inline-animate';
5482                 options.tempClasses = mergeClasses(options.tempClasses, className);
5483                 return $$animateQueue.push(element, 'animate', options);
5484               }
5485             };
5486           }];
5487         }];
5488
5489         /**
5490          * @ngdoc service
5491          * @name $animateCss
5492          * @kind object
5493          *
5494          * @description
5495          * This is the core version of `$animateCss`. By default, only when the `ngAnimate` is included,
5496          * then the `$animateCss` service will actually perform animations.
5497          *
5498          * Click here {@link ngAnimate.$animateCss to read the documentation for $animateCss}.
5499          */
5500         var $CoreAnimateCssProvider = function() {
5501           this.$get = ['$$rAF', '$q', function($$rAF, $q) {
5502
5503             var RAFPromise = function() {};
5504             RAFPromise.prototype = {
5505               done: function(cancel) {
5506                 this.defer && this.defer[cancel === true ? 'reject' : 'resolve']();
5507               },
5508               end: function() {
5509                 this.done();
5510               },
5511               cancel: function() {
5512                 this.done(true);
5513               },
5514               getPromise: function() {
5515                 if (!this.defer) {
5516                   this.defer = $q.defer();
5517                 }
5518                 return this.defer.promise;
5519               },
5520               then: function(f1,f2) {
5521                 return this.getPromise().then(f1,f2);
5522               },
5523               'catch': function(f1) {
5524                 return this.getPromise()['catch'](f1);
5525               },
5526               'finally': function(f1) {
5527                 return this.getPromise()['finally'](f1);
5528               }
5529             };
5530
5531             return function(element, options) {
5532               // there is no point in applying the styles since
5533               // there is no animation that goes on at all in
5534               // this version of $animateCss.
5535               if (options.cleanupStyles) {
5536                 options.from = options.to = null;
5537               }
5538
5539               if (options.from) {
5540                 element.css(options.from);
5541                 options.from = null;
5542               }
5543
5544               var closed, runner = new RAFPromise();
5545               return {
5546                 start: run,
5547                 end: run
5548               };
5549
5550               function run() {
5551                 $$rAF(function() {
5552                   close();
5553                   if (!closed) {
5554                     runner.done();
5555                   }
5556                   closed = true;
5557                 });
5558                 return runner;
5559               }
5560
5561               function close() {
5562                 if (options.addClass) {
5563                   element.addClass(options.addClass);
5564                   options.addClass = null;
5565                 }
5566                 if (options.removeClass) {
5567                   element.removeClass(options.removeClass);
5568                   options.removeClass = null;
5569                 }
5570                 if (options.to) {
5571                   element.css(options.to);
5572                   options.to = null;
5573                 }
5574               }
5575             };
5576           }];
5577         };
5578
5579         /* global stripHash: true */
5580
5581         /**
5582          * ! This is a private undocumented service !
5583          *
5584          * @name $browser
5585          * @requires $log
5586          * @description
5587          * This object has two goals:
5588          *
5589          * - hide all the global state in the browser caused by the window object
5590          * - abstract away all the browser specific features and inconsistencies
5591          *
5592          * For tests we provide {@link ngMock.$browser mock implementation} of the `$browser`
5593          * service, which can be used for convenient testing of the application without the interaction with
5594          * the real browser apis.
5595          */
5596         /**
5597          * @param {object} window The global window object.
5598          * @param {object} document jQuery wrapped document.
5599          * @param {object} $log window.console or an object with the same interface.
5600          * @param {object} $sniffer $sniffer service
5601          */
5602         function Browser(window, document, $log, $sniffer) {
5603           var self = this,
5604               rawDocument = document[0],
5605               location = window.location,
5606               history = window.history,
5607               setTimeout = window.setTimeout,
5608               clearTimeout = window.clearTimeout,
5609               pendingDeferIds = {};
5610
5611           self.isMock = false;
5612
5613           var outstandingRequestCount = 0;
5614           var outstandingRequestCallbacks = [];
5615
5616           // TODO(vojta): remove this temporary api
5617           self.$$completeOutstandingRequest = completeOutstandingRequest;
5618           self.$$incOutstandingRequestCount = function() { outstandingRequestCount++; };
5619
5620           /**
5621            * Executes the `fn` function(supports currying) and decrements the `outstandingRequestCallbacks`
5622            * counter. If the counter reaches 0, all the `outstandingRequestCallbacks` are executed.
5623            */
5624           function completeOutstandingRequest(fn) {
5625             try {
5626               fn.apply(null, sliceArgs(arguments, 1));
5627             } finally {
5628               outstandingRequestCount--;
5629               if (outstandingRequestCount === 0) {
5630                 while (outstandingRequestCallbacks.length) {
5631                   try {
5632                     outstandingRequestCallbacks.pop()();
5633                   } catch (e) {
5634                     $log.error(e);
5635                   }
5636                 }
5637               }
5638             }
5639           }
5640
5641           function getHash(url) {
5642             var index = url.indexOf('#');
5643             return index === -1 ? '' : url.substr(index);
5644           }
5645
5646           /**
5647            * @private
5648            * Note: this method is used only by scenario runner
5649            * TODO(vojta): prefix this method with $$ ?
5650            * @param {function()} callback Function that will be called when no outstanding request
5651            */
5652           self.notifyWhenNoOutstandingRequests = function(callback) {
5653             if (outstandingRequestCount === 0) {
5654               callback();
5655             } else {
5656               outstandingRequestCallbacks.push(callback);
5657             }
5658           };
5659
5660           //////////////////////////////////////////////////////////////
5661           // URL API
5662           //////////////////////////////////////////////////////////////
5663
5664           var cachedState, lastHistoryState,
5665               lastBrowserUrl = location.href,
5666               baseElement = document.find('base'),
5667               pendingLocation = null;
5668
5669           cacheState();
5670           lastHistoryState = cachedState;
5671
5672           /**
5673            * @name $browser#url
5674            *
5675            * @description
5676            * GETTER:
5677            * Without any argument, this method just returns current value of location.href.
5678            *
5679            * SETTER:
5680            * With at least one argument, this method sets url to new value.
5681            * If html5 history api supported, pushState/replaceState is used, otherwise
5682            * location.href/location.replace is used.
5683            * Returns its own instance to allow chaining
5684            *
5685            * NOTE: this api is intended for use only by the $location service. Please use the
5686            * {@link ng.$location $location service} to change url.
5687            *
5688            * @param {string} url New url (when used as setter)
5689            * @param {boolean=} replace Should new url replace current history record?
5690            * @param {object=} state object to use with pushState/replaceState
5691            */
5692           self.url = function(url, replace, state) {
5693             // In modern browsers `history.state` is `null` by default; treating it separately
5694             // from `undefined` would cause `$browser.url('/foo')` to change `history.state`
5695             // to undefined via `pushState`. Instead, let's change `undefined` to `null` here.
5696             if (isUndefined(state)) {
5697               state = null;
5698             }
5699
5700             // Android Browser BFCache causes location, history reference to become stale.
5701             if (location !== window.location) location = window.location;
5702             if (history !== window.history) history = window.history;
5703
5704             // setter
5705             if (url) {
5706               var sameState = lastHistoryState === state;
5707
5708               // Don't change anything if previous and current URLs and states match. This also prevents
5709               // IE<10 from getting into redirect loop when in LocationHashbangInHtml5Url mode.
5710               // See https://github.com/angular/angular.js/commit/ffb2701
5711               if (lastBrowserUrl === url && (!$sniffer.history || sameState)) {
5712                 return self;
5713               }
5714               var sameBase = lastBrowserUrl && stripHash(lastBrowserUrl) === stripHash(url);
5715               lastBrowserUrl = url;
5716               lastHistoryState = state;
5717               // Don't use history API if only the hash changed
5718               // due to a bug in IE10/IE11 which leads
5719               // to not firing a `hashchange` nor `popstate` event
5720               // in some cases (see #9143).
5721               if ($sniffer.history && (!sameBase || !sameState)) {
5722                 history[replace ? 'replaceState' : 'pushState'](state, '', url);
5723                 cacheState();
5724                 // Do the assignment again so that those two variables are referentially identical.
5725                 lastHistoryState = cachedState;
5726               } else {
5727                 if (!sameBase || pendingLocation) {
5728                   pendingLocation = url;
5729                 }
5730                 if (replace) {
5731                   location.replace(url);
5732                 } else if (!sameBase) {
5733                   location.href = url;
5734                 } else {
5735                   location.hash = getHash(url);
5736                 }
5737                 if (location.href !== url) {
5738                   pendingLocation = url;
5739                 }
5740               }
5741               return self;
5742             // getter
5743             } else {
5744               // - pendingLocation is needed as browsers don't allow to read out
5745               //   the new location.href if a reload happened or if there is a bug like in iOS 9 (see
5746               //   https://openradar.appspot.com/22186109).
5747               // - the replacement is a workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=407172
5748               return pendingLocation || location.href.replace(/%27/g,"'");
5749             }
5750           };
5751
5752           /**
5753            * @name $browser#state
5754            *
5755            * @description
5756            * This method is a getter.
5757            *
5758            * Return history.state or null if history.state is undefined.
5759            *
5760            * @returns {object} state
5761            */
5762           self.state = function() {
5763             return cachedState;
5764           };
5765
5766           var urlChangeListeners = [],
5767               urlChangeInit = false;
5768
5769           function cacheStateAndFireUrlChange() {
5770             pendingLocation = null;
5771             cacheState();
5772             fireUrlChange();
5773           }
5774
5775           function getCurrentState() {
5776             try {
5777               return history.state;
5778             } catch (e) {
5779               // MSIE can reportedly throw when there is no state (UNCONFIRMED).
5780             }
5781           }
5782
5783           // This variable should be used *only* inside the cacheState function.
5784           var lastCachedState = null;
5785           function cacheState() {
5786             // This should be the only place in $browser where `history.state` is read.
5787             cachedState = getCurrentState();
5788             cachedState = isUndefined(cachedState) ? null : cachedState;
5789
5790             // Prevent callbacks fo fire twice if both hashchange & popstate were fired.
5791             if (equals(cachedState, lastCachedState)) {
5792               cachedState = lastCachedState;
5793             }
5794             lastCachedState = cachedState;
5795           }
5796
5797           function fireUrlChange() {
5798             if (lastBrowserUrl === self.url() && lastHistoryState === cachedState) {
5799               return;
5800             }
5801
5802             lastBrowserUrl = self.url();
5803             lastHistoryState = cachedState;
5804             forEach(urlChangeListeners, function(listener) {
5805               listener(self.url(), cachedState);
5806             });
5807           }
5808
5809           /**
5810            * @name $browser#onUrlChange
5811            *
5812            * @description
5813            * Register callback function that will be called, when url changes.
5814            *
5815            * It's only called when the url is changed from outside of angular:
5816            * - user types different url into address bar
5817            * - user clicks on history (forward/back) button
5818            * - user clicks on a link
5819            *
5820            * It's not called when url is changed by $browser.url() method
5821            *
5822            * The listener gets called with new url as parameter.
5823            *
5824            * NOTE: this api is intended for use only by the $location service. Please use the
5825            * {@link ng.$location $location service} to monitor url changes in angular apps.
5826            *
5827            * @param {function(string)} listener Listener function to be called when url changes.
5828            * @return {function(string)} Returns the registered listener fn - handy if the fn is anonymous.
5829            */
5830           self.onUrlChange = function(callback) {
5831             // TODO(vojta): refactor to use node's syntax for events
5832             if (!urlChangeInit) {
5833               // We listen on both (hashchange/popstate) when available, as some browsers (e.g. Opera)
5834               // don't fire popstate when user change the address bar and don't fire hashchange when url
5835               // changed by push/replaceState
5836
5837               // html5 history api - popstate event
5838               if ($sniffer.history) jqLite(window).on('popstate', cacheStateAndFireUrlChange);
5839               // hashchange event
5840               jqLite(window).on('hashchange', cacheStateAndFireUrlChange);
5841
5842               urlChangeInit = true;
5843             }
5844
5845             urlChangeListeners.push(callback);
5846             return callback;
5847           };
5848
5849           /**
5850            * @private
5851            * Remove popstate and hashchange handler from window.
5852            *
5853            * NOTE: this api is intended for use only by $rootScope.
5854            */
5855           self.$$applicationDestroyed = function() {
5856             jqLite(window).off('hashchange popstate', cacheStateAndFireUrlChange);
5857           };
5858
5859           /**
5860            * Checks whether the url has changed outside of Angular.
5861            * Needs to be exported to be able to check for changes that have been done in sync,
5862            * as hashchange/popstate events fire in async.
5863            */
5864           self.$$checkUrlChange = fireUrlChange;
5865
5866           //////////////////////////////////////////////////////////////
5867           // Misc API
5868           //////////////////////////////////////////////////////////////
5869
5870           /**
5871            * @name $browser#baseHref
5872            *
5873            * @description
5874            * Returns current <base href>
5875            * (always relative - without domain)
5876            *
5877            * @returns {string} The current base href
5878            */
5879           self.baseHref = function() {
5880             var href = baseElement.attr('href');
5881             return href ? href.replace(/^(https?\:)?\/\/[^\/]*/, '') : '';
5882           };
5883
5884           /**
5885            * @name $browser#defer
5886            * @param {function()} fn A function, who's execution should be deferred.
5887            * @param {number=} [delay=0] of milliseconds to defer the function execution.
5888            * @returns {*} DeferId that can be used to cancel the task via `$browser.defer.cancel()`.
5889            *
5890            * @description
5891            * Executes a fn asynchronously via `setTimeout(fn, delay)`.
5892            *
5893            * Unlike when calling `setTimeout` directly, in test this function is mocked and instead of using
5894            * `setTimeout` in tests, the fns are queued in an array, which can be programmatically flushed
5895            * via `$browser.defer.flush()`.
5896            *
5897            */
5898           self.defer = function(fn, delay) {
5899             var timeoutId;
5900             outstandingRequestCount++;
5901             timeoutId = setTimeout(function() {
5902               delete pendingDeferIds[timeoutId];
5903               completeOutstandingRequest(fn);
5904             }, delay || 0);
5905             pendingDeferIds[timeoutId] = true;
5906             return timeoutId;
5907           };
5908
5909
5910           /**
5911            * @name $browser#defer.cancel
5912            *
5913            * @description
5914            * Cancels a deferred task identified with `deferId`.
5915            *
5916            * @param {*} deferId Token returned by the `$browser.defer` function.
5917            * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
5918            *                    canceled.
5919            */
5920           self.defer.cancel = function(deferId) {
5921             if (pendingDeferIds[deferId]) {
5922               delete pendingDeferIds[deferId];
5923               clearTimeout(deferId);
5924               completeOutstandingRequest(noop);
5925               return true;
5926             }
5927             return false;
5928           };
5929
5930         }
5931
5932         function $BrowserProvider() {
5933           this.$get = ['$window', '$log', '$sniffer', '$document',
5934               function($window, $log, $sniffer, $document) {
5935                 return new Browser($window, $document, $log, $sniffer);
5936               }];
5937         }
5938
5939         /**
5940          * @ngdoc service
5941          * @name $cacheFactory
5942          *
5943          * @description
5944          * Factory that constructs {@link $cacheFactory.Cache Cache} objects and gives access to
5945          * them.
5946          *
5947          * ```js
5948          *
5949          *  var cache = $cacheFactory('cacheId');
5950          *  expect($cacheFactory.get('cacheId')).toBe(cache);
5951          *  expect($cacheFactory.get('noSuchCacheId')).not.toBeDefined();
5952          *
5953          *  cache.put("key", "value");
5954          *  cache.put("another key", "another value");
5955          *
5956          *  // We've specified no options on creation
5957          *  expect(cache.info()).toEqual({id: 'cacheId', size: 2});
5958          *
5959          * ```
5960          *
5961          *
5962          * @param {string} cacheId Name or id of the newly created cache.
5963          * @param {object=} options Options object that specifies the cache behavior. Properties:
5964          *
5965          *   - `{number=}` `capacity` — turns the cache into LRU cache.
5966          *
5967          * @returns {object} Newly created cache object with the following set of methods:
5968          *
5969          * - `{object}` `info()` — Returns id, size, and options of cache.
5970          * - `{{*}}` `put({string} key, {*} value)` — Puts a new key-value pair into the cache and returns
5971          *   it.
5972          * - `{{*}}` `get({string} key)` — Returns cached value for `key` or undefined for cache miss.
5973          * - `{void}` `remove({string} key)` — Removes a key-value pair from the cache.
5974          * - `{void}` `removeAll()` — Removes all cached values.
5975          * - `{void}` `destroy()` — Removes references to this cache from $cacheFactory.
5976          *
5977          * @example
5978            <example module="cacheExampleApp">
5979              <file name="index.html">
5980                <div ng-controller="CacheController">
5981                  <input ng-model="newCacheKey" placeholder="Key">
5982                  <input ng-model="newCacheValue" placeholder="Value">
5983                  <button ng-click="put(newCacheKey, newCacheValue)">Cache</button>
5984
5985                  <p ng-if="keys.length">Cached Values</p>
5986                  <div ng-repeat="key in keys">
5987                    <span ng-bind="key"></span>
5988                    <span>: </span>
5989                    <b ng-bind="cache.get(key)"></b>
5990                  </div>
5991
5992                  <p>Cache Info</p>
5993                  <div ng-repeat="(key, value) in cache.info()">
5994                    <span ng-bind="key"></span>
5995                    <span>: </span>
5996                    <b ng-bind="value"></b>
5997                  </div>
5998                </div>
5999              </file>
6000              <file name="script.js">
6001                angular.module('cacheExampleApp', []).
6002                  controller('CacheController', ['$scope', '$cacheFactory', function($scope, $cacheFactory) {
6003                    $scope.keys = [];
6004                    $scope.cache = $cacheFactory('cacheId');
6005                    $scope.put = function(key, value) {
6006                      if (angular.isUndefined($scope.cache.get(key))) {
6007                        $scope.keys.push(key);
6008                      }
6009                      $scope.cache.put(key, angular.isUndefined(value) ? null : value);
6010                    };
6011                  }]);
6012              </file>
6013              <file name="style.css">
6014                p {
6015                  margin: 10px 0 3px;
6016                }
6017              </file>
6018            </example>
6019          */
6020         function $CacheFactoryProvider() {
6021
6022           this.$get = function() {
6023             var caches = {};
6024
6025             function cacheFactory(cacheId, options) {
6026               if (cacheId in caches) {
6027                 throw minErr('$cacheFactory')('iid', "CacheId '{0}' is already taken!", cacheId);
6028               }
6029
6030               var size = 0,
6031                   stats = extend({}, options, {id: cacheId}),
6032                   data = createMap(),
6033                   capacity = (options && options.capacity) || Number.MAX_VALUE,
6034                   lruHash = createMap(),
6035                   freshEnd = null,
6036                   staleEnd = null;
6037
6038               /**
6039                * @ngdoc type
6040                * @name $cacheFactory.Cache
6041                *
6042                * @description
6043                * A cache object used to store and retrieve data, primarily used by
6044                * {@link $http $http} and the {@link ng.directive:script script} directive to cache
6045                * templates and other data.
6046                *
6047                * ```js
6048                *  angular.module('superCache')
6049                *    .factory('superCache', ['$cacheFactory', function($cacheFactory) {
6050                *      return $cacheFactory('super-cache');
6051                *    }]);
6052                * ```
6053                *
6054                * Example test:
6055                *
6056                * ```js
6057                *  it('should behave like a cache', inject(function(superCache) {
6058                *    superCache.put('key', 'value');
6059                *    superCache.put('another key', 'another value');
6060                *
6061                *    expect(superCache.info()).toEqual({
6062                *      id: 'super-cache',
6063                *      size: 2
6064                *    });
6065                *
6066                *    superCache.remove('another key');
6067                *    expect(superCache.get('another key')).toBeUndefined();
6068                *
6069                *    superCache.removeAll();
6070                *    expect(superCache.info()).toEqual({
6071                *      id: 'super-cache',
6072                *      size: 0
6073                *    });
6074                *  }));
6075                * ```
6076                */
6077               return caches[cacheId] = {
6078
6079                 /**
6080                  * @ngdoc method
6081                  * @name $cacheFactory.Cache#put
6082                  * @kind function
6083                  *
6084                  * @description
6085                  * Inserts a named entry into the {@link $cacheFactory.Cache Cache} object to be
6086                  * retrieved later, and incrementing the size of the cache if the key was not already
6087                  * present in the cache. If behaving like an LRU cache, it will also remove stale
6088                  * entries from the set.
6089                  *
6090                  * It will not insert undefined values into the cache.
6091                  *
6092                  * @param {string} key the key under which the cached data is stored.
6093                  * @param {*} value the value to store alongside the key. If it is undefined, the key
6094                  *    will not be stored.
6095                  * @returns {*} the value stored.
6096                  */
6097                 put: function(key, value) {
6098                   if (isUndefined(value)) return;
6099                   if (capacity < Number.MAX_VALUE) {
6100                     var lruEntry = lruHash[key] || (lruHash[key] = {key: key});
6101
6102                     refresh(lruEntry);
6103                   }
6104
6105                   if (!(key in data)) size++;
6106                   data[key] = value;
6107
6108                   if (size > capacity) {
6109                     this.remove(staleEnd.key);
6110                   }
6111
6112                   return value;
6113                 },
6114
6115                 /**
6116                  * @ngdoc method
6117                  * @name $cacheFactory.Cache#get
6118                  * @kind function
6119                  *
6120                  * @description
6121                  * Retrieves named data stored in the {@link $cacheFactory.Cache Cache} object.
6122                  *
6123                  * @param {string} key the key of the data to be retrieved
6124                  * @returns {*} the value stored.
6125                  */
6126                 get: function(key) {
6127                   if (capacity < Number.MAX_VALUE) {
6128                     var lruEntry = lruHash[key];
6129
6130                     if (!lruEntry) return;
6131
6132                     refresh(lruEntry);
6133                   }
6134
6135                   return data[key];
6136                 },
6137
6138
6139                 /**
6140                  * @ngdoc method
6141                  * @name $cacheFactory.Cache#remove
6142                  * @kind function
6143                  *
6144                  * @description
6145                  * Removes an entry from the {@link $cacheFactory.Cache Cache} object.
6146                  *
6147                  * @param {string} key the key of the entry to be removed
6148                  */
6149                 remove: function(key) {
6150                   if (capacity < Number.MAX_VALUE) {
6151                     var lruEntry = lruHash[key];
6152
6153                     if (!lruEntry) return;
6154
6155                     if (lruEntry == freshEnd) freshEnd = lruEntry.p;
6156                     if (lruEntry == staleEnd) staleEnd = lruEntry.n;
6157                     link(lruEntry.n,lruEntry.p);
6158
6159                     delete lruHash[key];
6160                   }
6161
6162                   if (!(key in data)) return;
6163
6164                   delete data[key];
6165                   size--;
6166                 },
6167
6168
6169                 /**
6170                  * @ngdoc method
6171                  * @name $cacheFactory.Cache#removeAll
6172                  * @kind function
6173                  *
6174                  * @description
6175                  * Clears the cache object of any entries.
6176                  */
6177                 removeAll: function() {
6178                   data = createMap();
6179                   size = 0;
6180                   lruHash = createMap();
6181                   freshEnd = staleEnd = null;
6182                 },
6183
6184
6185                 /**
6186                  * @ngdoc method
6187                  * @name $cacheFactory.Cache#destroy
6188                  * @kind function
6189                  *
6190                  * @description
6191                  * Destroys the {@link $cacheFactory.Cache Cache} object entirely,
6192                  * removing it from the {@link $cacheFactory $cacheFactory} set.
6193                  */
6194                 destroy: function() {
6195                   data = null;
6196                   stats = null;
6197                   lruHash = null;
6198                   delete caches[cacheId];
6199                 },
6200
6201
6202                 /**
6203                  * @ngdoc method
6204                  * @name $cacheFactory.Cache#info
6205                  * @kind function
6206                  *
6207                  * @description
6208                  * Retrieve information regarding a particular {@link $cacheFactory.Cache Cache}.
6209                  *
6210                  * @returns {object} an object with the following properties:
6211                  *   <ul>
6212                  *     <li>**id**: the id of the cache instance</li>
6213                  *     <li>**size**: the number of entries kept in the cache instance</li>
6214                  *     <li>**...**: any additional properties from the options object when creating the
6215                  *       cache.</li>
6216                  *   </ul>
6217                  */
6218                 info: function() {
6219                   return extend({}, stats, {size: size});
6220                 }
6221               };
6222
6223
6224               /**
6225                * makes the `entry` the freshEnd of the LRU linked list
6226                */
6227               function refresh(entry) {
6228                 if (entry != freshEnd) {
6229                   if (!staleEnd) {
6230                     staleEnd = entry;
6231                   } else if (staleEnd == entry) {
6232                     staleEnd = entry.n;
6233                   }
6234
6235                   link(entry.n, entry.p);
6236                   link(entry, freshEnd);
6237                   freshEnd = entry;
6238                   freshEnd.n = null;
6239                 }
6240               }
6241
6242
6243               /**
6244                * bidirectionally links two entries of the LRU linked list
6245                */
6246               function link(nextEntry, prevEntry) {
6247                 if (nextEntry != prevEntry) {
6248                   if (nextEntry) nextEntry.p = prevEntry; //p stands for previous, 'prev' didn't minify
6249                   if (prevEntry) prevEntry.n = nextEntry; //n stands for next, 'next' didn't minify
6250                 }
6251               }
6252             }
6253
6254
6255           /**
6256            * @ngdoc method
6257            * @name $cacheFactory#info
6258            *
6259            * @description
6260            * Get information about all the caches that have been created
6261            *
6262            * @returns {Object} - key-value map of `cacheId` to the result of calling `cache#info`
6263            */
6264             cacheFactory.info = function() {
6265               var info = {};
6266               forEach(caches, function(cache, cacheId) {
6267                 info[cacheId] = cache.info();
6268               });
6269               return info;
6270             };
6271
6272
6273           /**
6274            * @ngdoc method
6275            * @name $cacheFactory#get
6276            *
6277            * @description
6278            * Get access to a cache object by the `cacheId` used when it was created.
6279            *
6280            * @param {string} cacheId Name or id of a cache to access.
6281            * @returns {object} Cache object identified by the cacheId or undefined if no such cache.
6282            */
6283             cacheFactory.get = function(cacheId) {
6284               return caches[cacheId];
6285             };
6286
6287
6288             return cacheFactory;
6289           };
6290         }
6291
6292         /**
6293          * @ngdoc service
6294          * @name $templateCache
6295          *
6296          * @description
6297          * The first time a template is used, it is loaded in the template cache for quick retrieval. You
6298          * can load templates directly into the cache in a `script` tag, or by consuming the
6299          * `$templateCache` service directly.
6300          *
6301          * Adding via the `script` tag:
6302          *
6303          * ```html
6304          *   <script type="text/ng-template" id="templateId.html">
6305          *     <p>This is the content of the template</p>
6306          *   </script>
6307          * ```
6308          *
6309          * **Note:** the `script` tag containing the template does not need to be included in the `head` of
6310          * the document, but it must be a descendent of the {@link ng.$rootElement $rootElement} (IE,
6311          * element with ng-app attribute), otherwise the template will be ignored.
6312          *
6313          * Adding via the `$templateCache` service:
6314          *
6315          * ```js
6316          * var myApp = angular.module('myApp', []);
6317          * myApp.run(function($templateCache) {
6318          *   $templateCache.put('templateId.html', 'This is the content of the template');
6319          * });
6320          * ```
6321          *
6322          * To retrieve the template later, simply use it in your HTML:
6323          * ```html
6324          * <div ng-include=" 'templateId.html' "></div>
6325          * ```
6326          *
6327          * or get it via Javascript:
6328          * ```js
6329          * $templateCache.get('templateId.html')
6330          * ```
6331          *
6332          * See {@link ng.$cacheFactory $cacheFactory}.
6333          *
6334          */
6335         function $TemplateCacheProvider() {
6336           this.$get = ['$cacheFactory', function($cacheFactory) {
6337             return $cacheFactory('templates');
6338           }];
6339         }
6340
6341         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6342          *     Any commits to this file should be reviewed with security in mind.  *
6343          *   Changes to this file can potentially create security vulnerabilities. *
6344          *          An approval from 2 Core members with history of modifying      *
6345          *                         this file is required.                          *
6346          *                                                                         *
6347          *  Does the change somehow allow for arbitrary javascript to be executed? *
6348          *    Or allows for someone to change the prototype of built-in objects?   *
6349          *     Or gives undesired access to variables likes document or window?    *
6350          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
6351
6352         /* ! VARIABLE/FUNCTION NAMING CONVENTIONS THAT APPLY TO THIS FILE!
6353          *
6354          * DOM-related variables:
6355          *
6356          * - "node" - DOM Node
6357          * - "element" - DOM Element or Node
6358          * - "$node" or "$element" - jqLite-wrapped node or element
6359          *
6360          *
6361          * Compiler related stuff:
6362          *
6363          * - "linkFn" - linking fn of a single directive
6364          * - "nodeLinkFn" - function that aggregates all linking fns for a particular node
6365          * - "childLinkFn" -  function that aggregates all linking fns for child nodes of a particular node
6366          * - "compositeLinkFn" - function that aggregates all linking fns for a compilation root (nodeList)
6367          */
6368
6369
6370         /**
6371          * @ngdoc service
6372          * @name $compile
6373          * @kind function
6374          *
6375          * @description
6376          * Compiles an HTML string or DOM into a template and produces a template function, which
6377          * can then be used to link {@link ng.$rootScope.Scope `scope`} and the template together.
6378          *
6379          * The compilation is a process of walking the DOM tree and matching DOM elements to
6380          * {@link ng.$compileProvider#directive directives}.
6381          *
6382          * <div class="alert alert-warning">
6383          * **Note:** This document is an in-depth reference of all directive options.
6384          * For a gentle introduction to directives with examples of common use cases,
6385          * see the {@link guide/directive directive guide}.
6386          * </div>
6387          *
6388          * ## Comprehensive Directive API
6389          *
6390          * There are many different options for a directive.
6391          *
6392          * The difference resides in the return value of the factory function.
6393          * You can either return a "Directive Definition Object" (see below) that defines the directive properties,
6394          * or just the `postLink` function (all other properties will have the default values).
6395          *
6396          * <div class="alert alert-success">
6397          * **Best Practice:** It's recommended to use the "directive definition object" form.
6398          * </div>
6399          *
6400          * Here's an example directive declared with a Directive Definition Object:
6401          *
6402          * ```js
6403          *   var myModule = angular.module(...);
6404          *
6405          *   myModule.directive('directiveName', function factory(injectables) {
6406          *     var directiveDefinitionObject = {
6407          *       priority: 0,
6408          *       template: '<div></div>', // or // function(tElement, tAttrs) { ... },
6409          *       // or
6410          *       // templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
6411          *       transclude: false,
6412          *       restrict: 'A',
6413          *       templateNamespace: 'html',
6414          *       scope: false,
6415          *       controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
6416          *       controllerAs: 'stringIdentifier',
6417          *       bindToController: false,
6418          *       require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
6419          *       compile: function compile(tElement, tAttrs, transclude) {
6420          *         return {
6421          *           pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6422          *           post: function postLink(scope, iElement, iAttrs, controller) { ... }
6423          *         }
6424          *         // or
6425          *         // return function postLink( ... ) { ... }
6426          *       },
6427          *       // or
6428          *       // link: {
6429          *       //  pre: function preLink(scope, iElement, iAttrs, controller) { ... },
6430          *       //  post: function postLink(scope, iElement, iAttrs, controller) { ... }
6431          *       // }
6432          *       // or
6433          *       // link: function postLink( ... ) { ... }
6434          *     };
6435          *     return directiveDefinitionObject;
6436          *   });
6437          * ```
6438          *
6439          * <div class="alert alert-warning">
6440          * **Note:** Any unspecified options will use the default value. You can see the default values below.
6441          * </div>
6442          *
6443          * Therefore the above can be simplified as:
6444          *
6445          * ```js
6446          *   var myModule = angular.module(...);
6447          *
6448          *   myModule.directive('directiveName', function factory(injectables) {
6449          *     var directiveDefinitionObject = {
6450          *       link: function postLink(scope, iElement, iAttrs) { ... }
6451          *     };
6452          *     return directiveDefinitionObject;
6453          *     // or
6454          *     // return function postLink(scope, iElement, iAttrs) { ... }
6455          *   });
6456          * ```
6457          *
6458          *
6459          *
6460          * ### Directive Definition Object
6461          *
6462          * The directive definition object provides instructions to the {@link ng.$compile
6463          * compiler}. The attributes are:
6464          *
6465          * #### `multiElement`
6466          * When this property is set to true, the HTML compiler will collect DOM nodes between
6467          * nodes with the attributes `directive-name-start` and `directive-name-end`, and group them
6468          * together as the directive elements. It is recommended that this feature be used on directives
6469          * which are not strictly behavioural (such as {@link ngClick}), and which
6470          * do not manipulate or replace child nodes (such as {@link ngInclude}).
6471          *
6472          * #### `priority`
6473          * When there are multiple directives defined on a single DOM element, sometimes it
6474          * is necessary to specify the order in which the directives are applied. The `priority` is used
6475          * to sort the directives before their `compile` functions get called. Priority is defined as a
6476          * number. Directives with greater numerical `priority` are compiled first. Pre-link functions
6477          * are also run in priority order, but post-link functions are run in reverse order. The order
6478          * of directives with the same priority is undefined. The default priority is `0`.
6479          *
6480          * #### `terminal`
6481          * If set to true then the current `priority` will be the last set of directives
6482          * which will execute (any directives at the current priority will still execute
6483          * as the order of execution on same `priority` is undefined). Note that expressions
6484          * and other directives used in the directive's template will also be excluded from execution.
6485          *
6486          * #### `scope`
6487          * The scope property can be `true`, an object or a falsy value:
6488          *
6489          * * **falsy:** No scope will be created for the directive. The directive will use its parent's scope.
6490          *
6491          * * **`true`:** A new child scope that prototypically inherits from its parent will be created for
6492          * the directive's element. If multiple directives on the same element request a new scope,
6493          * only one new scope is created. The new scope rule does not apply for the root of the template
6494          * since the root of the template always gets a new scope.
6495          *
6496          * * **`{...}` (an object hash):** A new "isolate" scope is created for the directive's element. The
6497          * 'isolate' scope differs from normal scope in that it does not prototypically inherit from its parent
6498          * scope. This is useful when creating reusable components, which should not accidentally read or modify
6499          * data in the parent scope.
6500          *
6501          * The 'isolate' scope object hash defines a set of local scope properties derived from attributes on the
6502          * directive's element. These local properties are useful for aliasing values for templates. The keys in
6503          * the object hash map to the name of the property on the isolate scope; the values define how the property
6504          * is bound to the parent scope, via matching attributes on the directive's element:
6505          *
6506          * * `@` or `@attr` - bind a local scope property to the value of DOM attribute. The result is
6507          *   always a string since DOM attributes are strings. If no `attr` name is specified  then the
6508          *   attribute name is assumed to be the same as the local name.
6509          *   Given `<widget my-attr="hello {{name}}">` and widget definition
6510          *   of `scope: { localName:'@myAttr' }`, then widget scope property `localName` will reflect
6511          *   the interpolated value of `hello {{name}}`. As the `name` attribute changes so will the
6512          *   `localName` property on the widget scope. The `name` is read from the parent scope (not
6513          *   component scope).
6514          *
6515          * * `=` or `=attr` - set up bi-directional binding between a local scope property and the
6516          *   parent scope property of name defined via the value of the `attr` attribute. If no `attr`
6517          *   name is specified then the attribute name is assumed to be the same as the local name.
6518          *   Given `<widget my-attr="parentModel">` and widget definition of
6519          *   `scope: { localModel:'=myAttr' }`, then widget scope property `localModel` will reflect the
6520          *   value of `parentModel` on the parent scope. Any changes to `parentModel` will be reflected
6521          *   in `localModel` and any changes in `localModel` will reflect in `parentModel`. If the parent
6522          *   scope property doesn't exist, it will throw a NON_ASSIGNABLE_MODEL_EXPRESSION exception. You
6523          *   can avoid this behavior using `=?` or `=?attr` in order to flag the property as optional. If
6524          *   you want to shallow watch for changes (i.e. $watchCollection instead of $watch) you can use
6525          *   `=*` or `=*attr` (`=*?` or `=*?attr` if the property is optional).
6526          *
6527          * * `&` or `&attr` - provides a way to execute an expression in the context of the parent scope.
6528          *   If no `attr` name is specified then the attribute name is assumed to be the same as the
6529          *   local name. Given `<widget my-attr="count = count + value">` and widget definition of
6530          *   `scope: { localFn:'&myAttr' }`, then isolate scope property `localFn` will point to
6531          *   a function wrapper for the `count = count + value` expression. Often it's desirable to
6532          *   pass data from the isolated scope via an expression to the parent scope, this can be
6533          *   done by passing a map of local variable names and values into the expression wrapper fn.
6534          *   For example, if the expression is `increment(amount)` then we can specify the amount value
6535          *   by calling the `localFn` as `localFn({amount: 22})`.
6536          *
6537          * In general it's possible to apply more than one directive to one element, but there might be limitations
6538          * depending on the type of scope required by the directives. The following points will help explain these limitations.
6539          * For simplicity only two directives are taken into account, but it is also applicable for several directives:
6540          *
6541          * * **no scope** + **no scope** => Two directives which don't require their own scope will use their parent's scope
6542          * * **child scope** + **no scope** =>  Both directives will share one single child scope
6543          * * **child scope** + **child scope** =>  Both directives will share one single child scope
6544          * * **isolated scope** + **no scope** =>  The isolated directive will use it's own created isolated scope. The other directive will use
6545          * its parent's scope
6546          * * **isolated scope** + **child scope** =>  **Won't work!** Only one scope can be related to one element. Therefore these directives cannot
6547          * be applied to the same element.
6548          * * **isolated scope** + **isolated scope**  =>  **Won't work!** Only one scope can be related to one element. Therefore these directives
6549          * cannot be applied to the same element.
6550          *
6551          *
6552          * #### `bindToController`
6553          * When an isolate scope is used for a component (see above), and `controllerAs` is used, `bindToController: true` will
6554          * allow a component to have its properties bound to the controller, rather than to scope. When the controller
6555          * is instantiated, the initial values of the isolate scope bindings are already available.
6556          *
6557          * #### `controller`
6558          * Controller constructor function. The controller is instantiated before the
6559          * pre-linking phase and can be accessed by other directives (see
6560          * `require` attribute). This allows the directives to communicate with each other and augment
6561          * each other's behavior. The controller is injectable (and supports bracket notation) with the following locals:
6562          *
6563          * * `$scope` - Current scope associated with the element
6564          * * `$element` - Current element
6565          * * `$attrs` - Current attributes object for the element
6566          * * `$transclude` - A transclude linking function pre-bound to the correct transclusion scope:
6567          *   `function([scope], cloneLinkingFn, futureParentElement)`.
6568          *    * `scope`: optional argument to override the scope.
6569          *    * `cloneLinkingFn`: optional argument to create clones of the original transcluded content.
6570          *    * `futureParentElement`:
6571          *        * defines the parent to which the `cloneLinkingFn` will add the cloned elements.
6572          *        * default: `$element.parent()` resp. `$element` for `transclude:'element'` resp. `transclude:true`.
6573          *        * only needed for transcludes that are allowed to contain non html elements (e.g. SVG elements)
6574          *          and when the `cloneLinkinFn` is passed,
6575          *          as those elements need to created and cloned in a special way when they are defined outside their
6576          *          usual containers (e.g. like `<svg>`).
6577          *        * See also the `directive.templateNamespace` property.
6578          *
6579          *
6580          * #### `require`
6581          * Require another directive and inject its controller as the fourth argument to the linking function. The
6582          * `require` takes a string name (or array of strings) of the directive(s) to pass in. If an array is used, the
6583          * injected argument will be an array in corresponding order. If no such directive can be
6584          * found, or if the directive does not have a controller, then an error is raised (unless no link function
6585          * is specified, in which case error checking is skipped). The name can be prefixed with:
6586          *
6587          * * (no prefix) - Locate the required controller on the current element. Throw an error if not found.
6588          * * `?` - Attempt to locate the required controller or pass `null` to the `link` fn if not found.
6589          * * `^` - Locate the required controller by searching the element and its parents. Throw an error if not found.
6590          * * `^^` - Locate the required controller by searching the element's parents. Throw an error if not found.
6591          * * `?^` - Attempt to locate the required controller by searching the element and its parents or pass
6592          *   `null` to the `link` fn if not found.
6593          * * `?^^` - Attempt to locate the required controller by searching the element's parents, or pass
6594          *   `null` to the `link` fn if not found.
6595          *
6596          *
6597          * #### `controllerAs`
6598          * Identifier name for a reference to the controller in the directive's scope.
6599          * This allows the controller to be referenced from the directive template. This is especially
6600          * useful when a directive is used as component, i.e. with an `isolate` scope. It's also possible
6601          * to use it in a directive without an `isolate` / `new` scope, but you need to be aware that the
6602          * `controllerAs` reference might overwrite a property that already exists on the parent scope.
6603          *
6604          *
6605          * #### `restrict`
6606          * String of subset of `EACM` which restricts the directive to a specific directive
6607          * declaration style. If omitted, the defaults (elements and attributes) are used.
6608          *
6609          * * `E` - Element name (default): `<my-directive></my-directive>`
6610          * * `A` - Attribute (default): `<div my-directive="exp"></div>`
6611          * * `C` - Class: `<div class="my-directive: exp;"></div>`
6612          * * `M` - Comment: `<!-- directive: my-directive exp -->`
6613          *
6614          *
6615          * #### `templateNamespace`
6616          * String representing the document type used by the markup in the template.
6617          * AngularJS needs this information as those elements need to be created and cloned
6618          * in a special way when they are defined outside their usual containers like `<svg>` and `<math>`.
6619          *
6620          * * `html` - All root nodes in the template are HTML. Root nodes may also be
6621          *   top-level elements such as `<svg>` or `<math>`.
6622          * * `svg` - The root nodes in the template are SVG elements (excluding `<math>`).
6623          * * `math` - The root nodes in the template are MathML elements (excluding `<svg>`).
6624          *
6625          * If no `templateNamespace` is specified, then the namespace is considered to be `html`.
6626          *
6627          * #### `template`
6628          * HTML markup that may:
6629          * * Replace the contents of the directive's element (default).
6630          * * Replace the directive's element itself (if `replace` is true - DEPRECATED).
6631          * * Wrap the contents of the directive's element (if `transclude` is true).
6632          *
6633          * Value may be:
6634          *
6635          * * A string. For example `<div red-on-hover>{{delete_str}}</div>`.
6636          * * A function which takes two arguments `tElement` and `tAttrs` (described in the `compile`
6637          *   function api below) and returns a string value.
6638          *
6639          *
6640          * #### `templateUrl`
6641          * This is similar to `template` but the template is loaded from the specified URL, asynchronously.
6642          *
6643          * Because template loading is asynchronous the compiler will suspend compilation of directives on that element
6644          * for later when the template has been resolved.  In the meantime it will continue to compile and link
6645          * sibling and parent elements as though this element had not contained any directives.
6646          *
6647          * The compiler does not suspend the entire compilation to wait for templates to be loaded because this
6648          * would result in the whole app "stalling" until all templates are loaded asynchronously - even in the
6649          * case when only one deeply nested directive has `templateUrl`.
6650          *
6651          * Template loading is asynchronous even if the template has been preloaded into the {@link $templateCache}
6652          *
6653          * You can specify `templateUrl` as a string representing the URL or as a function which takes two
6654          * arguments `tElement` and `tAttrs` (described in the `compile` function api below) and returns
6655          * a string value representing the url.  In either case, the template URL is passed through {@link
6656          * $sce#getTrustedResourceUrl $sce.getTrustedResourceUrl}.
6657          *
6658          *
6659          * #### `replace` ([*DEPRECATED*!], will be removed in next major release - i.e. v2.0)
6660          * specify what the template should replace. Defaults to `false`.
6661          *
6662          * * `true` - the template will replace the directive's element.
6663          * * `false` - the template will replace the contents of the directive's element.
6664          *
6665          * The replacement process migrates all of the attributes / classes from the old element to the new
6666          * one. See the {@link guide/directive#template-expanding-directive
6667          * Directives Guide} for an example.
6668          *
6669          * There are very few scenarios where element replacement is required for the application function,
6670          * the main one being reusable custom components that are used within SVG contexts
6671          * (because SVG doesn't work with custom elements in the DOM tree).
6672          *
6673          * #### `transclude`
6674          * Extract the contents of the element where the directive appears and make it available to the directive.
6675          * The contents are compiled and provided to the directive as a **transclusion function**. See the
6676          * {@link $compile#transclusion Transclusion} section below.
6677          *
6678          * There are two kinds of transclusion depending upon whether you want to transclude just the contents of the
6679          * directive's element or the entire element:
6680          *
6681          * * `true` - transclude the content (i.e. the child nodes) of the directive's element.
6682          * * `'element'` - transclude the whole of the directive's element including any directives on this
6683          *   element that defined at a lower priority than this directive. When used, the `template`
6684          *   property is ignored.
6685          *
6686          *
6687          * #### `compile`
6688          *
6689          * ```js
6690          *   function compile(tElement, tAttrs, transclude) { ... }
6691          * ```
6692          *
6693          * The compile function deals with transforming the template DOM. Since most directives do not do
6694          * template transformation, it is not used often. The compile function takes the following arguments:
6695          *
6696          *   * `tElement` - template element - The element where the directive has been declared. It is
6697          *     safe to do template transformation on the element and child elements only.
6698          *
6699          *   * `tAttrs` - template attributes - Normalized list of attributes declared on this element shared
6700          *     between all directive compile functions.
6701          *
6702          *   * `transclude` -  [*DEPRECATED*!] A transclude linking function: `function(scope, cloneLinkingFn)`
6703          *
6704          * <div class="alert alert-warning">
6705          * **Note:** The template instance and the link instance may be different objects if the template has
6706          * been cloned. For this reason it is **not** safe to do anything other than DOM transformations that
6707          * apply to all cloned DOM nodes within the compile function. Specifically, DOM listener registration
6708          * should be done in a linking function rather than in a compile function.
6709          * </div>
6710
6711          * <div class="alert alert-warning">
6712          * **Note:** The compile function cannot handle directives that recursively use themselves in their
6713          * own templates or compile functions. Compiling these directives results in an infinite loop and a
6714          * stack overflow errors.
6715          *
6716          * This can be avoided by manually using $compile in the postLink function to imperatively compile
6717          * a directive's template instead of relying on automatic template compilation via `template` or
6718          * `templateUrl` declaration or manual compilation inside the compile function.
6719          * </div>
6720          *
6721          * <div class="alert alert-danger">
6722          * **Note:** The `transclude` function that is passed to the compile function is deprecated, as it
6723          *   e.g. does not know about the right outer scope. Please use the transclude function that is passed
6724          *   to the link function instead.
6725          * </div>
6726
6727          * A compile function can have a return value which can be either a function or an object.
6728          *
6729          * * returning a (post-link) function - is equivalent to registering the linking function via the
6730          *   `link` property of the config object when the compile function is empty.
6731          *
6732          * * returning an object with function(s) registered via `pre` and `post` properties - allows you to
6733          *   control when a linking function should be called during the linking phase. See info about
6734          *   pre-linking and post-linking functions below.
6735          *
6736          *
6737          * #### `link`
6738          * This property is used only if the `compile` property is not defined.
6739          *
6740          * ```js
6741          *   function link(scope, iElement, iAttrs, controller, transcludeFn) { ... }
6742          * ```
6743          *
6744          * The link function is responsible for registering DOM listeners as well as updating the DOM. It is
6745          * executed after the template has been cloned. This is where most of the directive logic will be
6746          * put.
6747          *
6748          *   * `scope` - {@link ng.$rootScope.Scope Scope} - The scope to be used by the
6749          *     directive for registering {@link ng.$rootScope.Scope#$watch watches}.
6750          *
6751          *   * `iElement` - instance element - The element where the directive is to be used. It is safe to
6752          *     manipulate the children of the element only in `postLink` function since the children have
6753          *     already been linked.
6754          *
6755          *   * `iAttrs` - instance attributes - Normalized list of attributes declared on this element shared
6756          *     between all directive linking functions.
6757          *
6758          *   * `controller` - the directive's required controller instance(s) - Instances are shared
6759          *     among all directives, which allows the directives to use the controllers as a communication
6760          *     channel. The exact value depends on the directive's `require` property:
6761          *       * no controller(s) required: the directive's own controller, or `undefined` if it doesn't have one
6762          *       * `string`: the controller instance
6763          *       * `array`: array of controller instances
6764          *
6765          *     If a required controller cannot be found, and it is optional, the instance is `null`,
6766          *     otherwise the {@link error:$compile:ctreq Missing Required Controller} error is thrown.
6767          *
6768          *     Note that you can also require the directive's own controller - it will be made available like
6769          *     any other controller.
6770          *
6771          *   * `transcludeFn` - A transclude linking function pre-bound to the correct transclusion scope.
6772          *     This is the same as the `$transclude`
6773          *     parameter of directive controllers, see there for details.
6774          *     `function([scope], cloneLinkingFn, futureParentElement)`.
6775          *
6776          * #### Pre-linking function
6777          *
6778          * Executed before the child elements are linked. Not safe to do DOM transformation since the
6779          * compiler linking function will fail to locate the correct elements for linking.
6780          *
6781          * #### Post-linking function
6782          *
6783          * Executed after the child elements are linked.
6784          *
6785          * Note that child elements that contain `templateUrl` directives will not have been compiled
6786          * and linked since they are waiting for their template to load asynchronously and their own
6787          * compilation and linking has been suspended until that occurs.
6788          *
6789          * It is safe to do DOM transformation in the post-linking function on elements that are not waiting
6790          * for their async templates to be resolved.
6791          *
6792          *
6793          * ### Transclusion
6794          *
6795          * Transclusion is the process of extracting a collection of DOM elements from one part of the DOM and
6796          * copying them to another part of the DOM, while maintaining their connection to the original AngularJS
6797          * scope from where they were taken.
6798          *
6799          * Transclusion is used (often with {@link ngTransclude}) to insert the
6800          * original contents of a directive's element into a specified place in the template of the directive.
6801          * The benefit of transclusion, over simply moving the DOM elements manually, is that the transcluded
6802          * content has access to the properties on the scope from which it was taken, even if the directive
6803          * has isolated scope.
6804          * See the {@link guide/directive#creating-a-directive-that-wraps-other-elements Directives Guide}.
6805          *
6806          * This makes it possible for the widget to have private state for its template, while the transcluded
6807          * content has access to its originating scope.
6808          *
6809          * <div class="alert alert-warning">
6810          * **Note:** When testing an element transclude directive you must not place the directive at the root of the
6811          * DOM fragment that is being compiled. See {@link guide/unit-testing#testing-transclusion-directives
6812          * Testing Transclusion Directives}.
6813          * </div>
6814          *
6815          * #### Transclusion Functions
6816          *
6817          * When a directive requests transclusion, the compiler extracts its contents and provides a **transclusion
6818          * function** to the directive's `link` function and `controller`. This transclusion function is a special
6819          * **linking function** that will return the compiled contents linked to a new transclusion scope.
6820          *
6821          * <div class="alert alert-info">
6822          * If you are just using {@link ngTransclude} then you don't need to worry about this function, since
6823          * ngTransclude will deal with it for us.
6824          * </div>
6825          *
6826          * If you want to manually control the insertion and removal of the transcluded content in your directive
6827          * then you must use this transclude function. When you call a transclude function it returns a a jqLite/JQuery
6828          * object that contains the compiled DOM, which is linked to the correct transclusion scope.
6829          *
6830          * When you call a transclusion function you can pass in a **clone attach function**. This function accepts
6831          * two parameters, `function(clone, scope) { ... }`, where the `clone` is a fresh compiled copy of your transcluded
6832          * content and the `scope` is the newly created transclusion scope, to which the clone is bound.
6833          *
6834          * <div class="alert alert-info">
6835          * **Best Practice**: Always provide a `cloneFn` (clone attach function) when you call a translude function
6836          * since you then get a fresh clone of the original DOM and also have access to the new transclusion scope.
6837          * </div>
6838          *
6839          * It is normal practice to attach your transcluded content (`clone`) to the DOM inside your **clone
6840          * attach function**:
6841          *
6842          * ```js
6843          * var transcludedContent, transclusionScope;
6844          *
6845          * $transclude(function(clone, scope) {
6846          *   element.append(clone);
6847          *   transcludedContent = clone;
6848          *   transclusionScope = scope;
6849          * });
6850          * ```
6851          *
6852          * Later, if you want to remove the transcluded content from your DOM then you should also destroy the
6853          * associated transclusion scope:
6854          *
6855          * ```js
6856          * transcludedContent.remove();
6857          * transclusionScope.$destroy();
6858          * ```
6859          *
6860          * <div class="alert alert-info">
6861          * **Best Practice**: if you intend to add and remove transcluded content manually in your directive
6862          * (by calling the transclude function to get the DOM and calling `element.remove()` to remove it),
6863          * then you are also responsible for calling `$destroy` on the transclusion scope.
6864          * </div>
6865          *
6866          * The built-in DOM manipulation directives, such as {@link ngIf}, {@link ngSwitch} and {@link ngRepeat}
6867          * automatically destroy their transluded clones as necessary so you do not need to worry about this if
6868          * you are simply using {@link ngTransclude} to inject the transclusion into your directive.
6869          *
6870          *
6871          * #### Transclusion Scopes
6872          *
6873          * When you call a transclude function it returns a DOM fragment that is pre-bound to a **transclusion
6874          * scope**. This scope is special, in that it is a child of the directive's scope (and so gets destroyed
6875          * when the directive's scope gets destroyed) but it inherits the properties of the scope from which it
6876          * was taken.
6877          *
6878          * For example consider a directive that uses transclusion and isolated scope. The DOM hierarchy might look
6879          * like this:
6880          *
6881          * ```html
6882          * <div ng-app>
6883          *   <div isolate>
6884          *     <div transclusion>
6885          *     </div>
6886          *   </div>
6887          * </div>
6888          * ```
6889          *
6890          * The `$parent` scope hierarchy will look like this:
6891          *
6892          * ```
6893          * - $rootScope
6894          *   - isolate
6895          *     - transclusion
6896          * ```
6897          *
6898          * but the scopes will inherit prototypically from different scopes to their `$parent`.
6899          *
6900          * ```
6901          * - $rootScope
6902          *   - transclusion
6903          * - isolate
6904          * ```
6905          *
6906          *
6907          * ### Attributes
6908          *
6909          * The {@link ng.$compile.directive.Attributes Attributes} object - passed as a parameter in the
6910          * `link()` or `compile()` functions. It has a variety of uses.
6911          *
6912          * accessing *Normalized attribute names:*
6913          * Directives like 'ngBind' can be expressed in many ways: 'ng:bind', `data-ng-bind`, or 'x-ng-bind'.
6914          * the attributes object allows for normalized access to
6915          *   the attributes.
6916          *
6917          * * *Directive inter-communication:* All directives share the same instance of the attributes
6918          *   object which allows the directives to use the attributes object as inter directive
6919          *   communication.
6920          *
6921          * * *Supports interpolation:* Interpolation attributes are assigned to the attribute object
6922          *   allowing other directives to read the interpolated value.
6923          *
6924          * * *Observing interpolated attributes:* Use `$observe` to observe the value changes of attributes
6925          *   that contain interpolation (e.g. `src="{{bar}}"`). Not only is this very efficient but it's also
6926          *   the only way to easily get the actual value because during the linking phase the interpolation
6927          *   hasn't been evaluated yet and so the value is at this time set to `undefined`.
6928          *
6929          * ```js
6930          * function linkingFn(scope, elm, attrs, ctrl) {
6931          *   // get the attribute value
6932          *   console.log(attrs.ngModel);
6933          *
6934          *   // change the attribute
6935          *   attrs.$set('ngModel', 'new value');
6936          *
6937          *   // observe changes to interpolated attribute
6938          *   attrs.$observe('ngModel', function(value) {
6939          *     console.log('ngModel has changed value to ' + value);
6940          *   });
6941          * }
6942          * ```
6943          *
6944          * ## Example
6945          *
6946          * <div class="alert alert-warning">
6947          * **Note**: Typically directives are registered with `module.directive`. The example below is
6948          * to illustrate how `$compile` works.
6949          * </div>
6950          *
6951          <example module="compileExample">
6952            <file name="index.html">
6953             <script>
6954               angular.module('compileExample', [], function($compileProvider) {
6955                 // configure new 'compile' directive by passing a directive
6956                 // factory function. The factory function injects the '$compile'
6957                 $compileProvider.directive('compile', function($compile) {
6958                   // directive factory creates a link function
6959                   return function(scope, element, attrs) {
6960                     scope.$watch(
6961                       function(scope) {
6962                          // watch the 'compile' expression for changes
6963                         return scope.$eval(attrs.compile);
6964                       },
6965                       function(value) {
6966                         // when the 'compile' expression changes
6967                         // assign it into the current DOM
6968                         element.html(value);
6969
6970                         // compile the new DOM and link it to the current
6971                         // scope.
6972                         // NOTE: we only compile .childNodes so that
6973                         // we don't get into infinite loop compiling ourselves
6974                         $compile(element.contents())(scope);
6975                       }
6976                     );
6977                   };
6978                 });
6979               })
6980               .controller('GreeterController', ['$scope', function($scope) {
6981                 $scope.name = 'Angular';
6982                 $scope.html = 'Hello {{name}}';
6983               }]);
6984             </script>
6985             <div ng-controller="GreeterController">
6986               <input ng-model="name"> <br/>
6987               <textarea ng-model="html"></textarea> <br/>
6988               <div compile="html"></div>
6989             </div>
6990            </file>
6991            <file name="protractor.js" type="protractor">
6992              it('should auto compile', function() {
6993                var textarea = $('textarea');
6994                var output = $('div[compile]');
6995                // The initial state reads 'Hello Angular'.
6996                expect(output.getText()).toBe('Hello Angular');
6997                textarea.clear();
6998                textarea.sendKeys('{{name}}!');
6999                expect(output.getText()).toBe('Angular!');
7000              });
7001            </file>
7002          </example>
7003
7004          *
7005          *
7006          * @param {string|DOMElement} element Element or HTML string to compile into a template function.
7007          * @param {function(angular.Scope, cloneAttachFn=)} transclude function available to directives - DEPRECATED.
7008          *
7009          * <div class="alert alert-danger">
7010          * **Note:** Passing a `transclude` function to the $compile function is deprecated, as it
7011          *   e.g. will not use the right outer scope. Please pass the transclude function as a
7012          *   `parentBoundTranscludeFn` to the link function instead.
7013          * </div>
7014          *
7015          * @param {number} maxPriority only apply directives lower than given priority (Only effects the
7016          *                 root element(s), not their children)
7017          * @returns {function(scope, cloneAttachFn=, options=)} a link function which is used to bind template
7018          * (a DOM element/tree) to a scope. Where:
7019          *
7020          *  * `scope` - A {@link ng.$rootScope.Scope Scope} to bind to.
7021          *  * `cloneAttachFn` - If `cloneAttachFn` is provided, then the link function will clone the
7022          *  `template` and call the `cloneAttachFn` function allowing the caller to attach the
7023          *  cloned elements to the DOM document at the appropriate place. The `cloneAttachFn` is
7024          *  called as: <br/> `cloneAttachFn(clonedElement, scope)` where:
7025          *
7026          *      * `clonedElement` - is a clone of the original `element` passed into the compiler.
7027          *      * `scope` - is the current scope with which the linking function is working with.
7028          *
7029          *  * `options` - An optional object hash with linking options. If `options` is provided, then the following
7030          *  keys may be used to control linking behavior:
7031          *
7032          *      * `parentBoundTranscludeFn` - the transclude function made available to
7033          *        directives; if given, it will be passed through to the link functions of
7034          *        directives found in `element` during compilation.
7035          *      * `transcludeControllers` - an object hash with keys that map controller names
7036          *        to controller instances; if given, it will make the controllers
7037          *        available to directives.
7038          *      * `futureParentElement` - defines the parent to which the `cloneAttachFn` will add
7039          *        the cloned elements; only needed for transcludes that are allowed to contain non html
7040          *        elements (e.g. SVG elements). See also the directive.controller property.
7041          *
7042          * Calling the linking function returns the element of the template. It is either the original
7043          * element passed in, or the clone of the element if the `cloneAttachFn` is provided.
7044          *
7045          * After linking the view is not updated until after a call to $digest which typically is done by
7046          * Angular automatically.
7047          *
7048          * If you need access to the bound view, there are two ways to do it:
7049          *
7050          * - If you are not asking the linking function to clone the template, create the DOM element(s)
7051          *   before you send them to the compiler and keep this reference around.
7052          *   ```js
7053          *     var element = $compile('<p>{{total}}</p>')(scope);
7054          *   ```
7055          *
7056          * - if on the other hand, you need the element to be cloned, the view reference from the original
7057          *   example would not point to the clone, but rather to the original template that was cloned. In
7058          *   this case, you can access the clone via the cloneAttachFn:
7059          *   ```js
7060          *     var templateElement = angular.element('<p>{{total}}</p>'),
7061          *         scope = ....;
7062          *
7063          *     var clonedElement = $compile(templateElement)(scope, function(clonedElement, scope) {
7064          *       //attach the clone to DOM document at the right place
7065          *     });
7066          *
7067          *     //now we have reference to the cloned DOM via `clonedElement`
7068          *   ```
7069          *
7070          *
7071          * For information on how the compiler works, see the
7072          * {@link guide/compiler Angular HTML Compiler} section of the Developer Guide.
7073          */
7074
7075         var $compileMinErr = minErr('$compile');
7076
7077         /**
7078          * @ngdoc provider
7079          * @name $compileProvider
7080          *
7081          * @description
7082          */
7083         $CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];
7084         function $CompileProvider($provide, $$sanitizeUriProvider) {
7085           var hasDirectives = {},
7086               Suffix = 'Directive',
7087               COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
7088               CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
7089               ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
7090               REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
7091
7092           // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
7093           // The assumption is that future DOM event attribute names will begin with
7094           // 'on' and be composed of only English letters.
7095           var EVENT_HANDLER_ATTR_REGEXP = /^(on[a-z]+|formaction)$/;
7096
7097           function parseIsolateBindings(scope, directiveName, isController) {
7098             var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/;
7099
7100             var bindings = {};
7101
7102             forEach(scope, function(definition, scopeName) {
7103               var match = definition.match(LOCAL_REGEXP);
7104
7105               if (!match) {
7106                 throw $compileMinErr('iscp',
7107                     "Invalid {3} for directive '{0}'." +
7108                     " Definition: {... {1}: '{2}' ...}",
7109                     directiveName, scopeName, definition,
7110                     (isController ? "controller bindings definition" :
7111                     "isolate scope definition"));
7112               }
7113
7114               bindings[scopeName] = {
7115                 mode: match[1][0],
7116                 collection: match[2] === '*',
7117                 optional: match[3] === '?',
7118                 attrName: match[4] || scopeName
7119               };
7120             });
7121
7122             return bindings;
7123           }
7124
7125           function parseDirectiveBindings(directive, directiveName) {
7126             var bindings = {
7127               isolateScope: null,
7128               bindToController: null
7129             };
7130             if (isObject(directive.scope)) {
7131               if (directive.bindToController === true) {
7132                 bindings.bindToController = parseIsolateBindings(directive.scope,
7133                                                                  directiveName, true);
7134                 bindings.isolateScope = {};
7135               } else {
7136                 bindings.isolateScope = parseIsolateBindings(directive.scope,
7137                                                              directiveName, false);
7138               }
7139             }
7140             if (isObject(directive.bindToController)) {
7141               bindings.bindToController =
7142                   parseIsolateBindings(directive.bindToController, directiveName, true);
7143             }
7144             if (isObject(bindings.bindToController)) {
7145               var controller = directive.controller;
7146               var controllerAs = directive.controllerAs;
7147               if (!controller) {
7148                 // There is no controller, there may or may not be a controllerAs property
7149                 throw $compileMinErr('noctrl',
7150                       "Cannot bind to controller without directive '{0}'s controller.",
7151                       directiveName);
7152               } else if (!identifierForController(controller, controllerAs)) {
7153                 // There is a controller, but no identifier or controllerAs property
7154                 throw $compileMinErr('noident',
7155                       "Cannot bind to controller without identifier for directive '{0}'.",
7156                       directiveName);
7157               }
7158             }
7159             return bindings;
7160           }
7161
7162           function assertValidDirectiveName(name) {
7163             var letter = name.charAt(0);
7164             if (!letter || letter !== lowercase(letter)) {
7165               throw $compileMinErr('baddir', "Directive name '{0}' is invalid. The first character must be a lowercase letter", name);
7166             }
7167             if (name !== name.trim()) {
7168               throw $compileMinErr('baddir',
7169                     "Directive name '{0}' is invalid. The name should not contain leading or trailing whitespaces",
7170                     name);
7171             }
7172           }
7173
7174           /**
7175            * @ngdoc method
7176            * @name $compileProvider#directive
7177            * @kind function
7178            *
7179            * @description
7180            * Register a new directive with the compiler.
7181            *
7182            * @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
7183            *    will match as <code>ng-bind</code>), or an object map of directives where the keys are the
7184            *    names and the values are the factories.
7185            * @param {Function|Array} directiveFactory An injectable directive factory function. See
7186            *    {@link guide/directive} for more info.
7187            * @returns {ng.$compileProvider} Self for chaining.
7188            */
7189            this.directive = function registerDirective(name, directiveFactory) {
7190             assertNotHasOwnProperty(name, 'directive');
7191             if (isString(name)) {
7192               assertValidDirectiveName(name);
7193               assertArg(directiveFactory, 'directiveFactory');
7194               if (!hasDirectives.hasOwnProperty(name)) {
7195                 hasDirectives[name] = [];
7196                 $provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
7197                   function($injector, $exceptionHandler) {
7198                     var directives = [];
7199                     forEach(hasDirectives[name], function(directiveFactory, index) {
7200                       try {
7201                         var directive = $injector.invoke(directiveFactory);
7202                         if (isFunction(directive)) {
7203                           directive = { compile: valueFn(directive) };
7204                         } else if (!directive.compile && directive.link) {
7205                           directive.compile = valueFn(directive.link);
7206                         }
7207                         directive.priority = directive.priority || 0;
7208                         directive.index = index;
7209                         directive.name = directive.name || name;
7210                         directive.require = directive.require || (directive.controller && directive.name);
7211                         directive.restrict = directive.restrict || 'EA';
7212                         var bindings = directive.$$bindings =
7213                             parseDirectiveBindings(directive, directive.name);
7214                         if (isObject(bindings.isolateScope)) {
7215                           directive.$$isolateBindings = bindings.isolateScope;
7216                         }
7217                         directive.$$moduleName = directiveFactory.$$moduleName;
7218                         directives.push(directive);
7219                       } catch (e) {
7220                         $exceptionHandler(e);
7221                       }
7222                     });
7223                     return directives;
7224                   }]);
7225               }
7226               hasDirectives[name].push(directiveFactory);
7227             } else {
7228               forEach(name, reverseParams(registerDirective));
7229             }
7230             return this;
7231           };
7232
7233
7234           /**
7235            * @ngdoc method
7236            * @name $compileProvider#aHrefSanitizationWhitelist
7237            * @kind function
7238            *
7239            * @description
7240            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7241            * urls during a[href] sanitization.
7242            *
7243            * The sanitization is a security measure aimed at preventing XSS attacks via html links.
7244            *
7245            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
7246            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
7247            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7248            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7249            *
7250            * @param {RegExp=} regexp New regexp to whitelist urls with.
7251            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7252            *    chaining otherwise.
7253            */
7254           this.aHrefSanitizationWhitelist = function(regexp) {
7255             if (isDefined(regexp)) {
7256               $$sanitizeUriProvider.aHrefSanitizationWhitelist(regexp);
7257               return this;
7258             } else {
7259               return $$sanitizeUriProvider.aHrefSanitizationWhitelist();
7260             }
7261           };
7262
7263
7264           /**
7265            * @ngdoc method
7266            * @name $compileProvider#imgSrcSanitizationWhitelist
7267            * @kind function
7268            *
7269            * @description
7270            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
7271            * urls during img[src] sanitization.
7272            *
7273            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
7274            *
7275            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
7276            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
7277            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
7278            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
7279            *
7280            * @param {RegExp=} regexp New regexp to whitelist urls with.
7281            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
7282            *    chaining otherwise.
7283            */
7284           this.imgSrcSanitizationWhitelist = function(regexp) {
7285             if (isDefined(regexp)) {
7286               $$sanitizeUriProvider.imgSrcSanitizationWhitelist(regexp);
7287               return this;
7288             } else {
7289               return $$sanitizeUriProvider.imgSrcSanitizationWhitelist();
7290             }
7291           };
7292
7293           /**
7294            * @ngdoc method
7295            * @name  $compileProvider#debugInfoEnabled
7296            *
7297            * @param {boolean=} enabled update the debugInfoEnabled state if provided, otherwise just return the
7298            * current debugInfoEnabled state
7299            * @returns {*} current value if used as getter or itself (chaining) if used as setter
7300            *
7301            * @kind function
7302            *
7303            * @description
7304            * Call this method to enable/disable various debug runtime information in the compiler such as adding
7305            * binding information and a reference to the current scope on to DOM elements.
7306            * If enabled, the compiler will add the following to DOM elements that have been bound to the scope
7307            * * `ng-binding` CSS class
7308            * * `$binding` data property containing an array of the binding expressions
7309            *
7310            * You may want to disable this in production for a significant performance boost. See
7311            * {@link guide/production#disabling-debug-data Disabling Debug Data} for more.
7312            *
7313            * The default value is true.
7314            */
7315           var debugInfoEnabled = true;
7316           this.debugInfoEnabled = function(enabled) {
7317             if (isDefined(enabled)) {
7318               debugInfoEnabled = enabled;
7319               return this;
7320             }
7321             return debugInfoEnabled;
7322           };
7323
7324           this.$get = [
7325                     '$injector', '$interpolate', '$exceptionHandler', '$templateRequest', '$parse',
7326                     '$controller', '$rootScope', '$document', '$sce', '$animate', '$$sanitizeUri',
7327             function($injector,   $interpolate,   $exceptionHandler,   $templateRequest,   $parse,
7328                      $controller,   $rootScope,   $document,   $sce,   $animate,   $$sanitizeUri) {
7329
7330             var Attributes = function(element, attributesToCopy) {
7331               if (attributesToCopy) {
7332                 var keys = Object.keys(attributesToCopy);
7333                 var i, l, key;
7334
7335                 for (i = 0, l = keys.length; i < l; i++) {
7336                   key = keys[i];
7337                   this[key] = attributesToCopy[key];
7338                 }
7339               } else {
7340                 this.$attr = {};
7341               }
7342
7343               this.$$element = element;
7344             };
7345
7346             Attributes.prototype = {
7347               /**
7348                * @ngdoc method
7349                * @name $compile.directive.Attributes#$normalize
7350                * @kind function
7351                *
7352                * @description
7353                * Converts an attribute name (e.g. dash/colon/underscore-delimited string, optionally prefixed with `x-` or
7354                * `data-`) to its normalized, camelCase form.
7355                *
7356                * Also there is special case for Moz prefix starting with upper case letter.
7357                *
7358                * For further information check out the guide on {@link guide/directive#matching-directives Matching Directives}
7359                *
7360                * @param {string} name Name to normalize
7361                */
7362               $normalize: directiveNormalize,
7363
7364
7365               /**
7366                * @ngdoc method
7367                * @name $compile.directive.Attributes#$addClass
7368                * @kind function
7369                *
7370                * @description
7371                * Adds the CSS class value specified by the classVal parameter to the element. If animations
7372                * are enabled then an animation will be triggered for the class addition.
7373                *
7374                * @param {string} classVal The className value that will be added to the element
7375                */
7376               $addClass: function(classVal) {
7377                 if (classVal && classVal.length > 0) {
7378                   $animate.addClass(this.$$element, classVal);
7379                 }
7380               },
7381
7382               /**
7383                * @ngdoc method
7384                * @name $compile.directive.Attributes#$removeClass
7385                * @kind function
7386                *
7387                * @description
7388                * Removes the CSS class value specified by the classVal parameter from the element. If
7389                * animations are enabled then an animation will be triggered for the class removal.
7390                *
7391                * @param {string} classVal The className value that will be removed from the element
7392                */
7393               $removeClass: function(classVal) {
7394                 if (classVal && classVal.length > 0) {
7395                   $animate.removeClass(this.$$element, classVal);
7396                 }
7397               },
7398
7399               /**
7400                * @ngdoc method
7401                * @name $compile.directive.Attributes#$updateClass
7402                * @kind function
7403                *
7404                * @description
7405                * Adds and removes the appropriate CSS class values to the element based on the difference
7406                * between the new and old CSS class values (specified as newClasses and oldClasses).
7407                *
7408                * @param {string} newClasses The current CSS className value
7409                * @param {string} oldClasses The former CSS className value
7410                */
7411               $updateClass: function(newClasses, oldClasses) {
7412                 var toAdd = tokenDifference(newClasses, oldClasses);
7413                 if (toAdd && toAdd.length) {
7414                   $animate.addClass(this.$$element, toAdd);
7415                 }
7416
7417                 var toRemove = tokenDifference(oldClasses, newClasses);
7418                 if (toRemove && toRemove.length) {
7419                   $animate.removeClass(this.$$element, toRemove);
7420                 }
7421               },
7422
7423               /**
7424                * Set a normalized attribute on the element in a way such that all directives
7425                * can share the attribute. This function properly handles boolean attributes.
7426                * @param {string} key Normalized key. (ie ngAttribute)
7427                * @param {string|boolean} value The value to set. If `null` attribute will be deleted.
7428                * @param {boolean=} writeAttr If false, does not write the value to DOM element attribute.
7429                *     Defaults to true.
7430                * @param {string=} attrName Optional none normalized name. Defaults to key.
7431                */
7432               $set: function(key, value, writeAttr, attrName) {
7433                 // TODO: decide whether or not to throw an error if "class"
7434                 //is set through this function since it may cause $updateClass to
7435                 //become unstable.
7436
7437                 var node = this.$$element[0],
7438                     booleanKey = getBooleanAttrName(node, key),
7439                     aliasedKey = getAliasedAttrName(key),
7440                     observer = key,
7441                     nodeName;
7442
7443                 if (booleanKey) {
7444                   this.$$element.prop(key, value);
7445                   attrName = booleanKey;
7446                 } else if (aliasedKey) {
7447                   this[aliasedKey] = value;
7448                   observer = aliasedKey;
7449                 }
7450
7451                 this[key] = value;
7452
7453                 // translate normalized key to actual key
7454                 if (attrName) {
7455                   this.$attr[key] = attrName;
7456                 } else {
7457                   attrName = this.$attr[key];
7458                   if (!attrName) {
7459                     this.$attr[key] = attrName = snake_case(key, '-');
7460                   }
7461                 }
7462
7463                 nodeName = nodeName_(this.$$element);
7464
7465                 if ((nodeName === 'a' && key === 'href') ||
7466                     (nodeName === 'img' && key === 'src')) {
7467                   // sanitize a[href] and img[src] values
7468                   this[key] = value = $$sanitizeUri(value, key === 'src');
7469                 } else if (nodeName === 'img' && key === 'srcset') {
7470                   // sanitize img[srcset] values
7471                   var result = "";
7472
7473                   // first check if there are spaces because it's not the same pattern
7474                   var trimmedSrcset = trim(value);
7475                   //                (   999x   ,|   999w   ,|   ,|,   )
7476                   var srcPattern = /(\s+\d+x\s*,|\s+\d+w\s*,|\s+,|,\s+)/;
7477                   var pattern = /\s/.test(trimmedSrcset) ? srcPattern : /(,)/;
7478
7479                   // split srcset into tuple of uri and descriptor except for the last item
7480                   var rawUris = trimmedSrcset.split(pattern);
7481
7482                   // for each tuples
7483                   var nbrUrisWith2parts = Math.floor(rawUris.length / 2);
7484                   for (var i = 0; i < nbrUrisWith2parts; i++) {
7485                     var innerIdx = i * 2;
7486                     // sanitize the uri
7487                     result += $$sanitizeUri(trim(rawUris[innerIdx]), true);
7488                     // add the descriptor
7489                     result += (" " + trim(rawUris[innerIdx + 1]));
7490                   }
7491
7492                   // split the last item into uri and descriptor
7493                   var lastTuple = trim(rawUris[i * 2]).split(/\s/);
7494
7495                   // sanitize the last uri
7496                   result += $$sanitizeUri(trim(lastTuple[0]), true);
7497
7498                   // and add the last descriptor if any
7499                   if (lastTuple.length === 2) {
7500                     result += (" " + trim(lastTuple[1]));
7501                   }
7502                   this[key] = value = result;
7503                 }
7504
7505                 if (writeAttr !== false) {
7506                   if (value === null || isUndefined(value)) {
7507                     this.$$element.removeAttr(attrName);
7508                   } else {
7509                     this.$$element.attr(attrName, value);
7510                   }
7511                 }
7512
7513                 // fire observers
7514                 var $$observers = this.$$observers;
7515                 $$observers && forEach($$observers[observer], function(fn) {
7516                   try {
7517                     fn(value);
7518                   } catch (e) {
7519                     $exceptionHandler(e);
7520                   }
7521                 });
7522               },
7523
7524
7525               /**
7526                * @ngdoc method
7527                * @name $compile.directive.Attributes#$observe
7528                * @kind function
7529                *
7530                * @description
7531                * Observes an interpolated attribute.
7532                *
7533                * The observer function will be invoked once during the next `$digest` following
7534                * compilation. The observer is then invoked whenever the interpolated value
7535                * changes.
7536                *
7537                * @param {string} key Normalized key. (ie ngAttribute) .
7538                * @param {function(interpolatedValue)} fn Function that will be called whenever
7539                         the interpolated value of the attribute changes.
7540                *        See the {@link guide/directive#text-and-attribute-bindings Directives} guide for more info.
7541                * @returns {function()} Returns a deregistration function for this observer.
7542                */
7543               $observe: function(key, fn) {
7544                 var attrs = this,
7545                     $$observers = (attrs.$$observers || (attrs.$$observers = createMap())),
7546                     listeners = ($$observers[key] || ($$observers[key] = []));
7547
7548                 listeners.push(fn);
7549                 $rootScope.$evalAsync(function() {
7550                   if (!listeners.$$inter && attrs.hasOwnProperty(key) && !isUndefined(attrs[key])) {
7551                     // no one registered attribute interpolation function, so lets call it manually
7552                     fn(attrs[key]);
7553                   }
7554                 });
7555
7556                 return function() {
7557                   arrayRemove(listeners, fn);
7558                 };
7559               }
7560             };
7561
7562
7563             function safeAddClass($element, className) {
7564               try {
7565                 $element.addClass(className);
7566               } catch (e) {
7567                 // ignore, since it means that we are trying to set class on
7568                 // SVG element, where class name is read-only.
7569               }
7570             }
7571
7572
7573             var startSymbol = $interpolate.startSymbol(),
7574                 endSymbol = $interpolate.endSymbol(),
7575                 denormalizeTemplate = (startSymbol == '{{' || endSymbol  == '}}')
7576                     ? identity
7577                     : function denormalizeTemplate(template) {
7578                       return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
7579                 },
7580                 NG_ATTR_BINDING = /^ngAttr[A-Z]/;
7581             var MULTI_ELEMENT_DIR_RE = /^(.+)Start$/;
7582
7583             compile.$$addBindingInfo = debugInfoEnabled ? function $$addBindingInfo($element, binding) {
7584               var bindings = $element.data('$binding') || [];
7585
7586               if (isArray(binding)) {
7587                 bindings = bindings.concat(binding);
7588               } else {
7589                 bindings.push(binding);
7590               }
7591
7592               $element.data('$binding', bindings);
7593             } : noop;
7594
7595             compile.$$addBindingClass = debugInfoEnabled ? function $$addBindingClass($element) {
7596               safeAddClass($element, 'ng-binding');
7597             } : noop;
7598
7599             compile.$$addScopeInfo = debugInfoEnabled ? function $$addScopeInfo($element, scope, isolated, noTemplate) {
7600               var dataName = isolated ? (noTemplate ? '$isolateScopeNoTemplate' : '$isolateScope') : '$scope';
7601               $element.data(dataName, scope);
7602             } : noop;
7603
7604             compile.$$addScopeClass = debugInfoEnabled ? function $$addScopeClass($element, isolated) {
7605               safeAddClass($element, isolated ? 'ng-isolate-scope' : 'ng-scope');
7606             } : noop;
7607
7608             return compile;
7609
7610             //================================
7611
7612             function compile($compileNodes, transcludeFn, maxPriority, ignoreDirective,
7613                                 previousCompileContext) {
7614               if (!($compileNodes instanceof jqLite)) {
7615                 // jquery always rewraps, whereas we need to preserve the original selector so that we can
7616                 // modify it.
7617                 $compileNodes = jqLite($compileNodes);
7618               }
7619               // We can not compile top level text elements since text nodes can be merged and we will
7620               // not be able to attach scope data to them, so we will wrap them in <span>
7621               forEach($compileNodes, function(node, index) {
7622                 if (node.nodeType == NODE_TYPE_TEXT && node.nodeValue.match(/\S+/) /* non-empty */ ) {
7623                   $compileNodes[index] = jqLite(node).wrap('<span></span>').parent()[0];
7624                 }
7625               });
7626               var compositeLinkFn =
7627                       compileNodes($compileNodes, transcludeFn, $compileNodes,
7628                                    maxPriority, ignoreDirective, previousCompileContext);
7629               compile.$$addScopeClass($compileNodes);
7630               var namespace = null;
7631               return function publicLinkFn(scope, cloneConnectFn, options) {
7632                 assertArg(scope, 'scope');
7633
7634                 if (previousCompileContext && previousCompileContext.needsNewScope) {
7635                   // A parent directive did a replace and a directive on this element asked
7636                   // for transclusion, which caused us to lose a layer of element on which
7637                   // we could hold the new transclusion scope, so we will create it manually
7638                   // here.
7639                   scope = scope.$parent.$new();
7640                 }
7641
7642                 options = options || {};
7643                 var parentBoundTranscludeFn = options.parentBoundTranscludeFn,
7644                   transcludeControllers = options.transcludeControllers,
7645                   futureParentElement = options.futureParentElement;
7646
7647                 // When `parentBoundTranscludeFn` is passed, it is a
7648                 // `controllersBoundTransclude` function (it was previously passed
7649                 // as `transclude` to directive.link) so we must unwrap it to get
7650                 // its `boundTranscludeFn`
7651                 if (parentBoundTranscludeFn && parentBoundTranscludeFn.$$boundTransclude) {
7652                   parentBoundTranscludeFn = parentBoundTranscludeFn.$$boundTransclude;
7653                 }
7654
7655                 if (!namespace) {
7656                   namespace = detectNamespaceForChildElements(futureParentElement);
7657                 }
7658                 var $linkNode;
7659                 if (namespace !== 'html') {
7660                   // When using a directive with replace:true and templateUrl the $compileNodes
7661                   // (or a child element inside of them)
7662                   // might change, so we need to recreate the namespace adapted compileNodes
7663                   // for call to the link function.
7664                   // Note: This will already clone the nodes...
7665                   $linkNode = jqLite(
7666                     wrapTemplate(namespace, jqLite('<div>').append($compileNodes).html())
7667                   );
7668                 } else if (cloneConnectFn) {
7669                   // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart
7670                   // and sometimes changes the structure of the DOM.
7671                   $linkNode = JQLitePrototype.clone.call($compileNodes);
7672                 } else {
7673                   $linkNode = $compileNodes;
7674                 }
7675
7676                 if (transcludeControllers) {
7677                   for (var controllerName in transcludeControllers) {
7678                     $linkNode.data('$' + controllerName + 'Controller', transcludeControllers[controllerName].instance);
7679                   }
7680                 }
7681
7682                 compile.$$addScopeInfo($linkNode, scope);
7683
7684                 if (cloneConnectFn) cloneConnectFn($linkNode, scope);
7685                 if (compositeLinkFn) compositeLinkFn(scope, $linkNode, $linkNode, parentBoundTranscludeFn);
7686                 return $linkNode;
7687               };
7688             }
7689
7690             function detectNamespaceForChildElements(parentElement) {
7691               // TODO: Make this detect MathML as well...
7692               var node = parentElement && parentElement[0];
7693               if (!node) {
7694                 return 'html';
7695               } else {
7696                 return nodeName_(node) !== 'foreignobject' && node.toString().match(/SVG/) ? 'svg' : 'html';
7697               }
7698             }
7699
7700             /**
7701              * Compile function matches each node in nodeList against the directives. Once all directives
7702              * for a particular node are collected their compile functions are executed. The compile
7703              * functions return values - the linking functions - are combined into a composite linking
7704              * function, which is the a linking function for the node.
7705              *
7706              * @param {NodeList} nodeList an array of nodes or NodeList to compile
7707              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7708              *        scope argument is auto-generated to the new child of the transcluded parent scope.
7709              * @param {DOMElement=} $rootElement If the nodeList is the root of the compilation tree then
7710              *        the rootElement must be set the jqLite collection of the compile root. This is
7711              *        needed so that the jqLite collection items can be replaced with widgets.
7712              * @param {number=} maxPriority Max directive priority.
7713              * @returns {Function} A composite linking function of all of the matched directives or null.
7714              */
7715             function compileNodes(nodeList, transcludeFn, $rootElement, maxPriority, ignoreDirective,
7716                                     previousCompileContext) {
7717               var linkFns = [],
7718                   attrs, directives, nodeLinkFn, childNodes, childLinkFn, linkFnFound, nodeLinkFnFound;
7719
7720               for (var i = 0; i < nodeList.length; i++) {
7721                 attrs = new Attributes();
7722
7723                 // we must always refer to nodeList[i] since the nodes can be replaced underneath us.
7724                 directives = collectDirectives(nodeList[i], [], attrs, i === 0 ? maxPriority : undefined,
7725                                                 ignoreDirective);
7726
7727                 nodeLinkFn = (directives.length)
7728                     ? applyDirectivesToNode(directives, nodeList[i], attrs, transcludeFn, $rootElement,
7729                                               null, [], [], previousCompileContext)
7730                     : null;
7731
7732                 if (nodeLinkFn && nodeLinkFn.scope) {
7733                   compile.$$addScopeClass(attrs.$$element);
7734                 }
7735
7736                 childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
7737                               !(childNodes = nodeList[i].childNodes) ||
7738                               !childNodes.length)
7739                     ? null
7740                     : compileNodes(childNodes,
7741                          nodeLinkFn ? (
7742                           (nodeLinkFn.transcludeOnThisElement || !nodeLinkFn.templateOnThisElement)
7743                              && nodeLinkFn.transclude) : transcludeFn);
7744
7745                 if (nodeLinkFn || childLinkFn) {
7746                   linkFns.push(i, nodeLinkFn, childLinkFn);
7747                   linkFnFound = true;
7748                   nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
7749                 }
7750
7751                 //use the previous context only for the first element in the virtual group
7752                 previousCompileContext = null;
7753               }
7754
7755               // return a linking function if we have found anything, null otherwise
7756               return linkFnFound ? compositeLinkFn : null;
7757
7758               function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
7759                 var nodeLinkFn, childLinkFn, node, childScope, i, ii, idx, childBoundTranscludeFn;
7760                 var stableNodeList;
7761
7762
7763                 if (nodeLinkFnFound) {
7764                   // copy nodeList so that if a nodeLinkFn removes or adds an element at this DOM level our
7765                   // offsets don't get screwed up
7766                   var nodeListLength = nodeList.length;
7767                   stableNodeList = new Array(nodeListLength);
7768
7769                   // create a sparse array by only copying the elements which have a linkFn
7770                   for (i = 0; i < linkFns.length; i+=3) {
7771                     idx = linkFns[i];
7772                     stableNodeList[idx] = nodeList[idx];
7773                   }
7774                 } else {
7775                   stableNodeList = nodeList;
7776                 }
7777
7778                 for (i = 0, ii = linkFns.length; i < ii;) {
7779                   node = stableNodeList[linkFns[i++]];
7780                   nodeLinkFn = linkFns[i++];
7781                   childLinkFn = linkFns[i++];
7782
7783                   if (nodeLinkFn) {
7784                     if (nodeLinkFn.scope) {
7785                       childScope = scope.$new();
7786                       compile.$$addScopeInfo(jqLite(node), childScope);
7787                     } else {
7788                       childScope = scope;
7789                     }
7790
7791                     if (nodeLinkFn.transcludeOnThisElement) {
7792                       childBoundTranscludeFn = createBoundTranscludeFn(
7793                           scope, nodeLinkFn.transclude, parentBoundTranscludeFn);
7794
7795                     } else if (!nodeLinkFn.templateOnThisElement && parentBoundTranscludeFn) {
7796                       childBoundTranscludeFn = parentBoundTranscludeFn;
7797
7798                     } else if (!parentBoundTranscludeFn && transcludeFn) {
7799                       childBoundTranscludeFn = createBoundTranscludeFn(scope, transcludeFn);
7800
7801                     } else {
7802                       childBoundTranscludeFn = null;
7803                     }
7804
7805                     nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
7806
7807                   } else if (childLinkFn) {
7808                     childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
7809                   }
7810                 }
7811               }
7812             }
7813
7814             function createBoundTranscludeFn(scope, transcludeFn, previousBoundTranscludeFn) {
7815
7816               var boundTranscludeFn = function(transcludedScope, cloneFn, controllers, futureParentElement, containingScope) {
7817
7818                 if (!transcludedScope) {
7819                   transcludedScope = scope.$new(false, containingScope);
7820                   transcludedScope.$$transcluded = true;
7821                 }
7822
7823                 return transcludeFn(transcludedScope, cloneFn, {
7824                   parentBoundTranscludeFn: previousBoundTranscludeFn,
7825                   transcludeControllers: controllers,
7826                   futureParentElement: futureParentElement
7827                 });
7828               };
7829
7830               return boundTranscludeFn;
7831             }
7832
7833             /**
7834              * Looks for directives on the given node and adds them to the directive collection which is
7835              * sorted.
7836              *
7837              * @param node Node to search.
7838              * @param directives An array to which the directives are added to. This array is sorted before
7839              *        the function returns.
7840              * @param attrs The shared attrs object which is used to populate the normalized attributes.
7841              * @param {number=} maxPriority Max directive priority.
7842              */
7843             function collectDirectives(node, directives, attrs, maxPriority, ignoreDirective) {
7844               var nodeType = node.nodeType,
7845                   attrsMap = attrs.$attr,
7846                   match,
7847                   className;
7848
7849               switch (nodeType) {
7850                 case NODE_TYPE_ELEMENT: /* Element */
7851                   // use the node name: <directive>
7852                   addDirective(directives,
7853                       directiveNormalize(nodeName_(node)), 'E', maxPriority, ignoreDirective);
7854
7855                   // iterate over the attributes
7856                   for (var attr, name, nName, ngAttrName, value, isNgAttr, nAttrs = node.attributes,
7857                            j = 0, jj = nAttrs && nAttrs.length; j < jj; j++) {
7858                     var attrStartName = false;
7859                     var attrEndName = false;
7860
7861                     attr = nAttrs[j];
7862                     name = attr.name;
7863                     value = trim(attr.value);
7864
7865                     // support ngAttr attribute binding
7866                     ngAttrName = directiveNormalize(name);
7867                     if (isNgAttr = NG_ATTR_BINDING.test(ngAttrName)) {
7868                       name = name.replace(PREFIX_REGEXP, '')
7869                         .substr(8).replace(/_(.)/g, function(match, letter) {
7870                           return letter.toUpperCase();
7871                         });
7872                     }
7873
7874                     var multiElementMatch = ngAttrName.match(MULTI_ELEMENT_DIR_RE);
7875                     if (multiElementMatch && directiveIsMultiElement(multiElementMatch[1])) {
7876                       attrStartName = name;
7877                       attrEndName = name.substr(0, name.length - 5) + 'end';
7878                       name = name.substr(0, name.length - 6);
7879                     }
7880
7881                     nName = directiveNormalize(name.toLowerCase());
7882                     attrsMap[nName] = name;
7883                     if (isNgAttr || !attrs.hasOwnProperty(nName)) {
7884                         attrs[nName] = value;
7885                         if (getBooleanAttrName(node, nName)) {
7886                           attrs[nName] = true; // presence means true
7887                         }
7888                     }
7889                     addAttrInterpolateDirective(node, directives, value, nName, isNgAttr);
7890                     addDirective(directives, nName, 'A', maxPriority, ignoreDirective, attrStartName,
7891                                   attrEndName);
7892                   }
7893
7894                   // use class as directive
7895                   className = node.className;
7896                   if (isObject(className)) {
7897                       // Maybe SVGAnimatedString
7898                       className = className.animVal;
7899                   }
7900                   if (isString(className) && className !== '') {
7901                     while (match = CLASS_DIRECTIVE_REGEXP.exec(className)) {
7902                       nName = directiveNormalize(match[2]);
7903                       if (addDirective(directives, nName, 'C', maxPriority, ignoreDirective)) {
7904                         attrs[nName] = trim(match[3]);
7905                       }
7906                       className = className.substr(match.index + match[0].length);
7907                     }
7908                   }
7909                   break;
7910                 case NODE_TYPE_TEXT: /* Text Node */
7911                   if (msie === 11) {
7912                     // Workaround for #11781
7913                     while (node.parentNode && node.nextSibling && node.nextSibling.nodeType === NODE_TYPE_TEXT) {
7914                       node.nodeValue = node.nodeValue + node.nextSibling.nodeValue;
7915                       node.parentNode.removeChild(node.nextSibling);
7916                     }
7917                   }
7918                   addTextInterpolateDirective(directives, node.nodeValue);
7919                   break;
7920                 case NODE_TYPE_COMMENT: /* Comment */
7921                   try {
7922                     match = COMMENT_DIRECTIVE_REGEXP.exec(node.nodeValue);
7923                     if (match) {
7924                       nName = directiveNormalize(match[1]);
7925                       if (addDirective(directives, nName, 'M', maxPriority, ignoreDirective)) {
7926                         attrs[nName] = trim(match[2]);
7927                       }
7928                     }
7929                   } catch (e) {
7930                     // turns out that under some circumstances IE9 throws errors when one attempts to read
7931                     // comment's node value.
7932                     // Just ignore it and continue. (Can't seem to reproduce in test case.)
7933                   }
7934                   break;
7935               }
7936
7937               directives.sort(byPriority);
7938               return directives;
7939             }
7940
7941             /**
7942              * Given a node with an directive-start it collects all of the siblings until it finds
7943              * directive-end.
7944              * @param node
7945              * @param attrStart
7946              * @param attrEnd
7947              * @returns {*}
7948              */
7949             function groupScan(node, attrStart, attrEnd) {
7950               var nodes = [];
7951               var depth = 0;
7952               if (attrStart && node.hasAttribute && node.hasAttribute(attrStart)) {
7953                 do {
7954                   if (!node) {
7955                     throw $compileMinErr('uterdir',
7956                               "Unterminated attribute, found '{0}' but no matching '{1}' found.",
7957                               attrStart, attrEnd);
7958                   }
7959                   if (node.nodeType == NODE_TYPE_ELEMENT) {
7960                     if (node.hasAttribute(attrStart)) depth++;
7961                     if (node.hasAttribute(attrEnd)) depth--;
7962                   }
7963                   nodes.push(node);
7964                   node = node.nextSibling;
7965                 } while (depth > 0);
7966               } else {
7967                 nodes.push(node);
7968               }
7969
7970               return jqLite(nodes);
7971             }
7972
7973             /**
7974              * Wrapper for linking function which converts normal linking function into a grouped
7975              * linking function.
7976              * @param linkFn
7977              * @param attrStart
7978              * @param attrEnd
7979              * @returns {Function}
7980              */
7981             function groupElementsLinkFnWrapper(linkFn, attrStart, attrEnd) {
7982               return function(scope, element, attrs, controllers, transcludeFn) {
7983                 element = groupScan(element[0], attrStart, attrEnd);
7984                 return linkFn(scope, element, attrs, controllers, transcludeFn);
7985               };
7986             }
7987
7988             /**
7989              * Once the directives have been collected, their compile functions are executed. This method
7990              * is responsible for inlining directive templates as well as terminating the application
7991              * of the directives if the terminal directive has been reached.
7992              *
7993              * @param {Array} directives Array of collected directives to execute their compile function.
7994              *        this needs to be pre-sorted by priority order.
7995              * @param {Node} compileNode The raw DOM node to apply the compile functions to
7996              * @param {Object} templateAttrs The shared attribute function
7997              * @param {function(angular.Scope, cloneAttachFn=)} transcludeFn A linking function, where the
7998              *                                                  scope argument is auto-generated to the new
7999              *                                                  child of the transcluded parent scope.
8000              * @param {JQLite} jqCollection If we are working on the root of the compile tree then this
8001              *                              argument has the root jqLite array so that we can replace nodes
8002              *                              on it.
8003              * @param {Object=} originalReplaceDirective An optional directive that will be ignored when
8004              *                                           compiling the transclusion.
8005              * @param {Array.<Function>} preLinkFns
8006              * @param {Array.<Function>} postLinkFns
8007              * @param {Object} previousCompileContext Context used for previous compilation of the current
8008              *                                        node
8009              * @returns {Function} linkFn
8010              */
8011             function applyDirectivesToNode(directives, compileNode, templateAttrs, transcludeFn,
8012                                            jqCollection, originalReplaceDirective, preLinkFns, postLinkFns,
8013                                            previousCompileContext) {
8014               previousCompileContext = previousCompileContext || {};
8015
8016               var terminalPriority = -Number.MAX_VALUE,
8017                   newScopeDirective = previousCompileContext.newScopeDirective,
8018                   controllerDirectives = previousCompileContext.controllerDirectives,
8019                   newIsolateScopeDirective = previousCompileContext.newIsolateScopeDirective,
8020                   templateDirective = previousCompileContext.templateDirective,
8021                   nonTlbTranscludeDirective = previousCompileContext.nonTlbTranscludeDirective,
8022                   hasTranscludeDirective = false,
8023                   hasTemplate = false,
8024                   hasElementTranscludeDirective = previousCompileContext.hasElementTranscludeDirective,
8025                   $compileNode = templateAttrs.$$element = jqLite(compileNode),
8026                   directive,
8027                   directiveName,
8028                   $template,
8029                   replaceDirective = originalReplaceDirective,
8030                   childTranscludeFn = transcludeFn,
8031                   linkFn,
8032                   directiveValue;
8033
8034               // executes all directives on the current element
8035               for (var i = 0, ii = directives.length; i < ii; i++) {
8036                 directive = directives[i];
8037                 var attrStart = directive.$$start;
8038                 var attrEnd = directive.$$end;
8039
8040                 // collect multiblock sections
8041                 if (attrStart) {
8042                   $compileNode = groupScan(compileNode, attrStart, attrEnd);
8043                 }
8044                 $template = undefined;
8045
8046                 if (terminalPriority > directive.priority) {
8047                   break; // prevent further processing of directives
8048                 }
8049
8050                 if (directiveValue = directive.scope) {
8051
8052                   // skip the check for directives with async templates, we'll check the derived sync
8053                   // directive when the template arrives
8054                   if (!directive.templateUrl) {
8055                     if (isObject(directiveValue)) {
8056                       // This directive is trying to add an isolated scope.
8057                       // Check that there is no scope of any kind already
8058                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective || newScopeDirective,
8059                                         directive, $compileNode);
8060                       newIsolateScopeDirective = directive;
8061                     } else {
8062                       // This directive is trying to add a child scope.
8063                       // Check that there is no isolated scope already
8064                       assertNoDuplicate('new/isolated scope', newIsolateScopeDirective, directive,
8065                                         $compileNode);
8066                     }
8067                   }
8068
8069                   newScopeDirective = newScopeDirective || directive;
8070                 }
8071
8072                 directiveName = directive.name;
8073
8074                 if (!directive.templateUrl && directive.controller) {
8075                   directiveValue = directive.controller;
8076                   controllerDirectives = controllerDirectives || createMap();
8077                   assertNoDuplicate("'" + directiveName + "' controller",
8078                       controllerDirectives[directiveName], directive, $compileNode);
8079                   controllerDirectives[directiveName] = directive;
8080                 }
8081
8082                 if (directiveValue = directive.transclude) {
8083                   hasTranscludeDirective = true;
8084
8085                   // Special case ngIf and ngRepeat so that we don't complain about duplicate transclusion.
8086                   // This option should only be used by directives that know how to safely handle element transclusion,
8087                   // where the transcluded nodes are added or replaced after linking.
8088                   if (!directive.$$tlb) {
8089                     assertNoDuplicate('transclusion', nonTlbTranscludeDirective, directive, $compileNode);
8090                     nonTlbTranscludeDirective = directive;
8091                   }
8092
8093                   if (directiveValue == 'element') {
8094                     hasElementTranscludeDirective = true;
8095                     terminalPriority = directive.priority;
8096                     $template = $compileNode;
8097                     $compileNode = templateAttrs.$$element =
8098                         jqLite(document.createComment(' ' + directiveName + ': ' +
8099                                                       templateAttrs[directiveName] + ' '));
8100                     compileNode = $compileNode[0];
8101                     replaceWith(jqCollection, sliceArgs($template), compileNode);
8102
8103                     childTranscludeFn = compile($template, transcludeFn, terminalPriority,
8104                                                 replaceDirective && replaceDirective.name, {
8105                                                   // Don't pass in:
8106                                                   // - controllerDirectives - otherwise we'll create duplicates controllers
8107                                                   // - newIsolateScopeDirective or templateDirective - combining templates with
8108                                                   //   element transclusion doesn't make sense.
8109                                                   //
8110                                                   // We need only nonTlbTranscludeDirective so that we prevent putting transclusion
8111                                                   // on the same element more than once.
8112                                                   nonTlbTranscludeDirective: nonTlbTranscludeDirective
8113                                                 });
8114                   } else {
8115                     $template = jqLite(jqLiteClone(compileNode)).contents();
8116                     $compileNode.empty(); // clear contents
8117                     childTranscludeFn = compile($template, transcludeFn, undefined,
8118                         undefined, { needsNewScope: directive.$$isolateScope || directive.$$newScope});
8119                   }
8120                 }
8121
8122                 if (directive.template) {
8123                   hasTemplate = true;
8124                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8125                   templateDirective = directive;
8126
8127                   directiveValue = (isFunction(directive.template))
8128                       ? directive.template($compileNode, templateAttrs)
8129                       : directive.template;
8130
8131                   directiveValue = denormalizeTemplate(directiveValue);
8132
8133                   if (directive.replace) {
8134                     replaceDirective = directive;
8135                     if (jqLiteIsTextNode(directiveValue)) {
8136                       $template = [];
8137                     } else {
8138                       $template = removeComments(wrapTemplate(directive.templateNamespace, trim(directiveValue)));
8139                     }
8140                     compileNode = $template[0];
8141
8142                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8143                       throw $compileMinErr('tplrt',
8144                           "Template for directive '{0}' must have exactly one root element. {1}",
8145                           directiveName, '');
8146                     }
8147
8148                     replaceWith(jqCollection, $compileNode, compileNode);
8149
8150                     var newTemplateAttrs = {$attr: {}};
8151
8152                     // combine directives from the original node and from the template:
8153                     // - take the array of directives for this element
8154                     // - split it into two parts, those that already applied (processed) and those that weren't (unprocessed)
8155                     // - collect directives from the template and sort them by priority
8156                     // - combine directives as: processed + template + unprocessed
8157                     var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
8158                     var unprocessedDirectives = directives.splice(i + 1, directives.length - (i + 1));
8159
8160                     if (newIsolateScopeDirective || newScopeDirective) {
8161                       // The original directive caused the current element to be replaced but this element
8162                       // also needs to have a new scope, so we need to tell the template directives
8163                       // that they would need to get their scope from further up, if they require transclusion
8164                       markDirectiveScope(templateDirectives, newIsolateScopeDirective, newScopeDirective);
8165                     }
8166                     directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
8167                     mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
8168
8169                     ii = directives.length;
8170                   } else {
8171                     $compileNode.html(directiveValue);
8172                   }
8173                 }
8174
8175                 if (directive.templateUrl) {
8176                   hasTemplate = true;
8177                   assertNoDuplicate('template', templateDirective, directive, $compileNode);
8178                   templateDirective = directive;
8179
8180                   if (directive.replace) {
8181                     replaceDirective = directive;
8182                   }
8183
8184                   nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
8185                       templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
8186                         controllerDirectives: controllerDirectives,
8187                         newScopeDirective: (newScopeDirective !== directive) && newScopeDirective,
8188                         newIsolateScopeDirective: newIsolateScopeDirective,
8189                         templateDirective: templateDirective,
8190                         nonTlbTranscludeDirective: nonTlbTranscludeDirective
8191                       });
8192                   ii = directives.length;
8193                 } else if (directive.compile) {
8194                   try {
8195                     linkFn = directive.compile($compileNode, templateAttrs, childTranscludeFn);
8196                     if (isFunction(linkFn)) {
8197                       addLinkFns(null, linkFn, attrStart, attrEnd);
8198                     } else if (linkFn) {
8199                       addLinkFns(linkFn.pre, linkFn.post, attrStart, attrEnd);
8200                     }
8201                   } catch (e) {
8202                     $exceptionHandler(e, startingTag($compileNode));
8203                   }
8204                 }
8205
8206                 if (directive.terminal) {
8207                   nodeLinkFn.terminal = true;
8208                   terminalPriority = Math.max(terminalPriority, directive.priority);
8209                 }
8210
8211               }
8212
8213               nodeLinkFn.scope = newScopeDirective && newScopeDirective.scope === true;
8214               nodeLinkFn.transcludeOnThisElement = hasTranscludeDirective;
8215               nodeLinkFn.templateOnThisElement = hasTemplate;
8216               nodeLinkFn.transclude = childTranscludeFn;
8217
8218               previousCompileContext.hasElementTranscludeDirective = hasElementTranscludeDirective;
8219
8220               // might be normal or delayed nodeLinkFn depending on if templateUrl is present
8221               return nodeLinkFn;
8222
8223               ////////////////////
8224
8225               function addLinkFns(pre, post, attrStart, attrEnd) {
8226                 if (pre) {
8227                   if (attrStart) pre = groupElementsLinkFnWrapper(pre, attrStart, attrEnd);
8228                   pre.require = directive.require;
8229                   pre.directiveName = directiveName;
8230                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8231                     pre = cloneAndAnnotateFn(pre, {isolateScope: true});
8232                   }
8233                   preLinkFns.push(pre);
8234                 }
8235                 if (post) {
8236                   if (attrStart) post = groupElementsLinkFnWrapper(post, attrStart, attrEnd);
8237                   post.require = directive.require;
8238                   post.directiveName = directiveName;
8239                   if (newIsolateScopeDirective === directive || directive.$$isolateScope) {
8240                     post = cloneAndAnnotateFn(post, {isolateScope: true});
8241                   }
8242                   postLinkFns.push(post);
8243                 }
8244               }
8245
8246
8247               function getControllers(directiveName, require, $element, elementControllers) {
8248                 var value;
8249
8250                 if (isString(require)) {
8251                   var match = require.match(REQUIRE_PREFIX_REGEXP);
8252                   var name = require.substring(match[0].length);
8253                   var inheritType = match[1] || match[3];
8254                   var optional = match[2] === '?';
8255
8256                   //If only parents then start at the parent element
8257                   if (inheritType === '^^') {
8258                     $element = $element.parent();
8259                   //Otherwise attempt getting the controller from elementControllers in case
8260                   //the element is transcluded (and has no data) and to avoid .data if possible
8261                   } else {
8262                     value = elementControllers && elementControllers[name];
8263                     value = value && value.instance;
8264                   }
8265
8266                   if (!value) {
8267                     var dataName = '$' + name + 'Controller';
8268                     value = inheritType ? $element.inheritedData(dataName) : $element.data(dataName);
8269                   }
8270
8271                   if (!value && !optional) {
8272                     throw $compileMinErr('ctreq',
8273                         "Controller '{0}', required by directive '{1}', can't be found!",
8274                         name, directiveName);
8275                   }
8276                 } else if (isArray(require)) {
8277                   value = [];
8278                   for (var i = 0, ii = require.length; i < ii; i++) {
8279                     value[i] = getControllers(directiveName, require[i], $element, elementControllers);
8280                   }
8281                 }
8282
8283                 return value || null;
8284               }
8285
8286               function setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope) {
8287                 var elementControllers = createMap();
8288                 for (var controllerKey in controllerDirectives) {
8289                   var directive = controllerDirectives[controllerKey];
8290                   var locals = {
8291                     $scope: directive === newIsolateScopeDirective || directive.$$isolateScope ? isolateScope : scope,
8292                     $element: $element,
8293                     $attrs: attrs,
8294                     $transclude: transcludeFn
8295                   };
8296
8297                   var controller = directive.controller;
8298                   if (controller == '@') {
8299                     controller = attrs[directive.name];
8300                   }
8301
8302                   var controllerInstance = $controller(controller, locals, true, directive.controllerAs);
8303
8304                   // For directives with element transclusion the element is a comment,
8305                   // but jQuery .data doesn't support attaching data to comment nodes as it's hard to
8306                   // clean up (http://bugs.jquery.com/ticket/8335).
8307                   // Instead, we save the controllers for the element in a local hash and attach to .data
8308                   // later, once we have the actual element.
8309                   elementControllers[directive.name] = controllerInstance;
8310                   if (!hasElementTranscludeDirective) {
8311                     $element.data('$' + directive.name + 'Controller', controllerInstance.instance);
8312                   }
8313                 }
8314                 return elementControllers;
8315               }
8316
8317               function nodeLinkFn(childLinkFn, scope, linkNode, $rootElement, boundTranscludeFn) {
8318                 var linkFn, isolateScope, controllerScope, elementControllers, transcludeFn, $element,
8319                     attrs, removeScopeBindingWatches, removeControllerBindingWatches;
8320
8321                 if (compileNode === linkNode) {
8322                   attrs = templateAttrs;
8323                   $element = templateAttrs.$$element;
8324                 } else {
8325                   $element = jqLite(linkNode);
8326                   attrs = new Attributes($element, templateAttrs);
8327                 }
8328
8329                 controllerScope = scope;
8330                 if (newIsolateScopeDirective) {
8331                   isolateScope = scope.$new(true);
8332                 } else if (newScopeDirective) {
8333                   controllerScope = scope.$parent;
8334                 }
8335
8336                 if (boundTranscludeFn) {
8337                   // track `boundTranscludeFn` so it can be unwrapped if `transcludeFn`
8338                   // is later passed as `parentBoundTranscludeFn` to `publicLinkFn`
8339                   transcludeFn = controllersBoundTransclude;
8340                   transcludeFn.$$boundTransclude = boundTranscludeFn;
8341                 }
8342
8343                 if (controllerDirectives) {
8344                   elementControllers = setupControllers($element, attrs, transcludeFn, controllerDirectives, isolateScope, scope);
8345                 }
8346
8347                 if (newIsolateScopeDirective) {
8348                   // Initialize isolate scope bindings for new isolate scope directive.
8349                   compile.$$addScopeInfo($element, isolateScope, true, !(templateDirective && (templateDirective === newIsolateScopeDirective ||
8350                       templateDirective === newIsolateScopeDirective.$$originalDirective)));
8351                   compile.$$addScopeClass($element, true);
8352                   isolateScope.$$isolateBindings =
8353                       newIsolateScopeDirective.$$isolateBindings;
8354                   removeScopeBindingWatches = initializeDirectiveBindings(scope, attrs, isolateScope,
8355                                                 isolateScope.$$isolateBindings,
8356                                                 newIsolateScopeDirective);
8357                   if (removeScopeBindingWatches) {
8358                     isolateScope.$on('$destroy', removeScopeBindingWatches);
8359                   }
8360                 }
8361
8362                 // Initialize bindToController bindings
8363                 for (var name in elementControllers) {
8364                   var controllerDirective = controllerDirectives[name];
8365                   var controller = elementControllers[name];
8366                   var bindings = controllerDirective.$$bindings.bindToController;
8367
8368                   if (controller.identifier && bindings) {
8369                     removeControllerBindingWatches =
8370                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8371                   }
8372
8373                   var controllerResult = controller();
8374                   if (controllerResult !== controller.instance) {
8375                     // If the controller constructor has a return value, overwrite the instance
8376                     // from setupControllers
8377                     controller.instance = controllerResult;
8378                     $element.data('$' + controllerDirective.name + 'Controller', controllerResult);
8379                     removeControllerBindingWatches && removeControllerBindingWatches();
8380                     removeControllerBindingWatches =
8381                       initializeDirectiveBindings(controllerScope, attrs, controller.instance, bindings, controllerDirective);
8382                   }
8383                 }
8384
8385                 // PRELINKING
8386                 for (i = 0, ii = preLinkFns.length; i < ii; i++) {
8387                   linkFn = preLinkFns[i];
8388                   invokeLinkFn(linkFn,
8389                       linkFn.isolateScope ? isolateScope : scope,
8390                       $element,
8391                       attrs,
8392                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8393                       transcludeFn
8394                   );
8395                 }
8396
8397                 // RECURSION
8398                 // We only pass the isolate scope, if the isolate directive has a template,
8399                 // otherwise the child elements do not belong to the isolate directive.
8400                 var scopeToChild = scope;
8401                 if (newIsolateScopeDirective && (newIsolateScopeDirective.template || newIsolateScopeDirective.templateUrl === null)) {
8402                   scopeToChild = isolateScope;
8403                 }
8404                 childLinkFn && childLinkFn(scopeToChild, linkNode.childNodes, undefined, boundTranscludeFn);
8405
8406                 // POSTLINKING
8407                 for (i = postLinkFns.length - 1; i >= 0; i--) {
8408                   linkFn = postLinkFns[i];
8409                   invokeLinkFn(linkFn,
8410                       linkFn.isolateScope ? isolateScope : scope,
8411                       $element,
8412                       attrs,
8413                       linkFn.require && getControllers(linkFn.directiveName, linkFn.require, $element, elementControllers),
8414                       transcludeFn
8415                   );
8416                 }
8417
8418                 // This is the function that is injected as `$transclude`.
8419                 // Note: all arguments are optional!
8420                 function controllersBoundTransclude(scope, cloneAttachFn, futureParentElement) {
8421                   var transcludeControllers;
8422
8423                   // No scope passed in:
8424                   if (!isScope(scope)) {
8425                     futureParentElement = cloneAttachFn;
8426                     cloneAttachFn = scope;
8427                     scope = undefined;
8428                   }
8429
8430                   if (hasElementTranscludeDirective) {
8431                     transcludeControllers = elementControllers;
8432                   }
8433                   if (!futureParentElement) {
8434                     futureParentElement = hasElementTranscludeDirective ? $element.parent() : $element;
8435                   }
8436                   return boundTranscludeFn(scope, cloneAttachFn, transcludeControllers, futureParentElement, scopeToChild);
8437                 }
8438               }
8439             }
8440
8441             // Depending upon the context in which a directive finds itself it might need to have a new isolated
8442             // or child scope created. For instance:
8443             // * if the directive has been pulled into a template because another directive with a higher priority
8444             // asked for element transclusion
8445             // * if the directive itself asks for transclusion but it is at the root of a template and the original
8446             // element was replaced. See https://github.com/angular/angular.js/issues/12936
8447             function markDirectiveScope(directives, isolateScope, newScope) {
8448               for (var j = 0, jj = directives.length; j < jj; j++) {
8449                 directives[j] = inherit(directives[j], {$$isolateScope: isolateScope, $$newScope: newScope});
8450               }
8451             }
8452
8453             /**
8454              * looks up the directive and decorates it with exception handling and proper parameters. We
8455              * call this the boundDirective.
8456              *
8457              * @param {string} name name of the directive to look up.
8458              * @param {string} location The directive must be found in specific format.
8459              *   String containing any of theses characters:
8460              *
8461              *   * `E`: element name
8462              *   * `A': attribute
8463              *   * `C`: class
8464              *   * `M`: comment
8465              * @returns {boolean} true if directive was added.
8466              */
8467             function addDirective(tDirectives, name, location, maxPriority, ignoreDirective, startAttrName,
8468                                   endAttrName) {
8469               if (name === ignoreDirective) return null;
8470               var match = null;
8471               if (hasDirectives.hasOwnProperty(name)) {
8472                 for (var directive, directives = $injector.get(name + Suffix),
8473                     i = 0, ii = directives.length; i < ii; i++) {
8474                   try {
8475                     directive = directives[i];
8476                     if ((isUndefined(maxPriority) || maxPriority > directive.priority) &&
8477                          directive.restrict.indexOf(location) != -1) {
8478                       if (startAttrName) {
8479                         directive = inherit(directive, {$$start: startAttrName, $$end: endAttrName});
8480                       }
8481                       tDirectives.push(directive);
8482                       match = directive;
8483                     }
8484                   } catch (e) { $exceptionHandler(e); }
8485                 }
8486               }
8487               return match;
8488             }
8489
8490
8491             /**
8492              * looks up the directive and returns true if it is a multi-element directive,
8493              * and therefore requires DOM nodes between -start and -end markers to be grouped
8494              * together.
8495              *
8496              * @param {string} name name of the directive to look up.
8497              * @returns true if directive was registered as multi-element.
8498              */
8499             function directiveIsMultiElement(name) {
8500               if (hasDirectives.hasOwnProperty(name)) {
8501                 for (var directive, directives = $injector.get(name + Suffix),
8502                     i = 0, ii = directives.length; i < ii; i++) {
8503                   directive = directives[i];
8504                   if (directive.multiElement) {
8505                     return true;
8506                   }
8507                 }
8508               }
8509               return false;
8510             }
8511
8512             /**
8513              * When the element is replaced with HTML template then the new attributes
8514              * on the template need to be merged with the existing attributes in the DOM.
8515              * The desired effect is to have both of the attributes present.
8516              *
8517              * @param {object} dst destination attributes (original DOM)
8518              * @param {object} src source attributes (from the directive template)
8519              */
8520             function mergeTemplateAttributes(dst, src) {
8521               var srcAttr = src.$attr,
8522                   dstAttr = dst.$attr,
8523                   $element = dst.$$element;
8524
8525               // reapply the old attributes to the new element
8526               forEach(dst, function(value, key) {
8527                 if (key.charAt(0) != '$') {
8528                   if (src[key] && src[key] !== value) {
8529                     value += (key === 'style' ? ';' : ' ') + src[key];
8530                   }
8531                   dst.$set(key, value, true, srcAttr[key]);
8532                 }
8533               });
8534
8535               // copy the new attributes on the old attrs object
8536               forEach(src, function(value, key) {
8537                 if (key == 'class') {
8538                   safeAddClass($element, value);
8539                   dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
8540                 } else if (key == 'style') {
8541                   $element.attr('style', $element.attr('style') + ';' + value);
8542                   dst['style'] = (dst['style'] ? dst['style'] + ';' : '') + value;
8543                   // `dst` will never contain hasOwnProperty as DOM parser won't let it.
8544                   // You will get an "InvalidCharacterError: DOM Exception 5" error if you
8545                   // have an attribute like "has-own-property" or "data-has-own-property", etc.
8546                 } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
8547                   dst[key] = value;
8548                   dstAttr[key] = srcAttr[key];
8549                 }
8550               });
8551             }
8552
8553
8554             function compileTemplateUrl(directives, $compileNode, tAttrs,
8555                 $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
8556               var linkQueue = [],
8557                   afterTemplateNodeLinkFn,
8558                   afterTemplateChildLinkFn,
8559                   beforeTemplateCompileNode = $compileNode[0],
8560                   origAsyncDirective = directives.shift(),
8561                   derivedSyncDirective = inherit(origAsyncDirective, {
8562                     templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
8563                   }),
8564                   templateUrl = (isFunction(origAsyncDirective.templateUrl))
8565                       ? origAsyncDirective.templateUrl($compileNode, tAttrs)
8566                       : origAsyncDirective.templateUrl,
8567                   templateNamespace = origAsyncDirective.templateNamespace;
8568
8569               $compileNode.empty();
8570
8571               $templateRequest(templateUrl)
8572                 .then(function(content) {
8573                   var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
8574
8575                   content = denormalizeTemplate(content);
8576
8577                   if (origAsyncDirective.replace) {
8578                     if (jqLiteIsTextNode(content)) {
8579                       $template = [];
8580                     } else {
8581                       $template = removeComments(wrapTemplate(templateNamespace, trim(content)));
8582                     }
8583                     compileNode = $template[0];
8584
8585                     if ($template.length != 1 || compileNode.nodeType !== NODE_TYPE_ELEMENT) {
8586                       throw $compileMinErr('tplrt',
8587                           "Template for directive '{0}' must have exactly one root element. {1}",
8588                           origAsyncDirective.name, templateUrl);
8589                     }
8590
8591                     tempTemplateAttrs = {$attr: {}};
8592                     replaceWith($rootElement, $compileNode, compileNode);
8593                     var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
8594
8595                     if (isObject(origAsyncDirective.scope)) {
8596                       // the original directive that caused the template to be loaded async required
8597                       // an isolate scope
8598                       markDirectiveScope(templateDirectives, true);
8599                     }
8600                     directives = templateDirectives.concat(directives);
8601                     mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
8602                   } else {
8603                     compileNode = beforeTemplateCompileNode;
8604                     $compileNode.html(content);
8605                   }
8606
8607                   directives.unshift(derivedSyncDirective);
8608
8609                   afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
8610                       childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
8611                       previousCompileContext);
8612                   forEach($rootElement, function(node, i) {
8613                     if (node == compileNode) {
8614                       $rootElement[i] = $compileNode[0];
8615                     }
8616                   });
8617                   afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
8618
8619                   while (linkQueue.length) {
8620                     var scope = linkQueue.shift(),
8621                         beforeTemplateLinkNode = linkQueue.shift(),
8622                         linkRootElement = linkQueue.shift(),
8623                         boundTranscludeFn = linkQueue.shift(),
8624                         linkNode = $compileNode[0];
8625
8626                     if (scope.$$destroyed) continue;
8627
8628                     if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
8629                       var oldClasses = beforeTemplateLinkNode.className;
8630
8631                       if (!(previousCompileContext.hasElementTranscludeDirective &&
8632                           origAsyncDirective.replace)) {
8633                         // it was cloned therefore we have to clone as well.
8634                         linkNode = jqLiteClone(compileNode);
8635                       }
8636                       replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
8637
8638                       // Copy in CSS classes from original node
8639                       safeAddClass(jqLite(linkNode), oldClasses);
8640                     }
8641                     if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8642                       childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8643                     } else {
8644                       childBoundTranscludeFn = boundTranscludeFn;
8645                     }
8646                     afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
8647                       childBoundTranscludeFn);
8648                   }
8649                   linkQueue = null;
8650                 });
8651
8652               return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
8653                 var childBoundTranscludeFn = boundTranscludeFn;
8654                 if (scope.$$destroyed) return;
8655                 if (linkQueue) {
8656                   linkQueue.push(scope,
8657                                  node,
8658                                  rootElement,
8659                                  childBoundTranscludeFn);
8660                 } else {
8661                   if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
8662                     childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
8663                   }
8664                   afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
8665                 }
8666               };
8667             }
8668
8669
8670             /**
8671              * Sorting function for bound directives.
8672              */
8673             function byPriority(a, b) {
8674               var diff = b.priority - a.priority;
8675               if (diff !== 0) return diff;
8676               if (a.name !== b.name) return (a.name < b.name) ? -1 : 1;
8677               return a.index - b.index;
8678             }
8679
8680             function assertNoDuplicate(what, previousDirective, directive, element) {
8681
8682               function wrapModuleNameIfDefined(moduleName) {
8683                 return moduleName ?
8684                   (' (module: ' + moduleName + ')') :
8685                   '';
8686               }
8687
8688               if (previousDirective) {
8689                 throw $compileMinErr('multidir', 'Multiple directives [{0}{1}, {2}{3}] asking for {4} on: {5}',
8690                     previousDirective.name, wrapModuleNameIfDefined(previousDirective.$$moduleName),
8691                     directive.name, wrapModuleNameIfDefined(directive.$$moduleName), what, startingTag(element));
8692               }
8693             }
8694
8695
8696             function addTextInterpolateDirective(directives, text) {
8697               var interpolateFn = $interpolate(text, true);
8698               if (interpolateFn) {
8699                 directives.push({
8700                   priority: 0,
8701                   compile: function textInterpolateCompileFn(templateNode) {
8702                     var templateNodeParent = templateNode.parent(),
8703                         hasCompileParent = !!templateNodeParent.length;
8704
8705                     // When transcluding a template that has bindings in the root
8706                     // we don't have a parent and thus need to add the class during linking fn.
8707                     if (hasCompileParent) compile.$$addBindingClass(templateNodeParent);
8708
8709                     return function textInterpolateLinkFn(scope, node) {
8710                       var parent = node.parent();
8711                       if (!hasCompileParent) compile.$$addBindingClass(parent);
8712                       compile.$$addBindingInfo(parent, interpolateFn.expressions);
8713                       scope.$watch(interpolateFn, function interpolateFnWatchAction(value) {
8714                         node[0].nodeValue = value;
8715                       });
8716                     };
8717                   }
8718                 });
8719               }
8720             }
8721
8722
8723             function wrapTemplate(type, template) {
8724               type = lowercase(type || 'html');
8725               switch (type) {
8726               case 'svg':
8727               case 'math':
8728                 var wrapper = document.createElement('div');
8729                 wrapper.innerHTML = '<' + type + '>' + template + '</' + type + '>';
8730                 return wrapper.childNodes[0].childNodes;
8731               default:
8732                 return template;
8733               }
8734             }
8735
8736
8737             function getTrustedContext(node, attrNormalizedName) {
8738               if (attrNormalizedName == "srcdoc") {
8739                 return $sce.HTML;
8740               }
8741               var tag = nodeName_(node);
8742               // maction[xlink:href] can source SVG.  It's not limited to <maction>.
8743               if (attrNormalizedName == "xlinkHref" ||
8744                   (tag == "form" && attrNormalizedName == "action") ||
8745                   (tag != "img" && (attrNormalizedName == "src" ||
8746                                     attrNormalizedName == "ngSrc"))) {
8747                 return $sce.RESOURCE_URL;
8748               }
8749             }
8750
8751
8752             function addAttrInterpolateDirective(node, directives, value, name, allOrNothing) {
8753               var trustedContext = getTrustedContext(node, name);
8754               allOrNothing = ALL_OR_NOTHING_ATTRS[name] || allOrNothing;
8755
8756               var interpolateFn = $interpolate(value, true, trustedContext, allOrNothing);
8757
8758               // no interpolation found -> ignore
8759               if (!interpolateFn) return;
8760
8761
8762               if (name === "multiple" && nodeName_(node) === "select") {
8763                 throw $compileMinErr("selmulti",
8764                     "Binding to the 'multiple' attribute is not supported. Element: {0}",
8765                     startingTag(node));
8766               }
8767
8768               directives.push({
8769                 priority: 100,
8770                 compile: function() {
8771                     return {
8772                       pre: function attrInterpolatePreLinkFn(scope, element, attr) {
8773                         var $$observers = (attr.$$observers || (attr.$$observers = createMap()));
8774
8775                         if (EVENT_HANDLER_ATTR_REGEXP.test(name)) {
8776                           throw $compileMinErr('nodomevents',
8777                               "Interpolations for HTML DOM event attributes are disallowed.  Please use the " +
8778                                   "ng- versions (such as ng-click instead of onclick) instead.");
8779                         }
8780
8781                         // If the attribute has changed since last $interpolate()ed
8782                         var newValue = attr[name];
8783                         if (newValue !== value) {
8784                           // we need to interpolate again since the attribute value has been updated
8785                           // (e.g. by another directive's compile function)
8786                           // ensure unset/empty values make interpolateFn falsy
8787                           interpolateFn = newValue && $interpolate(newValue, true, trustedContext, allOrNothing);
8788                           value = newValue;
8789                         }
8790
8791                         // if attribute was updated so that there is no interpolation going on we don't want to
8792                         // register any observers
8793                         if (!interpolateFn) return;
8794
8795                         // initialize attr object so that it's ready in case we need the value for isolate
8796                         // scope initialization, otherwise the value would not be available from isolate
8797                         // directive's linking fn during linking phase
8798                         attr[name] = interpolateFn(scope);
8799
8800                         ($$observers[name] || ($$observers[name] = [])).$$inter = true;
8801                         (attr.$$observers && attr.$$observers[name].$$scope || scope).
8802                           $watch(interpolateFn, function interpolateFnWatchAction(newValue, oldValue) {
8803                             //special case for class attribute addition + removal
8804                             //so that class changes can tap into the animation
8805                             //hooks provided by the $animate service. Be sure to
8806                             //skip animations when the first digest occurs (when
8807                             //both the new and the old values are the same) since
8808                             //the CSS classes are the non-interpolated values
8809                             if (name === 'class' && newValue != oldValue) {
8810                               attr.$updateClass(newValue, oldValue);
8811                             } else {
8812                               attr.$set(name, newValue);
8813                             }
8814                           });
8815                       }
8816                     };
8817                   }
8818               });
8819             }
8820
8821
8822             /**
8823              * This is a special jqLite.replaceWith, which can replace items which
8824              * have no parents, provided that the containing jqLite collection is provided.
8825              *
8826              * @param {JqLite=} $rootElement The root of the compile tree. Used so that we can replace nodes
8827              *                               in the root of the tree.
8828              * @param {JqLite} elementsToRemove The jqLite element which we are going to replace. We keep
8829              *                                  the shell, but replace its DOM node reference.
8830              * @param {Node} newNode The new DOM node.
8831              */
8832             function replaceWith($rootElement, elementsToRemove, newNode) {
8833               var firstElementToRemove = elementsToRemove[0],
8834                   removeCount = elementsToRemove.length,
8835                   parent = firstElementToRemove.parentNode,
8836                   i, ii;
8837
8838               if ($rootElement) {
8839                 for (i = 0, ii = $rootElement.length; i < ii; i++) {
8840                   if ($rootElement[i] == firstElementToRemove) {
8841                     $rootElement[i++] = newNode;
8842                     for (var j = i, j2 = j + removeCount - 1,
8843                              jj = $rootElement.length;
8844                          j < jj; j++, j2++) {
8845                       if (j2 < jj) {
8846                         $rootElement[j] = $rootElement[j2];
8847                       } else {
8848                         delete $rootElement[j];
8849                       }
8850                     }
8851                     $rootElement.length -= removeCount - 1;
8852
8853                     // If the replaced element is also the jQuery .context then replace it
8854                     // .context is a deprecated jQuery api, so we should set it only when jQuery set it
8855                     // http://api.jquery.com/context/
8856                     if ($rootElement.context === firstElementToRemove) {
8857                       $rootElement.context = newNode;
8858                     }
8859                     break;
8860                   }
8861                 }
8862               }
8863
8864               if (parent) {
8865                 parent.replaceChild(newNode, firstElementToRemove);
8866               }
8867
8868               // TODO(perf): what's this document fragment for? is it needed? can we at least reuse it?
8869               var fragment = document.createDocumentFragment();
8870               fragment.appendChild(firstElementToRemove);
8871
8872               if (jqLite.hasData(firstElementToRemove)) {
8873                 // Copy over user data (that includes Angular's $scope etc.). Don't copy private
8874                 // data here because there's no public interface in jQuery to do that and copying over
8875                 // event listeners (which is the main use of private data) wouldn't work anyway.
8876                 jqLite.data(newNode, jqLite.data(firstElementToRemove));
8877
8878                 // Remove data of the replaced element. We cannot just call .remove()
8879                 // on the element it since that would deallocate scope that is needed
8880                 // for the new node. Instead, remove the data "manually".
8881                 if (!jQuery) {
8882                   delete jqLite.cache[firstElementToRemove[jqLite.expando]];
8883                 } else {
8884                   // jQuery 2.x doesn't expose the data storage. Use jQuery.cleanData to clean up after
8885                   // the replaced element. The cleanData version monkey-patched by Angular would cause
8886                   // the scope to be trashed and we do need the very same scope to work with the new
8887                   // element. However, we cannot just cache the non-patched version and use it here as
8888                   // that would break if another library patches the method after Angular does (one
8889                   // example is jQuery UI). Instead, set a flag indicating scope destroying should be
8890                   // skipped this one time.
8891                   skipDestroyOnNextJQueryCleanData = true;
8892                   jQuery.cleanData([firstElementToRemove]);
8893                 }
8894               }
8895
8896               for (var k = 1, kk = elementsToRemove.length; k < kk; k++) {
8897                 var element = elementsToRemove[k];
8898                 jqLite(element).remove(); // must do this way to clean up expando
8899                 fragment.appendChild(element);
8900                 delete elementsToRemove[k];
8901               }
8902
8903               elementsToRemove[0] = newNode;
8904               elementsToRemove.length = 1;
8905             }
8906
8907
8908             function cloneAndAnnotateFn(fn, annotation) {
8909               return extend(function() { return fn.apply(null, arguments); }, fn, annotation);
8910             }
8911
8912
8913             function invokeLinkFn(linkFn, scope, $element, attrs, controllers, transcludeFn) {
8914               try {
8915                 linkFn(scope, $element, attrs, controllers, transcludeFn);
8916               } catch (e) {
8917                 $exceptionHandler(e, startingTag($element));
8918               }
8919             }
8920
8921
8922             // Set up $watches for isolate scope and controller bindings. This process
8923             // only occurs for isolate scopes and new scopes with controllerAs.
8924             function initializeDirectiveBindings(scope, attrs, destination, bindings, directive) {
8925               var removeWatchCollection = [];
8926               forEach(bindings, function(definition, scopeName) {
8927                 var attrName = definition.attrName,
8928                 optional = definition.optional,
8929                 mode = definition.mode, // @, =, or &
8930                 lastValue,
8931                 parentGet, parentSet, compare;
8932
8933                 switch (mode) {
8934
8935                   case '@':
8936                     if (!optional && !hasOwnProperty.call(attrs, attrName)) {
8937                       destination[scopeName] = attrs[attrName] = void 0;
8938                     }
8939                     attrs.$observe(attrName, function(value) {
8940                       if (isString(value)) {
8941                         destination[scopeName] = value;
8942                       }
8943                     });
8944                     attrs.$$observers[attrName].$$scope = scope;
8945                     if (isString(attrs[attrName])) {
8946                       // If the attribute has been provided then we trigger an interpolation to ensure
8947                       // the value is there for use in the link fn
8948                       destination[scopeName] = $interpolate(attrs[attrName])(scope);
8949                     }
8950                     break;
8951
8952                   case '=':
8953                     if (!hasOwnProperty.call(attrs, attrName)) {
8954                       if (optional) break;
8955                       attrs[attrName] = void 0;
8956                     }
8957                     if (optional && !attrs[attrName]) break;
8958
8959                     parentGet = $parse(attrs[attrName]);
8960                     if (parentGet.literal) {
8961                       compare = equals;
8962                     } else {
8963                       compare = function(a, b) { return a === b || (a !== a && b !== b); };
8964                     }
8965                     parentSet = parentGet.assign || function() {
8966                       // reset the change, or we will throw this exception on every $digest
8967                       lastValue = destination[scopeName] = parentGet(scope);
8968                       throw $compileMinErr('nonassign',
8969                           "Expression '{0}' used with directive '{1}' is non-assignable!",
8970                           attrs[attrName], directive.name);
8971                     };
8972                     lastValue = destination[scopeName] = parentGet(scope);
8973                     var parentValueWatch = function parentValueWatch(parentValue) {
8974                       if (!compare(parentValue, destination[scopeName])) {
8975                         // we are out of sync and need to copy
8976                         if (!compare(parentValue, lastValue)) {
8977                           // parent changed and it has precedence
8978                           destination[scopeName] = parentValue;
8979                         } else {
8980                           // if the parent can be assigned then do so
8981                           parentSet(scope, parentValue = destination[scopeName]);
8982                         }
8983                       }
8984                       return lastValue = parentValue;
8985                     };
8986                     parentValueWatch.$stateful = true;
8987                     var removeWatch;
8988                     if (definition.collection) {
8989                       removeWatch = scope.$watchCollection(attrs[attrName], parentValueWatch);
8990                     } else {
8991                       removeWatch = scope.$watch($parse(attrs[attrName], parentValueWatch), null, parentGet.literal);
8992                     }
8993                     removeWatchCollection.push(removeWatch);
8994                     break;
8995
8996                   case '&':
8997                     // Don't assign Object.prototype method to scope
8998                     parentGet = attrs.hasOwnProperty(attrName) ? $parse(attrs[attrName]) : noop;
8999
9000                     // Don't assign noop to destination if expression is not valid
9001                     if (parentGet === noop && optional) break;
9002
9003                     destination[scopeName] = function(locals) {
9004                       return parentGet(scope, locals);
9005                     };
9006                     break;
9007                 }
9008               });
9009
9010               return removeWatchCollection.length && function removeWatches() {
9011                 for (var i = 0, ii = removeWatchCollection.length; i < ii; ++i) {
9012                   removeWatchCollection[i]();
9013                 }
9014               };
9015             }
9016           }];
9017         }
9018
9019         var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
9020         /**
9021          * Converts all accepted directives format into proper directive name.
9022          * @param name Name to normalize
9023          */
9024         function directiveNormalize(name) {
9025           return camelCase(name.replace(PREFIX_REGEXP, ''));
9026         }
9027
9028         /**
9029          * @ngdoc type
9030          * @name $compile.directive.Attributes
9031          *
9032          * @description
9033          * A shared object between directive compile / linking functions which contains normalized DOM
9034          * element attributes. The values reflect current binding state `{{ }}`. The normalization is
9035          * needed since all of these are treated as equivalent in Angular:
9036          *
9037          * ```
9038          *    <span ng:bind="a" ng-bind="a" data-ng-bind="a" x-ng-bind="a">
9039          * ```
9040          */
9041
9042         /**
9043          * @ngdoc property
9044          * @name $compile.directive.Attributes#$attr
9045          *
9046          * @description
9047          * A map of DOM element attribute names to the normalized name. This is
9048          * needed to do reverse lookup from normalized name back to actual name.
9049          */
9050
9051
9052         /**
9053          * @ngdoc method
9054          * @name $compile.directive.Attributes#$set
9055          * @kind function
9056          *
9057          * @description
9058          * Set DOM element attribute value.
9059          *
9060          *
9061          * @param {string} name Normalized element attribute name of the property to modify. The name is
9062          *          reverse-translated using the {@link ng.$compile.directive.Attributes#$attr $attr}
9063          *          property to the original name.
9064          * @param {string} value Value to set the attribute to. The value can be an interpolated string.
9065          */
9066
9067
9068
9069         /**
9070          * Closure compiler type information
9071          */
9072
9073         function nodesetLinkingFn(
9074           /* angular.Scope */ scope,
9075           /* NodeList */ nodeList,
9076           /* Element */ rootElement,
9077           /* function(Function) */ boundTranscludeFn
9078         ) {}
9079
9080         function directiveLinkingFn(
9081           /* nodesetLinkingFn */ nodesetLinkingFn,
9082           /* angular.Scope */ scope,
9083           /* Node */ node,
9084           /* Element */ rootElement,
9085           /* function(Function) */ boundTranscludeFn
9086         ) {}
9087
9088         function tokenDifference(str1, str2) {
9089           var values = '',
9090               tokens1 = str1.split(/\s+/),
9091               tokens2 = str2.split(/\s+/);
9092
9093           outer:
9094           for (var i = 0; i < tokens1.length; i++) {
9095             var token = tokens1[i];
9096             for (var j = 0; j < tokens2.length; j++) {
9097               if (token == tokens2[j]) continue outer;
9098             }
9099             values += (values.length > 0 ? ' ' : '') + token;
9100           }
9101           return values;
9102         }
9103
9104         function removeComments(jqNodes) {
9105           jqNodes = jqLite(jqNodes);
9106           var i = jqNodes.length;
9107
9108           if (i <= 1) {
9109             return jqNodes;
9110           }
9111
9112           while (i--) {
9113             var node = jqNodes[i];
9114             if (node.nodeType === NODE_TYPE_COMMENT) {
9115               splice.call(jqNodes, i, 1);
9116             }
9117           }
9118           return jqNodes;
9119         }
9120
9121         var $controllerMinErr = minErr('$controller');
9122
9123
9124         var CNTRL_REG = /^(\S+)(\s+as\s+(\w+))?$/;
9125         function identifierForController(controller, ident) {
9126           if (ident && isString(ident)) return ident;
9127           if (isString(controller)) {
9128             var match = CNTRL_REG.exec(controller);
9129             if (match) return match[3];
9130           }
9131         }
9132
9133
9134         /**
9135          * @ngdoc provider
9136          * @name $controllerProvider
9137          * @description
9138          * The {@link ng.$controller $controller service} is used by Angular to create new
9139          * controllers.
9140          *
9141          * This provider allows controller registration via the
9142          * {@link ng.$controllerProvider#register register} method.
9143          */
9144         function $ControllerProvider() {
9145           var controllers = {},
9146               globals = false;
9147
9148           /**
9149            * @ngdoc method
9150            * @name $controllerProvider#register
9151            * @param {string|Object} name Controller name, or an object map of controllers where the keys are
9152            *    the names and the values are the constructors.
9153            * @param {Function|Array} constructor Controller constructor fn (optionally decorated with DI
9154            *    annotations in the array notation).
9155            */
9156           this.register = function(name, constructor) {
9157             assertNotHasOwnProperty(name, 'controller');
9158             if (isObject(name)) {
9159               extend(controllers, name);
9160             } else {
9161               controllers[name] = constructor;
9162             }
9163           };
9164
9165           /**
9166            * @ngdoc method
9167            * @name $controllerProvider#allowGlobals
9168            * @description If called, allows `$controller` to find controller constructors on `window`
9169            */
9170           this.allowGlobals = function() {
9171             globals = true;
9172           };
9173
9174
9175           this.$get = ['$injector', '$window', function($injector, $window) {
9176
9177             /**
9178              * @ngdoc service
9179              * @name $controller
9180              * @requires $injector
9181              *
9182              * @param {Function|string} constructor If called with a function then it's considered to be the
9183              *    controller constructor function. Otherwise it's considered to be a string which is used
9184              *    to retrieve the controller constructor using the following steps:
9185              *
9186              *    * check if a controller with given name is registered via `$controllerProvider`
9187              *    * check if evaluating the string on the current scope returns a constructor
9188              *    * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
9189              *      `window` object (not recommended)
9190              *
9191              *    The string can use the `controller as property` syntax, where the controller instance is published
9192              *    as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
9193              *    to work correctly.
9194              *
9195              * @param {Object} locals Injection locals for Controller.
9196              * @return {Object} Instance of given controller.
9197              *
9198              * @description
9199              * `$controller` service is responsible for instantiating controllers.
9200              *
9201              * It's just a simple call to {@link auto.$injector $injector}, but extracted into
9202              * a service, so that one can override this service with [BC version](https://gist.github.com/1649788).
9203              */
9204             return function(expression, locals, later, ident) {
9205               // PRIVATE API:
9206               //   param `later` --- indicates that the controller's constructor is invoked at a later time.
9207               //                     If true, $controller will allocate the object with the correct
9208               //                     prototype chain, but will not invoke the controller until a returned
9209               //                     callback is invoked.
9210               //   param `ident` --- An optional label which overrides the label parsed from the controller
9211               //                     expression, if any.
9212               var instance, match, constructor, identifier;
9213               later = later === true;
9214               if (ident && isString(ident)) {
9215                 identifier = ident;
9216               }
9217
9218               if (isString(expression)) {
9219                 match = expression.match(CNTRL_REG);
9220                 if (!match) {
9221                   throw $controllerMinErr('ctrlfmt',
9222                     "Badly formed controller string '{0}'. " +
9223                     "Must match `__name__ as __id__` or `__name__`.", expression);
9224                 }
9225                 constructor = match[1],
9226                 identifier = identifier || match[3];
9227                 expression = controllers.hasOwnProperty(constructor)
9228                     ? controllers[constructor]
9229                     : getter(locals.$scope, constructor, true) ||
9230                         (globals ? getter($window, constructor, true) : undefined);
9231
9232                 assertArgFn(expression, constructor, true);
9233               }
9234
9235               if (later) {
9236                 // Instantiate controller later:
9237                 // This machinery is used to create an instance of the object before calling the
9238                 // controller's constructor itself.
9239                 //
9240                 // This allows properties to be added to the controller before the constructor is
9241                 // invoked. Primarily, this is used for isolate scope bindings in $compile.
9242                 //
9243                 // This feature is not intended for use by applications, and is thus not documented
9244                 // publicly.
9245                 // Object creation: http://jsperf.com/create-constructor/2
9246                 var controllerPrototype = (isArray(expression) ?
9247                   expression[expression.length - 1] : expression).prototype;
9248                 instance = Object.create(controllerPrototype || null);
9249
9250                 if (identifier) {
9251                   addIdentifier(locals, identifier, instance, constructor || expression.name);
9252                 }
9253
9254                 var instantiate;
9255                 return instantiate = extend(function() {
9256                   var result = $injector.invoke(expression, instance, locals, constructor);
9257                   if (result !== instance && (isObject(result) || isFunction(result))) {
9258                     instance = result;
9259                     if (identifier) {
9260                       // If result changed, re-assign controllerAs value to scope.
9261                       addIdentifier(locals, identifier, instance, constructor || expression.name);
9262                     }
9263                   }
9264                   return instance;
9265                 }, {
9266                   instance: instance,
9267                   identifier: identifier
9268                 });
9269               }
9270
9271               instance = $injector.instantiate(expression, locals, constructor);
9272
9273               if (identifier) {
9274                 addIdentifier(locals, identifier, instance, constructor || expression.name);
9275               }
9276
9277               return instance;
9278             };
9279
9280             function addIdentifier(locals, identifier, instance, name) {
9281               if (!(locals && isObject(locals.$scope))) {
9282                 throw minErr('$controller')('noscp',
9283                   "Cannot export controller '{0}' as '{1}'! No $scope object provided via `locals`.",
9284                   name, identifier);
9285               }
9286
9287               locals.$scope[identifier] = instance;
9288             }
9289           }];
9290         }
9291
9292         /**
9293          * @ngdoc service
9294          * @name $document
9295          * @requires $window
9296          *
9297          * @description
9298          * A {@link angular.element jQuery or jqLite} wrapper for the browser's `window.document` object.
9299          *
9300          * @example
9301            <example module="documentExample">
9302              <file name="index.html">
9303                <div ng-controller="ExampleController">
9304                  <p>$document title: <b ng-bind="title"></b></p>
9305                  <p>window.document title: <b ng-bind="windowTitle"></b></p>
9306                </div>
9307              </file>
9308              <file name="script.js">
9309                angular.module('documentExample', [])
9310                  .controller('ExampleController', ['$scope', '$document', function($scope, $document) {
9311                    $scope.title = $document[0].title;
9312                    $scope.windowTitle = angular.element(window.document)[0].title;
9313                  }]);
9314              </file>
9315            </example>
9316          */
9317         function $DocumentProvider() {
9318           this.$get = ['$window', function(window) {
9319             return jqLite(window.document);
9320           }];
9321         }
9322
9323         /**
9324          * @ngdoc service
9325          * @name $exceptionHandler
9326          * @requires ng.$log
9327          *
9328          * @description
9329          * Any uncaught exception in angular expressions is delegated to this service.
9330          * The default implementation simply delegates to `$log.error` which logs it into
9331          * the browser console.
9332          *
9333          * In unit tests, if `angular-mocks.js` is loaded, this service is overridden by
9334          * {@link ngMock.$exceptionHandler mock $exceptionHandler} which aids in testing.
9335          *
9336          * ## Example:
9337          *
9338          * ```js
9339          *   angular.module('exceptionOverride', []).factory('$exceptionHandler', function() {
9340          *     return function(exception, cause) {
9341          *       exception.message += ' (caused by "' + cause + '")';
9342          *       throw exception;
9343          *     };
9344          *   });
9345          * ```
9346          *
9347          * This example will override the normal action of `$exceptionHandler`, to make angular
9348          * exceptions fail hard when they happen, instead of just logging to the console.
9349          *
9350          * <hr />
9351          * Note, that code executed in event-listeners (even those registered using jqLite's `on`/`bind`
9352          * methods) does not delegate exceptions to the {@link ng.$exceptionHandler $exceptionHandler}
9353          * (unless executed during a digest).
9354          *
9355          * If you wish, you can manually delegate exceptions, e.g.
9356          * `try { ... } catch(e) { $exceptionHandler(e); }`
9357          *
9358          * @param {Error} exception Exception associated with the error.
9359          * @param {string=} cause optional information about the context in which
9360          *       the error was thrown.
9361          *
9362          */
9363         function $ExceptionHandlerProvider() {
9364           this.$get = ['$log', function($log) {
9365             return function(exception, cause) {
9366               $log.error.apply($log, arguments);
9367             };
9368           }];
9369         }
9370
9371         var $$ForceReflowProvider = function() {
9372           this.$get = ['$document', function($document) {
9373             return function(domNode) {
9374               //the line below will force the browser to perform a repaint so
9375               //that all the animated elements within the animation frame will
9376               //be properly updated and drawn on screen. This is required to
9377               //ensure that the preparation animation is properly flushed so that
9378               //the active state picks up from there. DO NOT REMOVE THIS LINE.
9379               //DO NOT OPTIMIZE THIS LINE. THE MINIFIER WILL REMOVE IT OTHERWISE WHICH
9380               //WILL RESULT IN AN UNPREDICTABLE BUG THAT IS VERY HARD TO TRACK DOWN AND
9381               //WILL TAKE YEARS AWAY FROM YOUR LIFE.
9382               if (domNode) {
9383                 if (!domNode.nodeType && domNode instanceof jqLite) {
9384                   domNode = domNode[0];
9385                 }
9386               } else {
9387                 domNode = $document[0].body;
9388               }
9389               return domNode.offsetWidth + 1;
9390             };
9391           }];
9392         };
9393
9394         var APPLICATION_JSON = 'application/json';
9395         var CONTENT_TYPE_APPLICATION_JSON = {'Content-Type': APPLICATION_JSON + ';charset=utf-8'};
9396         var JSON_START = /^\[|^\{(?!\{)/;
9397         var JSON_ENDS = {
9398           '[': /]$/,
9399           '{': /}$/
9400         };
9401         var JSON_PROTECTION_PREFIX = /^\)\]\}',?\n/;
9402         var $httpMinErr = minErr('$http');
9403         var $httpMinErrLegacyFn = function(method) {
9404           return function() {
9405             throw $httpMinErr('legacy', 'The method `{0}` on the promise returned from `$http` has been disabled.', method);
9406           };
9407         };
9408
9409         function serializeValue(v) {
9410           if (isObject(v)) {
9411             return isDate(v) ? v.toISOString() : toJson(v);
9412           }
9413           return v;
9414         }
9415
9416
9417         function $HttpParamSerializerProvider() {
9418           /**
9419            * @ngdoc service
9420            * @name $httpParamSerializer
9421            * @description
9422            *
9423            * Default {@link $http `$http`} params serializer that converts objects to strings
9424            * according to the following rules:
9425            *
9426            * * `{'foo': 'bar'}` results in `foo=bar`
9427            * * `{'foo': Date.now()}` results in `foo=2015-04-01T09%3A50%3A49.262Z` (`toISOString()` and encoded representation of a Date object)
9428            * * `{'foo': ['bar', 'baz']}` results in `foo=bar&foo=baz` (repeated key for each array element)
9429            * * `{'foo': {'bar':'baz'}}` results in `foo=%7B%22bar%22%3A%22baz%22%7D"` (stringified and encoded representation of an object)
9430            *
9431            * Note that serializer will sort the request parameters alphabetically.
9432            * */
9433
9434           this.$get = function() {
9435             return function ngParamSerializer(params) {
9436               if (!params) return '';
9437               var parts = [];
9438               forEachSorted(params, function(value, key) {
9439                 if (value === null || isUndefined(value)) return;
9440                 if (isArray(value)) {
9441                   forEach(value, function(v, k) {
9442                     parts.push(encodeUriQuery(key)  + '=' + encodeUriQuery(serializeValue(v)));
9443                   });
9444                 } else {
9445                   parts.push(encodeUriQuery(key) + '=' + encodeUriQuery(serializeValue(value)));
9446                 }
9447               });
9448
9449               return parts.join('&');
9450             };
9451           };
9452         }
9453
9454         function $HttpParamSerializerJQLikeProvider() {
9455           /**
9456            * @ngdoc service
9457            * @name $httpParamSerializerJQLike
9458            * @description
9459            *
9460            * Alternative {@link $http `$http`} params serializer that follows
9461            * jQuery's [`param()`](http://api.jquery.com/jquery.param/) method logic.
9462            * The serializer will also sort the params alphabetically.
9463            *
9464            * To use it for serializing `$http` request parameters, set it as the `paramSerializer` property:
9465            *
9466            * ```js
9467            * $http({
9468            *   url: myUrl,
9469            *   method: 'GET',
9470            *   params: myParams,
9471            *   paramSerializer: '$httpParamSerializerJQLike'
9472            * });
9473            * ```
9474            *
9475            * It is also possible to set it as the default `paramSerializer` in the
9476            * {@link $httpProvider#defaults `$httpProvider`}.
9477            *
9478            * Additionally, you can inject the serializer and use it explicitly, for example to serialize
9479            * form data for submission:
9480            *
9481            * ```js
9482            * .controller(function($http, $httpParamSerializerJQLike) {
9483            *   //...
9484            *
9485            *   $http({
9486            *     url: myUrl,
9487            *     method: 'POST',
9488            *     data: $httpParamSerializerJQLike(myData),
9489            *     headers: {
9490            *       'Content-Type': 'application/x-www-form-urlencoded'
9491            *     }
9492            *   });
9493            *
9494            * });
9495            * ```
9496            *
9497            * */
9498           this.$get = function() {
9499             return function jQueryLikeParamSerializer(params) {
9500               if (!params) return '';
9501               var parts = [];
9502               serialize(params, '', true);
9503               return parts.join('&');
9504
9505               function serialize(toSerialize, prefix, topLevel) {
9506                 if (toSerialize === null || isUndefined(toSerialize)) return;
9507                 if (isArray(toSerialize)) {
9508                   forEach(toSerialize, function(value, index) {
9509                     serialize(value, prefix + '[' + (isObject(value) ? index : '') + ']');
9510                   });
9511                 } else if (isObject(toSerialize) && !isDate(toSerialize)) {
9512                   forEachSorted(toSerialize, function(value, key) {
9513                     serialize(value, prefix +
9514                         (topLevel ? '' : '[') +
9515                         key +
9516                         (topLevel ? '' : ']'));
9517                   });
9518                 } else {
9519                   parts.push(encodeUriQuery(prefix) + '=' + encodeUriQuery(serializeValue(toSerialize)));
9520                 }
9521               }
9522             };
9523           };
9524         }
9525
9526         function defaultHttpResponseTransform(data, headers) {
9527           if (isString(data)) {
9528             // Strip json vulnerability protection prefix and trim whitespace
9529             var tempData = data.replace(JSON_PROTECTION_PREFIX, '').trim();
9530
9531             if (tempData) {
9532               var contentType = headers('Content-Type');
9533               if ((contentType && (contentType.indexOf(APPLICATION_JSON) === 0)) || isJsonLike(tempData)) {
9534                 data = fromJson(tempData);
9535               }
9536             }
9537           }
9538
9539           return data;
9540         }
9541
9542         function isJsonLike(str) {
9543             var jsonStart = str.match(JSON_START);
9544             return jsonStart && JSON_ENDS[jsonStart[0]].test(str);
9545         }
9546
9547         /**
9548          * Parse headers into key value object
9549          *
9550          * @param {string} headers Raw headers as a string
9551          * @returns {Object} Parsed headers as key value object
9552          */
9553         function parseHeaders(headers) {
9554           var parsed = createMap(), i;
9555
9556           function fillInParsed(key, val) {
9557             if (key) {
9558               parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
9559             }
9560           }
9561
9562           if (isString(headers)) {
9563             forEach(headers.split('\n'), function(line) {
9564               i = line.indexOf(':');
9565               fillInParsed(lowercase(trim(line.substr(0, i))), trim(line.substr(i + 1)));
9566             });
9567           } else if (isObject(headers)) {
9568             forEach(headers, function(headerVal, headerKey) {
9569               fillInParsed(lowercase(headerKey), trim(headerVal));
9570             });
9571           }
9572
9573           return parsed;
9574         }
9575
9576
9577         /**
9578          * Returns a function that provides access to parsed headers.
9579          *
9580          * Headers are lazy parsed when first requested.
9581          * @see parseHeaders
9582          *
9583          * @param {(string|Object)} headers Headers to provide access to.
9584          * @returns {function(string=)} Returns a getter function which if called with:
9585          *
9586          *   - if called with single an argument returns a single header value or null
9587          *   - if called with no arguments returns an object containing all headers.
9588          */
9589         function headersGetter(headers) {
9590           var headersObj;
9591
9592           return function(name) {
9593             if (!headersObj) headersObj =  parseHeaders(headers);
9594
9595             if (name) {
9596               var value = headersObj[lowercase(name)];
9597               if (value === void 0) {
9598                 value = null;
9599               }
9600               return value;
9601             }
9602
9603             return headersObj;
9604           };
9605         }
9606
9607
9608         /**
9609          * Chain all given functions
9610          *
9611          * This function is used for both request and response transforming
9612          *
9613          * @param {*} data Data to transform.
9614          * @param {function(string=)} headers HTTP headers getter fn.
9615          * @param {number} status HTTP status code of the response.
9616          * @param {(Function|Array.<Function>)} fns Function or an array of functions.
9617          * @returns {*} Transformed data.
9618          */
9619         function transformData(data, headers, status, fns) {
9620           if (isFunction(fns)) {
9621             return fns(data, headers, status);
9622           }
9623
9624           forEach(fns, function(fn) {
9625             data = fn(data, headers, status);
9626           });
9627
9628           return data;
9629         }
9630
9631
9632         function isSuccess(status) {
9633           return 200 <= status && status < 300;
9634         }
9635
9636
9637         /**
9638          * @ngdoc provider
9639          * @name $httpProvider
9640          * @description
9641          * Use `$httpProvider` to change the default behavior of the {@link ng.$http $http} service.
9642          * */
9643         function $HttpProvider() {
9644           /**
9645            * @ngdoc property
9646            * @name $httpProvider#defaults
9647            * @description
9648            *
9649            * Object containing default values for all {@link ng.$http $http} requests.
9650            *
9651            * - **`defaults.cache`** - {Object} - an object built with {@link ng.$cacheFactory `$cacheFactory`}
9652            * that will provide the cache for all requests who set their `cache` property to `true`.
9653            * If you set the `defaults.cache = false` then only requests that specify their own custom
9654            * cache object will be cached. See {@link $http#caching $http Caching} for more information.
9655            *
9656            * - **`defaults.xsrfCookieName`** - {string} - Name of cookie containing the XSRF token.
9657            * Defaults value is `'XSRF-TOKEN'`.
9658            *
9659            * - **`defaults.xsrfHeaderName`** - {string} - Name of HTTP header to populate with the
9660            * XSRF token. Defaults value is `'X-XSRF-TOKEN'`.
9661            *
9662            * - **`defaults.headers`** - {Object} - Default headers for all $http requests.
9663            * Refer to {@link ng.$http#setting-http-headers $http} for documentation on
9664            * setting default headers.
9665            *     - **`defaults.headers.common`**
9666            *     - **`defaults.headers.post`**
9667            *     - **`defaults.headers.put`**
9668            *     - **`defaults.headers.patch`**
9669            *
9670            *
9671            * - **`defaults.paramSerializer`** - `{string|function(Object<string,string>):string}` - A function
9672            *  used to the prepare string representation of request parameters (specified as an object).
9673            *  If specified as string, it is interpreted as a function registered with the {@link auto.$injector $injector}.
9674            *  Defaults to {@link ng.$httpParamSerializer $httpParamSerializer}.
9675            *
9676            **/
9677           var defaults = this.defaults = {
9678             // transform incoming response data
9679             transformResponse: [defaultHttpResponseTransform],
9680
9681             // transform outgoing request data
9682             transformRequest: [function(d) {
9683               return isObject(d) && !isFile(d) && !isBlob(d) && !isFormData(d) ? toJson(d) : d;
9684             }],
9685
9686             // default headers
9687             headers: {
9688               common: {
9689                 'Accept': 'application/json, text/plain, */*'
9690               },
9691               post:   shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9692               put:    shallowCopy(CONTENT_TYPE_APPLICATION_JSON),
9693               patch:  shallowCopy(CONTENT_TYPE_APPLICATION_JSON)
9694             },
9695
9696             xsrfCookieName: 'XSRF-TOKEN',
9697             xsrfHeaderName: 'X-XSRF-TOKEN',
9698
9699             paramSerializer: '$httpParamSerializer'
9700           };
9701
9702           var useApplyAsync = false;
9703           /**
9704            * @ngdoc method
9705            * @name $httpProvider#useApplyAsync
9706            * @description
9707            *
9708            * Configure $http service to combine processing of multiple http responses received at around
9709            * the same time via {@link ng.$rootScope.Scope#$applyAsync $rootScope.$applyAsync}. This can result in
9710            * significant performance improvement for bigger applications that make many HTTP requests
9711            * concurrently (common during application bootstrap).
9712            *
9713            * Defaults to false. If no value is specified, returns the current configured value.
9714            *
9715            * @param {boolean=} value If true, when requests are loaded, they will schedule a deferred
9716            *    "apply" on the next tick, giving time for subsequent requests in a roughly ~10ms window
9717            *    to load and share the same digest cycle.
9718            *
9719            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9720            *    otherwise, returns the current configured value.
9721            **/
9722           this.useApplyAsync = function(value) {
9723             if (isDefined(value)) {
9724               useApplyAsync = !!value;
9725               return this;
9726             }
9727             return useApplyAsync;
9728           };
9729
9730           var useLegacyPromise = true;
9731           /**
9732            * @ngdoc method
9733            * @name $httpProvider#useLegacyPromiseExtensions
9734            * @description
9735            *
9736            * Configure `$http` service to return promises without the shorthand methods `success` and `error`.
9737            * This should be used to make sure that applications work without these methods.
9738            *
9739            * Defaults to true. If no value is specified, returns the current configured value.
9740            *
9741            * @param {boolean=} value If true, `$http` will return a promise with the deprecated legacy `success` and `error` methods.
9742            *
9743            * @returns {boolean|Object} If a value is specified, returns the $httpProvider for chaining.
9744            *    otherwise, returns the current configured value.
9745            **/
9746           this.useLegacyPromiseExtensions = function(value) {
9747             if (isDefined(value)) {
9748               useLegacyPromise = !!value;
9749               return this;
9750             }
9751             return useLegacyPromise;
9752           };
9753
9754           /**
9755            * @ngdoc property
9756            * @name $httpProvider#interceptors
9757            * @description
9758            *
9759            * Array containing service factories for all synchronous or asynchronous {@link ng.$http $http}
9760            * pre-processing of request or postprocessing of responses.
9761            *
9762            * These service factories are ordered by request, i.e. they are applied in the same order as the
9763            * array, on request, but reverse order, on response.
9764            *
9765            * {@link ng.$http#interceptors Interceptors detailed info}
9766            **/
9767           var interceptorFactories = this.interceptors = [];
9768
9769           this.$get = ['$httpBackend', '$$cookieReader', '$cacheFactory', '$rootScope', '$q', '$injector',
9770               function($httpBackend, $$cookieReader, $cacheFactory, $rootScope, $q, $injector) {
9771
9772             var defaultCache = $cacheFactory('$http');
9773
9774             /**
9775              * Make sure that default param serializer is exposed as a function
9776              */
9777             defaults.paramSerializer = isString(defaults.paramSerializer) ?
9778               $injector.get(defaults.paramSerializer) : defaults.paramSerializer;
9779
9780             /**
9781              * Interceptors stored in reverse order. Inner interceptors before outer interceptors.
9782              * The reversal is needed so that we can build up the interception chain around the
9783              * server request.
9784              */
9785             var reversedInterceptors = [];
9786
9787             forEach(interceptorFactories, function(interceptorFactory) {
9788               reversedInterceptors.unshift(isString(interceptorFactory)
9789                   ? $injector.get(interceptorFactory) : $injector.invoke(interceptorFactory));
9790             });
9791
9792             /**
9793              * @ngdoc service
9794              * @kind function
9795              * @name $http
9796              * @requires ng.$httpBackend
9797              * @requires $cacheFactory
9798              * @requires $rootScope
9799              * @requires $q
9800              * @requires $injector
9801              *
9802              * @description
9803              * The `$http` service is a core Angular service that facilitates communication with the remote
9804              * HTTP servers via the browser's [XMLHttpRequest](https://developer.mozilla.org/en/xmlhttprequest)
9805              * object or via [JSONP](http://en.wikipedia.org/wiki/JSONP).
9806              *
9807              * For unit testing applications that use `$http` service, see
9808              * {@link ngMock.$httpBackend $httpBackend mock}.
9809              *
9810              * For a higher level of abstraction, please check out the {@link ngResource.$resource
9811              * $resource} service.
9812              *
9813              * The $http API is based on the {@link ng.$q deferred/promise APIs} exposed by
9814              * the $q service. While for simple usage patterns this doesn't matter much, for advanced usage
9815              * it is important to familiarize yourself with these APIs and the guarantees they provide.
9816              *
9817              *
9818              * ## General usage
9819              * The `$http` service is a function which takes a single argument — a {@link $http#usage configuration object} —
9820              * that is used to generate an HTTP request and returns  a {@link ng.$q promise}.
9821              *
9822              * ```js
9823              *   // Simple GET request example:
9824              *   $http({
9825              *     method: 'GET',
9826              *     url: '/someUrl'
9827              *   }).then(function successCallback(response) {
9828              *       // this callback will be called asynchronously
9829              *       // when the response is available
9830              *     }, function errorCallback(response) {
9831              *       // called asynchronously if an error occurs
9832              *       // or server returns response with an error status.
9833              *     });
9834              * ```
9835              *
9836              * The response object has these properties:
9837              *
9838              *   - **data** – `{string|Object}` – The response body transformed with the transform
9839              *     functions.
9840              *   - **status** – `{number}` – HTTP status code of the response.
9841              *   - **headers** – `{function([headerName])}` – Header getter function.
9842              *   - **config** – `{Object}` – The configuration object that was used to generate the request.
9843              *   - **statusText** – `{string}` – HTTP status text of the response.
9844              *
9845              * A response status code between 200 and 299 is considered a success status and
9846              * will result in the success callback being called. Note that if the response is a redirect,
9847              * XMLHttpRequest will transparently follow it, meaning that the error callback will not be
9848              * called for such responses.
9849              *
9850              *
9851              * ## Shortcut methods
9852              *
9853              * Shortcut methods are also available. All shortcut methods require passing in the URL, and
9854              * request data must be passed in for POST/PUT requests. An optional config can be passed as the
9855              * last argument.
9856              *
9857              * ```js
9858              *   $http.get('/someUrl', config).then(successCallback, errorCallback);
9859              *   $http.post('/someUrl', data, config).then(successCallback, errorCallback);
9860              * ```
9861              *
9862              * Complete list of shortcut methods:
9863              *
9864              * - {@link ng.$http#get $http.get}
9865              * - {@link ng.$http#head $http.head}
9866              * - {@link ng.$http#post $http.post}
9867              * - {@link ng.$http#put $http.put}
9868              * - {@link ng.$http#delete $http.delete}
9869              * - {@link ng.$http#jsonp $http.jsonp}
9870              * - {@link ng.$http#patch $http.patch}
9871              *
9872              *
9873              * ## Writing Unit Tests that use $http
9874              * When unit testing (using {@link ngMock ngMock}), it is necessary to call
9875              * {@link ngMock.$httpBackend#flush $httpBackend.flush()} to flush each pending
9876              * request using trained responses.
9877              *
9878              * ```
9879              * $httpBackend.expectGET(...);
9880              * $http.get(...);
9881              * $httpBackend.flush();
9882              * ```
9883              *
9884              * ## Deprecation Notice
9885              * <div class="alert alert-danger">
9886              *   The `$http` legacy promise methods `success` and `error` have been deprecated.
9887              *   Use the standard `then` method instead.
9888              *   If {@link $httpProvider#useLegacyPromiseExtensions `$httpProvider.useLegacyPromiseExtensions`} is set to
9889              *   `false` then these methods will throw {@link $http:legacy `$http/legacy`} error.
9890              * </div>
9891              *
9892              * ## Setting HTTP Headers
9893              *
9894              * The $http service will automatically add certain HTTP headers to all requests. These defaults
9895              * can be fully configured by accessing the `$httpProvider.defaults.headers` configuration
9896              * object, which currently contains this default configuration:
9897              *
9898              * - `$httpProvider.defaults.headers.common` (headers that are common for all requests):
9899              *   - `Accept: application/json, text/plain, * / *`
9900              * - `$httpProvider.defaults.headers.post`: (header defaults for POST requests)
9901              *   - `Content-Type: application/json`
9902              * - `$httpProvider.defaults.headers.put` (header defaults for PUT requests)
9903              *   - `Content-Type: application/json`
9904              *
9905              * To add or overwrite these defaults, simply add or remove a property from these configuration
9906              * objects. To add headers for an HTTP method other than POST or PUT, simply add a new object
9907              * with the lowercased HTTP method name as the key, e.g.
9908              * `$httpProvider.defaults.headers.get = { 'My-Header' : 'value' }`.
9909              *
9910              * The defaults can also be set at runtime via the `$http.defaults` object in the same
9911              * fashion. For example:
9912              *
9913              * ```
9914              * module.run(function($http) {
9915              *   $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w'
9916              * });
9917              * ```
9918              *
9919              * In addition, you can supply a `headers` property in the config object passed when
9920              * calling `$http(config)`, which overrides the defaults without changing them globally.
9921              *
9922              * To explicitly remove a header automatically added via $httpProvider.defaults.headers on a per request basis,
9923              * Use the `headers` property, setting the desired header to `undefined`. For example:
9924              *
9925              * ```js
9926              * var req = {
9927              *  method: 'POST',
9928              *  url: 'http://example.com',
9929              *  headers: {
9930              *    'Content-Type': undefined
9931              *  },
9932              *  data: { test: 'test' }
9933              * }
9934              *
9935              * $http(req).then(function(){...}, function(){...});
9936              * ```
9937              *
9938              * ## Transforming Requests and Responses
9939              *
9940              * Both requests and responses can be transformed using transformation functions: `transformRequest`
9941              * and `transformResponse`. These properties can be a single function that returns
9942              * the transformed value (`function(data, headersGetter, status)`) or an array of such transformation functions,
9943              * which allows you to `push` or `unshift` a new transformation function into the transformation chain.
9944              *
9945              * ### Default Transformations
9946              *
9947              * The `$httpProvider` provider and `$http` service expose `defaults.transformRequest` and
9948              * `defaults.transformResponse` properties. If a request does not provide its own transformations
9949              * then these will be applied.
9950              *
9951              * You can augment or replace the default transformations by modifying these properties by adding to or
9952              * replacing the array.
9953              *
9954              * Angular provides the following default transformations:
9955              *
9956              * Request transformations (`$httpProvider.defaults.transformRequest` and `$http.defaults.transformRequest`):
9957              *
9958              * - If the `data` property of the request configuration object contains an object, serialize it
9959              *   into JSON format.
9960              *
9961              * Response transformations (`$httpProvider.defaults.transformResponse` and `$http.defaults.transformResponse`):
9962              *
9963              *  - If XSRF prefix is detected, strip it (see Security Considerations section below).
9964              *  - If JSON response is detected, deserialize it using a JSON parser.
9965              *
9966              *
9967              * ### Overriding the Default Transformations Per Request
9968              *
9969              * If you wish override the request/response transformations only for a single request then provide
9970              * `transformRequest` and/or `transformResponse` properties on the configuration object passed
9971              * into `$http`.
9972              *
9973              * Note that if you provide these properties on the config object the default transformations will be
9974              * overwritten. If you wish to augment the default transformations then you must include them in your
9975              * local transformation array.
9976              *
9977              * The following code demonstrates adding a new response transformation to be run after the default response
9978              * transformations have been run.
9979              *
9980              * ```js
9981              * function appendTransform(defaults, transform) {
9982              *
9983              *   // We can't guarantee that the default transformation is an array
9984              *   defaults = angular.isArray(defaults) ? defaults : [defaults];
9985              *
9986              *   // Append the new transformation to the defaults
9987              *   return defaults.concat(transform);
9988              * }
9989              *
9990              * $http({
9991              *   url: '...',
9992              *   method: 'GET',
9993              *   transformResponse: appendTransform($http.defaults.transformResponse, function(value) {
9994              *     return doTransform(value);
9995              *   })
9996              * });
9997              * ```
9998              *
9999              *
10000              * ## Caching
10001              *
10002              * To enable caching, set the request configuration `cache` property to `true` (to use default
10003              * cache) or to a custom cache object (built with {@link ng.$cacheFactory `$cacheFactory`}).
10004              * When the cache is enabled, `$http` stores the response from the server in the specified
10005              * cache. The next time the same request is made, the response is served from the cache without
10006              * sending a request to the server.
10007              *
10008              * Note that even if the response is served from cache, delivery of the data is asynchronous in
10009              * the same way that real requests are.
10010              *
10011              * If there are multiple GET requests for the same URL that should be cached using the same
10012              * cache, but the cache is not populated yet, only one request to the server will be made and
10013              * the remaining requests will be fulfilled using the response from the first request.
10014              *
10015              * You can change the default cache to a new object (built with
10016              * {@link ng.$cacheFactory `$cacheFactory`}) by updating the
10017              * {@link ng.$http#defaults `$http.defaults.cache`} property. All requests who set
10018              * their `cache` property to `true` will now use this cache object.
10019              *
10020              * If you set the default cache to `false` then only requests that specify their own custom
10021              * cache object will be cached.
10022              *
10023              * ## Interceptors
10024              *
10025              * Before you start creating interceptors, be sure to understand the
10026              * {@link ng.$q $q and deferred/promise APIs}.
10027              *
10028              * For purposes of global error handling, authentication, or any kind of synchronous or
10029              * asynchronous pre-processing of request or postprocessing of responses, it is desirable to be
10030              * able to intercept requests before they are handed to the server and
10031              * responses before they are handed over to the application code that
10032              * initiated these requests. The interceptors leverage the {@link ng.$q
10033              * promise APIs} to fulfill this need for both synchronous and asynchronous pre-processing.
10034              *
10035              * The interceptors are service factories that are registered with the `$httpProvider` by
10036              * adding them to the `$httpProvider.interceptors` array. The factory is called and
10037              * injected with dependencies (if specified) and returns the interceptor.
10038              *
10039              * There are two kinds of interceptors (and two kinds of rejection interceptors):
10040              *
10041              *   * `request`: interceptors get called with a http {@link $http#usage config} object. The function is free to
10042              *     modify the `config` object or create a new one. The function needs to return the `config`
10043              *     object directly, or a promise containing the `config` or a new `config` object.
10044              *   * `requestError`: interceptor gets called when a previous interceptor threw an error or
10045              *     resolved with a rejection.
10046              *   * `response`: interceptors get called with http `response` object. The function is free to
10047              *     modify the `response` object or create a new one. The function needs to return the `response`
10048              *     object directly, or as a promise containing the `response` or a new `response` object.
10049              *   * `responseError`: interceptor gets called when a previous interceptor threw an error or
10050              *     resolved with a rejection.
10051              *
10052              *
10053              * ```js
10054              *   // register the interceptor as a service
10055              *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
10056              *     return {
10057              *       // optional method
10058              *       'request': function(config) {
10059              *         // do something on success
10060              *         return config;
10061              *       },
10062              *
10063              *       // optional method
10064              *      'requestError': function(rejection) {
10065              *         // do something on error
10066              *         if (canRecover(rejection)) {
10067              *           return responseOrNewPromise
10068              *         }
10069              *         return $q.reject(rejection);
10070              *       },
10071              *
10072              *
10073              *
10074              *       // optional method
10075              *       'response': function(response) {
10076              *         // do something on success
10077              *         return response;
10078              *       },
10079              *
10080              *       // optional method
10081              *      'responseError': function(rejection) {
10082              *         // do something on error
10083              *         if (canRecover(rejection)) {
10084              *           return responseOrNewPromise
10085              *         }
10086              *         return $q.reject(rejection);
10087              *       }
10088              *     };
10089              *   });
10090              *
10091              *   $httpProvider.interceptors.push('myHttpInterceptor');
10092              *
10093              *
10094              *   // alternatively, register the interceptor via an anonymous factory
10095              *   $httpProvider.interceptors.push(function($q, dependency1, dependency2) {
10096              *     return {
10097              *      'request': function(config) {
10098              *          // same as above
10099              *       },
10100              *
10101              *       'response': function(response) {
10102              *          // same as above
10103              *       }
10104              *     };
10105              *   });
10106              * ```
10107              *
10108              * ## Security Considerations
10109              *
10110              * When designing web applications, consider security threats from:
10111              *
10112              * - [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10113              * - [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery)
10114              *
10115              * Both server and the client must cooperate in order to eliminate these threats. Angular comes
10116              * pre-configured with strategies that address these issues, but for this to work backend server
10117              * cooperation is required.
10118              *
10119              * ### JSON Vulnerability Protection
10120              *
10121              * A [JSON vulnerability](http://haacked.com/archive/2008/11/20/anatomy-of-a-subtle-json-vulnerability.aspx)
10122              * allows third party website to turn your JSON resource URL into
10123              * [JSONP](http://en.wikipedia.org/wiki/JSONP) request under some conditions. To
10124              * counter this your server can prefix all JSON requests with following string `")]}',\n"`.
10125              * Angular will automatically strip the prefix before processing it as JSON.
10126              *
10127              * For example if your server needs to return:
10128              * ```js
10129              * ['one','two']
10130              * ```
10131              *
10132              * which is vulnerable to attack, your server can return:
10133              * ```js
10134              * )]}',
10135              * ['one','two']
10136              * ```
10137              *
10138              * Angular will strip the prefix, before processing the JSON.
10139              *
10140              *
10141              * ### Cross Site Request Forgery (XSRF) Protection
10142              *
10143              * [XSRF](http://en.wikipedia.org/wiki/Cross-site_request_forgery) is a technique by which
10144              * an unauthorized site can gain your user's private data. Angular provides a mechanism
10145              * to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
10146              * (by default, `XSRF-TOKEN`) and sets it as an HTTP header (`X-XSRF-TOKEN`). Since only
10147              * JavaScript that runs on your domain could read the cookie, your server can be assured that
10148              * the XHR came from JavaScript running on your domain. The header will not be set for
10149              * cross-domain requests.
10150              *
10151              * To take advantage of this, your server needs to set a token in a JavaScript readable session
10152              * cookie called `XSRF-TOKEN` on the first HTTP GET request. On subsequent XHR requests the
10153              * server can verify that the cookie matches `X-XSRF-TOKEN` HTTP header, and therefore be sure
10154              * that only JavaScript running on your domain could have sent the request. The token must be
10155              * unique for each user and must be verifiable by the server (to prevent the JavaScript from
10156              * making up its own tokens). We recommend that the token is a digest of your site's
10157              * authentication cookie with a [salt](https://en.wikipedia.org/wiki/Salt_(cryptography&#41;)
10158              * for added security.
10159              *
10160              * The name of the headers can be specified using the xsrfHeaderName and xsrfCookieName
10161              * properties of either $httpProvider.defaults at config-time, $http.defaults at run-time,
10162              * or the per-request config object.
10163              *
10164              * In order to prevent collisions in environments where multiple Angular apps share the
10165              * same domain or subdomain, we recommend that each application uses unique cookie name.
10166              *
10167              * @param {object} config Object describing the request to be made and how it should be
10168              *    processed. The object has following properties:
10169              *
10170              *    - **method** – `{string}` – HTTP method (e.g. 'GET', 'POST', etc)
10171              *    - **url** – `{string}` – Absolute or relative URL of the resource that is being requested.
10172              *    - **params** – `{Object.<string|Object>}` – Map of strings or objects which will be serialized
10173              *      with the `paramSerializer` and appended as GET parameters.
10174              *    - **data** – `{string|Object}` – Data to be sent as the request message data.
10175              *    - **headers** – `{Object}` – Map of strings or functions which return strings representing
10176              *      HTTP headers to send to the server. If the return value of a function is null, the
10177              *      header will not be sent. Functions accept a config object as an argument.
10178              *    - **xsrfHeaderName** – `{string}` – Name of HTTP header to populate with the XSRF token.
10179              *    - **xsrfCookieName** – `{string}` – Name of cookie containing the XSRF token.
10180              *    - **transformRequest** –
10181              *      `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
10182              *      transform function or an array of such functions. The transform function takes the http
10183              *      request body and headers and returns its transformed (typically serialized) version.
10184              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10185              *      Overriding the Default Transformations}
10186              *    - **transformResponse** –
10187              *      `{function(data, headersGetter, status)|Array.<function(data, headersGetter, status)>}` –
10188              *      transform function or an array of such functions. The transform function takes the http
10189              *      response body, headers and status and returns its transformed (typically deserialized) version.
10190              *      See {@link ng.$http#overriding-the-default-transformations-per-request
10191              *      Overriding the Default TransformationjqLiks}
10192              *    - **paramSerializer** - `{string|function(Object<string,string>):string}` - A function used to
10193              *      prepare the string representation of request parameters (specified as an object).
10194              *      If specified as string, it is interpreted as function registered with the
10195              *      {@link $injector $injector}, which means you can create your own serializer
10196              *      by registering it as a {@link auto.$provide#service service}.
10197              *      The default serializer is the {@link $httpParamSerializer $httpParamSerializer};
10198              *      alternatively, you can use the {@link $httpParamSerializerJQLike $httpParamSerializerJQLike}
10199              *    - **cache** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
10200              *      GET request, otherwise if a cache instance built with
10201              *      {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
10202              *      caching.
10203              *    - **timeout** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise}
10204              *      that should abort the request when resolved.
10205              *    - **withCredentials** - `{boolean}` - whether to set the `withCredentials` flag on the
10206              *      XHR object. See [requests with credentials](https://developer.mozilla.org/docs/Web/HTTP/Access_control_CORS#Requests_with_credentials)
10207              *      for more information.
10208              *    - **responseType** - `{string}` - see
10209              *      [XMLHttpRequest.responseType](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest#xmlhttprequest-responsetype).
10210              *
10211              * @returns {HttpPromise} Returns a {@link ng.$q `Promise}` that will be resolved to a response object
10212              *                        when the request succeeds or fails.
10213              *
10214              *
10215              * @property {Array.<Object>} pendingRequests Array of config objects for currently pending
10216              *   requests. This is primarily meant to be used for debugging purposes.
10217              *
10218              *
10219              * @example
10220         <example module="httpExample">
10221         <file name="index.html">
10222           <div ng-controller="FetchController">
10223             <select ng-model="method" aria-label="Request method">
10224               <option>GET</option>
10225               <option>JSONP</option>
10226             </select>
10227             <input type="text" ng-model="url" size="80" aria-label="URL" />
10228             <button id="fetchbtn" ng-click="fetch()">fetch</button><br>
10229             <button id="samplegetbtn" ng-click="updateModel('GET', 'http-hello.html')">Sample GET</button>
10230             <button id="samplejsonpbtn"
10231               ng-click="updateModel('JSONP',
10232                             'https://angularjs.org/greet.php?callback=JSON_CALLBACK&name=Super%20Hero')">
10233               Sample JSONP
10234             </button>
10235             <button id="invalidjsonpbtn"
10236               ng-click="updateModel('JSONP', 'https://angularjs.org/doesntexist&callback=JSON_CALLBACK')">
10237                 Invalid JSONP
10238               </button>
10239             <pre>http status code: {{status}}</pre>
10240             <pre>http response data: {{data}}</pre>
10241           </div>
10242         </file>
10243         <file name="script.js">
10244           angular.module('httpExample', [])
10245             .controller('FetchController', ['$scope', '$http', '$templateCache',
10246               function($scope, $http, $templateCache) {
10247                 $scope.method = 'GET';
10248                 $scope.url = 'http-hello.html';
10249
10250                 $scope.fetch = function() {
10251                   $scope.code = null;
10252                   $scope.response = null;
10253
10254                   $http({method: $scope.method, url: $scope.url, cache: $templateCache}).
10255                     then(function(response) {
10256                       $scope.status = response.status;
10257                       $scope.data = response.data;
10258                     }, function(response) {
10259                       $scope.data = response.data || "Request failed";
10260                       $scope.status = response.status;
10261                   });
10262                 };
10263
10264                 $scope.updateModel = function(method, url) {
10265                   $scope.method = method;
10266                   $scope.url = url;
10267                 };
10268               }]);
10269         </file>
10270         <file name="http-hello.html">
10271           Hello, $http!
10272         </file>
10273         <file name="protractor.js" type="protractor">
10274           var status = element(by.binding('status'));
10275           var data = element(by.binding('data'));
10276           var fetchBtn = element(by.id('fetchbtn'));
10277           var sampleGetBtn = element(by.id('samplegetbtn'));
10278           var sampleJsonpBtn = element(by.id('samplejsonpbtn'));
10279           var invalidJsonpBtn = element(by.id('invalidjsonpbtn'));
10280
10281           it('should make an xhr GET request', function() {
10282             sampleGetBtn.click();
10283             fetchBtn.click();
10284             expect(status.getText()).toMatch('200');
10285             expect(data.getText()).toMatch(/Hello, \$http!/);
10286           });
10287
10288         // Commented out due to flakes. See https://github.com/angular/angular.js/issues/9185
10289         // it('should make a JSONP request to angularjs.org', function() {
10290         //   sampleJsonpBtn.click();
10291         //   fetchBtn.click();
10292         //   expect(status.getText()).toMatch('200');
10293         //   expect(data.getText()).toMatch(/Super Hero!/);
10294         // });
10295
10296           it('should make JSONP request to invalid URL and invoke the error handler',
10297               function() {
10298             invalidJsonpBtn.click();
10299             fetchBtn.click();
10300             expect(status.getText()).toMatch('0');
10301             expect(data.getText()).toMatch('Request failed');
10302           });
10303         </file>
10304         </example>
10305              */
10306             function $http(requestConfig) {
10307
10308               if (!angular.isObject(requestConfig)) {
10309                 throw minErr('$http')('badreq', 'Http request configuration must be an object.  Received: {0}', requestConfig);
10310               }
10311
10312               var config = extend({
10313                 method: 'get',
10314                 transformRequest: defaults.transformRequest,
10315                 transformResponse: defaults.transformResponse,
10316                 paramSerializer: defaults.paramSerializer
10317               }, requestConfig);
10318
10319               config.headers = mergeHeaders(requestConfig);
10320               config.method = uppercase(config.method);
10321               config.paramSerializer = isString(config.paramSerializer) ?
10322                 $injector.get(config.paramSerializer) : config.paramSerializer;
10323
10324               var serverRequest = function(config) {
10325                 var headers = config.headers;
10326                 var reqData = transformData(config.data, headersGetter(headers), undefined, config.transformRequest);
10327
10328                 // strip content-type if data is undefined
10329                 if (isUndefined(reqData)) {
10330                   forEach(headers, function(value, header) {
10331                     if (lowercase(header) === 'content-type') {
10332                         delete headers[header];
10333                     }
10334                   });
10335                 }
10336
10337                 if (isUndefined(config.withCredentials) && !isUndefined(defaults.withCredentials)) {
10338                   config.withCredentials = defaults.withCredentials;
10339                 }
10340
10341                 // send request
10342                 return sendReq(config, reqData).then(transformResponse, transformResponse);
10343               };
10344
10345               var chain = [serverRequest, undefined];
10346               var promise = $q.when(config);
10347
10348               // apply interceptors
10349               forEach(reversedInterceptors, function(interceptor) {
10350                 if (interceptor.request || interceptor.requestError) {
10351                   chain.unshift(interceptor.request, interceptor.requestError);
10352                 }
10353                 if (interceptor.response || interceptor.responseError) {
10354                   chain.push(interceptor.response, interceptor.responseError);
10355                 }
10356               });
10357
10358               while (chain.length) {
10359                 var thenFn = chain.shift();
10360                 var rejectFn = chain.shift();
10361
10362                 promise = promise.then(thenFn, rejectFn);
10363               }
10364
10365               if (useLegacyPromise) {
10366                 promise.success = function(fn) {
10367                   assertArgFn(fn, 'fn');
10368
10369                   promise.then(function(response) {
10370                     fn(response.data, response.status, response.headers, config);
10371                   });
10372                   return promise;
10373                 };
10374
10375                 promise.error = function(fn) {
10376                   assertArgFn(fn, 'fn');
10377
10378                   promise.then(null, function(response) {
10379                     fn(response.data, response.status, response.headers, config);
10380                   });
10381                   return promise;
10382                 };
10383               } else {
10384                 promise.success = $httpMinErrLegacyFn('success');
10385                 promise.error = $httpMinErrLegacyFn('error');
10386               }
10387
10388               return promise;
10389
10390               function transformResponse(response) {
10391                 // make a copy since the response must be cacheable
10392                 var resp = extend({}, response);
10393                 resp.data = transformData(response.data, response.headers, response.status,
10394                                           config.transformResponse);
10395                 return (isSuccess(response.status))
10396                   ? resp
10397                   : $q.reject(resp);
10398               }
10399
10400               function executeHeaderFns(headers, config) {
10401                 var headerContent, processedHeaders = {};
10402
10403                 forEach(headers, function(headerFn, header) {
10404                   if (isFunction(headerFn)) {
10405                     headerContent = headerFn(config);
10406                     if (headerContent != null) {
10407                       processedHeaders[header] = headerContent;
10408                     }
10409                   } else {
10410                     processedHeaders[header] = headerFn;
10411                   }
10412                 });
10413
10414                 return processedHeaders;
10415               }
10416
10417               function mergeHeaders(config) {
10418                 var defHeaders = defaults.headers,
10419                     reqHeaders = extend({}, config.headers),
10420                     defHeaderName, lowercaseDefHeaderName, reqHeaderName;
10421
10422                 defHeaders = extend({}, defHeaders.common, defHeaders[lowercase(config.method)]);
10423
10424                 // using for-in instead of forEach to avoid unecessary iteration after header has been found
10425                 defaultHeadersIteration:
10426                 for (defHeaderName in defHeaders) {
10427                   lowercaseDefHeaderName = lowercase(defHeaderName);
10428
10429                   for (reqHeaderName in reqHeaders) {
10430                     if (lowercase(reqHeaderName) === lowercaseDefHeaderName) {
10431                       continue defaultHeadersIteration;
10432                     }
10433                   }
10434
10435                   reqHeaders[defHeaderName] = defHeaders[defHeaderName];
10436                 }
10437
10438                 // execute if header value is a function for merged headers
10439                 return executeHeaderFns(reqHeaders, shallowCopy(config));
10440               }
10441             }
10442
10443             $http.pendingRequests = [];
10444
10445             /**
10446              * @ngdoc method
10447              * @name $http#get
10448              *
10449              * @description
10450              * Shortcut method to perform `GET` request.
10451              *
10452              * @param {string} url Relative or absolute URL specifying the destination of the request
10453              * @param {Object=} config Optional configuration object
10454              * @returns {HttpPromise} Future object
10455              */
10456
10457             /**
10458              * @ngdoc method
10459              * @name $http#delete
10460              *
10461              * @description
10462              * Shortcut method to perform `DELETE` request.
10463              *
10464              * @param {string} url Relative or absolute URL specifying the destination of the request
10465              * @param {Object=} config Optional configuration object
10466              * @returns {HttpPromise} Future object
10467              */
10468
10469             /**
10470              * @ngdoc method
10471              * @name $http#head
10472              *
10473              * @description
10474              * Shortcut method to perform `HEAD` request.
10475              *
10476              * @param {string} url Relative or absolute URL specifying the destination of the request
10477              * @param {Object=} config Optional configuration object
10478              * @returns {HttpPromise} Future object
10479              */
10480
10481             /**
10482              * @ngdoc method
10483              * @name $http#jsonp
10484              *
10485              * @description
10486              * Shortcut method to perform `JSONP` request.
10487              *
10488              * @param {string} url Relative or absolute URL specifying the destination of the request.
10489              *                     The name of the callback should be the string `JSON_CALLBACK`.
10490              * @param {Object=} config Optional configuration object
10491              * @returns {HttpPromise} Future object
10492              */
10493             createShortMethods('get', 'delete', 'head', 'jsonp');
10494
10495             /**
10496              * @ngdoc method
10497              * @name $http#post
10498              *
10499              * @description
10500              * Shortcut method to perform `POST` request.
10501              *
10502              * @param {string} url Relative or absolute URL specifying the destination of the request
10503              * @param {*} data Request content
10504              * @param {Object=} config Optional configuration object
10505              * @returns {HttpPromise} Future object
10506              */
10507
10508             /**
10509              * @ngdoc method
10510              * @name $http#put
10511              *
10512              * @description
10513              * Shortcut method to perform `PUT` request.
10514              *
10515              * @param {string} url Relative or absolute URL specifying the destination of the request
10516              * @param {*} data Request content
10517              * @param {Object=} config Optional configuration object
10518              * @returns {HttpPromise} Future object
10519              */
10520
10521              /**
10522               * @ngdoc method
10523               * @name $http#patch
10524               *
10525               * @description
10526               * Shortcut method to perform `PATCH` request.
10527               *
10528               * @param {string} url Relative or absolute URL specifying the destination of the request
10529               * @param {*} data Request content
10530               * @param {Object=} config Optional configuration object
10531               * @returns {HttpPromise} Future object
10532               */
10533             createShortMethodsWithData('post', 'put', 'patch');
10534
10535                 /**
10536                  * @ngdoc property
10537                  * @name $http#defaults
10538                  *
10539                  * @description
10540                  * Runtime equivalent of the `$httpProvider.defaults` property. Allows configuration of
10541                  * default headers, withCredentials as well as request and response transformations.
10542                  *
10543                  * See "Setting HTTP Headers" and "Transforming Requests and Responses" sections above.
10544                  */
10545             $http.defaults = defaults;
10546
10547
10548             return $http;
10549
10550
10551             function createShortMethods(names) {
10552               forEach(arguments, function(name) {
10553                 $http[name] = function(url, config) {
10554                   return $http(extend({}, config || {}, {
10555                     method: name,
10556                     url: url
10557                   }));
10558                 };
10559               });
10560             }
10561
10562
10563             function createShortMethodsWithData(name) {
10564               forEach(arguments, function(name) {
10565                 $http[name] = function(url, data, config) {
10566                   return $http(extend({}, config || {}, {
10567                     method: name,
10568                     url: url,
10569                     data: data
10570                   }));
10571                 };
10572               });
10573             }
10574
10575
10576             /**
10577              * Makes the request.
10578              *
10579              * !!! ACCESSES CLOSURE VARS:
10580              * $httpBackend, defaults, $log, $rootScope, defaultCache, $http.pendingRequests
10581              */
10582             function sendReq(config, reqData) {
10583               var deferred = $q.defer(),
10584                   promise = deferred.promise,
10585                   cache,
10586                   cachedResp,
10587                   reqHeaders = config.headers,
10588                   url = buildUrl(config.url, config.paramSerializer(config.params));
10589
10590               $http.pendingRequests.push(config);
10591               promise.then(removePendingReq, removePendingReq);
10592
10593
10594               if ((config.cache || defaults.cache) && config.cache !== false &&
10595                   (config.method === 'GET' || config.method === 'JSONP')) {
10596                 cache = isObject(config.cache) ? config.cache
10597                       : isObject(defaults.cache) ? defaults.cache
10598                       : defaultCache;
10599               }
10600
10601               if (cache) {
10602                 cachedResp = cache.get(url);
10603                 if (isDefined(cachedResp)) {
10604                   if (isPromiseLike(cachedResp)) {
10605                     // cached request has already been sent, but there is no response yet
10606                     cachedResp.then(resolvePromiseWithResult, resolvePromiseWithResult);
10607                   } else {
10608                     // serving from cache
10609                     if (isArray(cachedResp)) {
10610                       resolvePromise(cachedResp[1], cachedResp[0], shallowCopy(cachedResp[2]), cachedResp[3]);
10611                     } else {
10612                       resolvePromise(cachedResp, 200, {}, 'OK');
10613                     }
10614                   }
10615                 } else {
10616                   // put the promise for the non-transformed response into cache as a placeholder
10617                   cache.put(url, promise);
10618                 }
10619               }
10620
10621
10622               // if we won't have the response in cache, set the xsrf headers and
10623               // send the request to the backend
10624               if (isUndefined(cachedResp)) {
10625                 var xsrfValue = urlIsSameOrigin(config.url)
10626                     ? $$cookieReader()[config.xsrfCookieName || defaults.xsrfCookieName]
10627                     : undefined;
10628                 if (xsrfValue) {
10629                   reqHeaders[(config.xsrfHeaderName || defaults.xsrfHeaderName)] = xsrfValue;
10630                 }
10631
10632                 $httpBackend(config.method, url, reqData, done, reqHeaders, config.timeout,
10633                     config.withCredentials, config.responseType);
10634               }
10635
10636               return promise;
10637
10638
10639               /**
10640                * Callback registered to $httpBackend():
10641                *  - caches the response if desired
10642                *  - resolves the raw $http promise
10643                *  - calls $apply
10644                */
10645               function done(status, response, headersString, statusText) {
10646                 if (cache) {
10647                   if (isSuccess(status)) {
10648                     cache.put(url, [status, response, parseHeaders(headersString), statusText]);
10649                   } else {
10650                     // remove promise from the cache
10651                     cache.remove(url);
10652                   }
10653                 }
10654
10655                 function resolveHttpPromise() {
10656                   resolvePromise(response, status, headersString, statusText);
10657                 }
10658
10659                 if (useApplyAsync) {
10660                   $rootScope.$applyAsync(resolveHttpPromise);
10661                 } else {
10662                   resolveHttpPromise();
10663                   if (!$rootScope.$$phase) $rootScope.$apply();
10664                 }
10665               }
10666
10667
10668               /**
10669                * Resolves the raw $http promise.
10670                */
10671               function resolvePromise(response, status, headers, statusText) {
10672                 //status: HTTP response status code, 0, -1 (aborted by timeout / promise)
10673                 status = status >= -1 ? status : 0;
10674
10675                 (isSuccess(status) ? deferred.resolve : deferred.reject)({
10676                   data: response,
10677                   status: status,
10678                   headers: headersGetter(headers),
10679                   config: config,
10680                   statusText: statusText
10681                 });
10682               }
10683
10684               function resolvePromiseWithResult(result) {
10685                 resolvePromise(result.data, result.status, shallowCopy(result.headers()), result.statusText);
10686               }
10687
10688               function removePendingReq() {
10689                 var idx = $http.pendingRequests.indexOf(config);
10690                 if (idx !== -1) $http.pendingRequests.splice(idx, 1);
10691               }
10692             }
10693
10694
10695             function buildUrl(url, serializedParams) {
10696               if (serializedParams.length > 0) {
10697                 url += ((url.indexOf('?') == -1) ? '?' : '&') + serializedParams;
10698               }
10699               return url;
10700             }
10701           }];
10702         }
10703
10704         /**
10705          * @ngdoc service
10706          * @name $xhrFactory
10707          *
10708          * @description
10709          * Factory function used to create XMLHttpRequest objects.
10710          *
10711          * Replace or decorate this service to create your own custom XMLHttpRequest objects.
10712          *
10713          * ```
10714          * angular.module('myApp', [])
10715          * .factory('$xhrFactory', function() {
10716          *   return function createXhr(method, url) {
10717          *     return new window.XMLHttpRequest({mozSystem: true});
10718          *   };
10719          * });
10720          * ```
10721          *
10722          * @param {string} method HTTP method of the request (GET, POST, PUT, ..)
10723          * @param {string} url URL of the request.
10724          */
10725         function $xhrFactoryProvider() {
10726           this.$get = function() {
10727             return function createXhr() {
10728               return new window.XMLHttpRequest();
10729             };
10730           };
10731         }
10732
10733         /**
10734          * @ngdoc service
10735          * @name $httpBackend
10736          * @requires $window
10737          * @requires $document
10738          * @requires $xhrFactory
10739          *
10740          * @description
10741          * HTTP backend used by the {@link ng.$http service} that delegates to
10742          * XMLHttpRequest object or JSONP and deals with browser incompatibilities.
10743          *
10744          * You should never need to use this service directly, instead use the higher-level abstractions:
10745          * {@link ng.$http $http} or {@link ngResource.$resource $resource}.
10746          *
10747          * During testing this implementation is swapped with {@link ngMock.$httpBackend mock
10748          * $httpBackend} which can be trained with responses.
10749          */
10750         function $HttpBackendProvider() {
10751           this.$get = ['$browser', '$window', '$document', '$xhrFactory', function($browser, $window, $document, $xhrFactory) {
10752             return createHttpBackend($browser, $xhrFactory, $browser.defer, $window.angular.callbacks, $document[0]);
10753           }];
10754         }
10755
10756         function createHttpBackend($browser, createXhr, $browserDefer, callbacks, rawDocument) {
10757           // TODO(vojta): fix the signature
10758           return function(method, url, post, callback, headers, timeout, withCredentials, responseType) {
10759             $browser.$$incOutstandingRequestCount();
10760             url = url || $browser.url();
10761
10762             if (lowercase(method) == 'jsonp') {
10763               var callbackId = '_' + (callbacks.counter++).toString(36);
10764               callbacks[callbackId] = function(data) {
10765                 callbacks[callbackId].data = data;
10766                 callbacks[callbackId].called = true;
10767               };
10768
10769               var jsonpDone = jsonpReq(url.replace('JSON_CALLBACK', 'angular.callbacks.' + callbackId),
10770                   callbackId, function(status, text) {
10771                 completeRequest(callback, status, callbacks[callbackId].data, "", text);
10772                 callbacks[callbackId] = noop;
10773               });
10774             } else {
10775
10776               var xhr = createXhr(method, url);
10777
10778               xhr.open(method, url, true);
10779               forEach(headers, function(value, key) {
10780                 if (isDefined(value)) {
10781                     xhr.setRequestHeader(key, value);
10782                 }
10783               });
10784
10785               xhr.onload = function requestLoaded() {
10786                 var statusText = xhr.statusText || '';
10787
10788                 // responseText is the old-school way of retrieving response (supported by IE9)
10789                 // response/responseType properties were introduced in XHR Level2 spec (supported by IE10)
10790                 var response = ('response' in xhr) ? xhr.response : xhr.responseText;
10791
10792                 // normalize IE9 bug (http://bugs.jquery.com/ticket/1450)
10793                 var status = xhr.status === 1223 ? 204 : xhr.status;
10794
10795                 // fix status code when it is 0 (0 status is undocumented).
10796                 // Occurs when accessing file resources or on Android 4.1 stock browser
10797                 // while retrieving files from application cache.
10798                 if (status === 0) {
10799                   status = response ? 200 : urlResolve(url).protocol == 'file' ? 404 : 0;
10800                 }
10801
10802                 completeRequest(callback,
10803                     status,
10804                     response,
10805                     xhr.getAllResponseHeaders(),
10806                     statusText);
10807               };
10808
10809               var requestError = function() {
10810                 // The response is always empty
10811                 // See https://xhr.spec.whatwg.org/#request-error-steps and https://fetch.spec.whatwg.org/#concept-network-error
10812                 completeRequest(callback, -1, null, null, '');
10813               };
10814
10815               xhr.onerror = requestError;
10816               xhr.onabort = requestError;
10817
10818               if (withCredentials) {
10819                 xhr.withCredentials = true;
10820               }
10821
10822               if (responseType) {
10823                 try {
10824                   xhr.responseType = responseType;
10825                 } catch (e) {
10826                   // WebKit added support for the json responseType value on 09/03/2013
10827                   // https://bugs.webkit.org/show_bug.cgi?id=73648. Versions of Safari prior to 7 are
10828                   // known to throw when setting the value "json" as the response type. Other older
10829                   // browsers implementing the responseType
10830                   //
10831                   // The json response type can be ignored if not supported, because JSON payloads are
10832                   // parsed on the client-side regardless.
10833                   if (responseType !== 'json') {
10834                     throw e;
10835                   }
10836                 }
10837               }
10838
10839               xhr.send(isUndefined(post) ? null : post);
10840             }
10841
10842             if (timeout > 0) {
10843               var timeoutId = $browserDefer(timeoutRequest, timeout);
10844             } else if (isPromiseLike(timeout)) {
10845               timeout.then(timeoutRequest);
10846             }
10847
10848
10849             function timeoutRequest() {
10850               jsonpDone && jsonpDone();
10851               xhr && xhr.abort();
10852             }
10853
10854             function completeRequest(callback, status, response, headersString, statusText) {
10855               // cancel timeout and subsequent timeout promise resolution
10856               if (isDefined(timeoutId)) {
10857                 $browserDefer.cancel(timeoutId);
10858               }
10859               jsonpDone = xhr = null;
10860
10861               callback(status, response, headersString, statusText);
10862               $browser.$$completeOutstandingRequest(noop);
10863             }
10864           };
10865
10866           function jsonpReq(url, callbackId, done) {
10867             // we can't use jQuery/jqLite here because jQuery does crazy stuff with script elements, e.g.:
10868             // - fetches local scripts via XHR and evals them
10869             // - adds and immediately removes script elements from the document
10870             var script = rawDocument.createElement('script'), callback = null;
10871             script.type = "text/javascript";
10872             script.src = url;
10873             script.async = true;
10874
10875             callback = function(event) {
10876               removeEventListenerFn(script, "load", callback);
10877               removeEventListenerFn(script, "error", callback);
10878               rawDocument.body.removeChild(script);
10879               script = null;
10880               var status = -1;
10881               var text = "unknown";
10882
10883               if (event) {
10884                 if (event.type === "load" && !callbacks[callbackId].called) {
10885                   event = { type: "error" };
10886                 }
10887                 text = event.type;
10888                 status = event.type === "error" ? 404 : 200;
10889               }
10890
10891               if (done) {
10892                 done(status, text);
10893               }
10894             };
10895
10896             addEventListenerFn(script, "load", callback);
10897             addEventListenerFn(script, "error", callback);
10898             rawDocument.body.appendChild(script);
10899             return callback;
10900           }
10901         }
10902
10903         var $interpolateMinErr = angular.$interpolateMinErr = minErr('$interpolate');
10904         $interpolateMinErr.throwNoconcat = function(text) {
10905           throw $interpolateMinErr('noconcat',
10906               "Error while interpolating: {0}\nStrict Contextual Escaping disallows " +
10907               "interpolations that concatenate multiple expressions when a trusted value is " +
10908               "required.  See http://docs.angularjs.org/api/ng.$sce", text);
10909         };
10910
10911         $interpolateMinErr.interr = function(text, err) {
10912           return $interpolateMinErr('interr', "Can't interpolate: {0}\n{1}", text, err.toString());
10913         };
10914
10915         /**
10916          * @ngdoc provider
10917          * @name $interpolateProvider
10918          *
10919          * @description
10920          *
10921          * Used for configuring the interpolation markup. Defaults to `{{` and `}}`.
10922          *
10923          * @example
10924         <example module="customInterpolationApp">
10925         <file name="index.html">
10926         <script>
10927           var customInterpolationApp = angular.module('customInterpolationApp', []);
10928
10929           customInterpolationApp.config(function($interpolateProvider) {
10930             $interpolateProvider.startSymbol('//');
10931             $interpolateProvider.endSymbol('//');
10932           });
10933
10934
10935           customInterpolationApp.controller('DemoController', function() {
10936               this.label = "This binding is brought you by // interpolation symbols.";
10937           });
10938         </script>
10939         <div ng-app="App" ng-controller="DemoController as demo">
10940             //demo.label//
10941         </div>
10942         </file>
10943         <file name="protractor.js" type="protractor">
10944           it('should interpolate binding with custom symbols', function() {
10945             expect(element(by.binding('demo.label')).getText()).toBe('This binding is brought you by // interpolation symbols.');
10946           });
10947         </file>
10948         </example>
10949          */
10950         function $InterpolateProvider() {
10951           var startSymbol = '{{';
10952           var endSymbol = '}}';
10953
10954           /**
10955            * @ngdoc method
10956            * @name $interpolateProvider#startSymbol
10957            * @description
10958            * Symbol to denote start of expression in the interpolated string. Defaults to `{{`.
10959            *
10960            * @param {string=} value new value to set the starting symbol to.
10961            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10962            */
10963           this.startSymbol = function(value) {
10964             if (value) {
10965               startSymbol = value;
10966               return this;
10967             } else {
10968               return startSymbol;
10969             }
10970           };
10971
10972           /**
10973            * @ngdoc method
10974            * @name $interpolateProvider#endSymbol
10975            * @description
10976            * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
10977            *
10978            * @param {string=} value new value to set the ending symbol to.
10979            * @returns {string|self} Returns the symbol when used as getter and self if used as setter.
10980            */
10981           this.endSymbol = function(value) {
10982             if (value) {
10983               endSymbol = value;
10984               return this;
10985             } else {
10986               return endSymbol;
10987             }
10988           };
10989
10990
10991           this.$get = ['$parse', '$exceptionHandler', '$sce', function($parse, $exceptionHandler, $sce) {
10992             var startSymbolLength = startSymbol.length,
10993                 endSymbolLength = endSymbol.length,
10994                 escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),
10995                 escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
10996
10997             function escape(ch) {
10998               return '\\\\\\' + ch;
10999             }
11000
11001             function unescapeText(text) {
11002               return text.replace(escapedStartRegexp, startSymbol).
11003                 replace(escapedEndRegexp, endSymbol);
11004             }
11005
11006             function stringify(value) {
11007               if (value == null) { // null || undefined
11008                 return '';
11009               }
11010               switch (typeof value) {
11011                 case 'string':
11012                   break;
11013                 case 'number':
11014                   value = '' + value;
11015                   break;
11016                 default:
11017                   value = toJson(value);
11018               }
11019
11020               return value;
11021             }
11022
11023             /**
11024              * @ngdoc service
11025              * @name $interpolate
11026              * @kind function
11027              *
11028              * @requires $parse
11029              * @requires $sce
11030              *
11031              * @description
11032              *
11033              * Compiles a string with markup into an interpolation function. This service is used by the
11034              * HTML {@link ng.$compile $compile} service for data binding. See
11035              * {@link ng.$interpolateProvider $interpolateProvider} for configuring the
11036              * interpolation markup.
11037              *
11038              *
11039              * ```js
11040              *   var $interpolate = ...; // injected
11041              *   var exp = $interpolate('Hello {{name | uppercase}}!');
11042              *   expect(exp({name:'Angular'})).toEqual('Hello ANGULAR!');
11043              * ```
11044              *
11045              * `$interpolate` takes an optional fourth argument, `allOrNothing`. If `allOrNothing` is
11046              * `true`, the interpolation function will return `undefined` unless all embedded expressions
11047              * evaluate to a value other than `undefined`.
11048              *
11049              * ```js
11050              *   var $interpolate = ...; // injected
11051              *   var context = {greeting: 'Hello', name: undefined };
11052              *
11053              *   // default "forgiving" mode
11054              *   var exp = $interpolate('{{greeting}} {{name}}!');
11055              *   expect(exp(context)).toEqual('Hello !');
11056              *
11057              *   // "allOrNothing" mode
11058              *   exp = $interpolate('{{greeting}} {{name}}!', false, null, true);
11059              *   expect(exp(context)).toBeUndefined();
11060              *   context.name = 'Angular';
11061              *   expect(exp(context)).toEqual('Hello Angular!');
11062              * ```
11063              *
11064              * `allOrNothing` is useful for interpolating URLs. `ngSrc` and `ngSrcset` use this behavior.
11065              *
11066              * ####Escaped Interpolation
11067              * $interpolate provides a mechanism for escaping interpolation markers. Start and end markers
11068              * can be escaped by preceding each of their characters with a REVERSE SOLIDUS U+005C (backslash).
11069              * It will be rendered as a regular start/end marker, and will not be interpreted as an expression
11070              * or binding.
11071              *
11072              * This enables web-servers to prevent script injection attacks and defacing attacks, to some
11073              * degree, while also enabling code examples to work without relying on the
11074              * {@link ng.directive:ngNonBindable ngNonBindable} directive.
11075              *
11076              * **For security purposes, it is strongly encouraged that web servers escape user-supplied data,
11077              * replacing angle brackets (&lt;, &gt;) with &amp;lt; and &amp;gt; respectively, and replacing all
11078              * interpolation start/end markers with their escaped counterparts.**
11079              *
11080              * Escaped interpolation markers are only replaced with the actual interpolation markers in rendered
11081              * output when the $interpolate service processes the text. So, for HTML elements interpolated
11082              * by {@link ng.$compile $compile}, or otherwise interpolated with the `mustHaveExpression` parameter
11083              * set to `true`, the interpolated text must contain an unescaped interpolation expression. As such,
11084              * this is typically useful only when user-data is used in rendering a template from the server, or
11085              * when otherwise untrusted data is used by a directive.
11086              *
11087              * <example>
11088              *  <file name="index.html">
11089              *    <div ng-init="username='A user'">
11090              *      <p ng-init="apptitle='Escaping demo'">{{apptitle}}: \{\{ username = "defaced value"; \}\}
11091              *        </p>
11092              *      <p><strong>{{username}}</strong> attempts to inject code which will deface the
11093              *        application, but fails to accomplish their task, because the server has correctly
11094              *        escaped the interpolation start/end markers with REVERSE SOLIDUS U+005C (backslash)
11095              *        characters.</p>
11096              *      <p>Instead, the result of the attempted script injection is visible, and can be removed
11097              *        from the database by an administrator.</p>
11098              *    </div>
11099              *  </file>
11100              * </example>
11101              *
11102              * @param {string} text The text with markup to interpolate.
11103              * @param {boolean=} mustHaveExpression if set to true then the interpolation string must have
11104              *    embedded expression in order to return an interpolation function. Strings with no
11105              *    embedded expression will return null for the interpolation function.
11106              * @param {string=} trustedContext when provided, the returned function passes the interpolated
11107              *    result through {@link ng.$sce#getTrusted $sce.getTrusted(interpolatedResult,
11108              *    trustedContext)} before returning it.  Refer to the {@link ng.$sce $sce} service that
11109              *    provides Strict Contextual Escaping for details.
11110              * @param {boolean=} allOrNothing if `true`, then the returned function returns undefined
11111              *    unless all embedded expressions evaluate to a value other than `undefined`.
11112              * @returns {function(context)} an interpolation function which is used to compute the
11113              *    interpolated string. The function has these parameters:
11114              *
11115              * - `context`: evaluation context for all expressions embedded in the interpolated text
11116              */
11117             function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
11118               allOrNothing = !!allOrNothing;
11119               var startIndex,
11120                   endIndex,
11121                   index = 0,
11122                   expressions = [],
11123                   parseFns = [],
11124                   textLength = text.length,
11125                   exp,
11126                   concat = [],
11127                   expressionPositions = [];
11128
11129               while (index < textLength) {
11130                 if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
11131                      ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
11132                   if (index !== startIndex) {
11133                     concat.push(unescapeText(text.substring(index, startIndex)));
11134                   }
11135                   exp = text.substring(startIndex + startSymbolLength, endIndex);
11136                   expressions.push(exp);
11137                   parseFns.push($parse(exp, parseStringifyInterceptor));
11138                   index = endIndex + endSymbolLength;
11139                   expressionPositions.push(concat.length);
11140                   concat.push('');
11141                 } else {
11142                   // we did not find an interpolation, so we have to add the remainder to the separators array
11143                   if (index !== textLength) {
11144                     concat.push(unescapeText(text.substring(index)));
11145                   }
11146                   break;
11147                 }
11148               }
11149
11150               // Concatenating expressions makes it hard to reason about whether some combination of
11151               // concatenated values are unsafe to use and could easily lead to XSS.  By requiring that a
11152               // single expression be used for iframe[src], object[src], etc., we ensure that the value
11153               // that's used is assigned or constructed by some JS code somewhere that is more testable or
11154               // make it obvious that you bound the value to some user controlled value.  This helps reduce
11155               // the load when auditing for XSS issues.
11156               if (trustedContext && concat.length > 1) {
11157                   $interpolateMinErr.throwNoconcat(text);
11158               }
11159
11160               if (!mustHaveExpression || expressions.length) {
11161                 var compute = function(values) {
11162                   for (var i = 0, ii = expressions.length; i < ii; i++) {
11163                     if (allOrNothing && isUndefined(values[i])) return;
11164                     concat[expressionPositions[i]] = values[i];
11165                   }
11166                   return concat.join('');
11167                 };
11168
11169                 var getValue = function(value) {
11170                   return trustedContext ?
11171                     $sce.getTrusted(trustedContext, value) :
11172                     $sce.valueOf(value);
11173                 };
11174
11175                 return extend(function interpolationFn(context) {
11176                     var i = 0;
11177                     var ii = expressions.length;
11178                     var values = new Array(ii);
11179
11180                     try {
11181                       for (; i < ii; i++) {
11182                         values[i] = parseFns[i](context);
11183                       }
11184
11185                       return compute(values);
11186                     } catch (err) {
11187                       $exceptionHandler($interpolateMinErr.interr(text, err));
11188                     }
11189
11190                   }, {
11191                   // all of these properties are undocumented for now
11192                   exp: text, //just for compatibility with regular watchers created via $watch
11193                   expressions: expressions,
11194                   $$watchDelegate: function(scope, listener) {
11195                     var lastValue;
11196                     return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
11197                       var currValue = compute(values);
11198                       if (isFunction(listener)) {
11199                         listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
11200                       }
11201                       lastValue = currValue;
11202                     });
11203                   }
11204                 });
11205               }
11206
11207               function parseStringifyInterceptor(value) {
11208                 try {
11209                   value = getValue(value);
11210                   return allOrNothing && !isDefined(value) ? value : stringify(value);
11211                 } catch (err) {
11212                   $exceptionHandler($interpolateMinErr.interr(text, err));
11213                 }
11214               }
11215             }
11216
11217
11218             /**
11219              * @ngdoc method
11220              * @name $interpolate#startSymbol
11221              * @description
11222              * Symbol to denote the start of expression in the interpolated string. Defaults to `{{`.
11223              *
11224              * Use {@link ng.$interpolateProvider#startSymbol `$interpolateProvider.startSymbol`} to change
11225              * the symbol.
11226              *
11227              * @returns {string} start symbol.
11228              */
11229             $interpolate.startSymbol = function() {
11230               return startSymbol;
11231             };
11232
11233
11234             /**
11235              * @ngdoc method
11236              * @name $interpolate#endSymbol
11237              * @description
11238              * Symbol to denote the end of expression in the interpolated string. Defaults to `}}`.
11239              *
11240              * Use {@link ng.$interpolateProvider#endSymbol `$interpolateProvider.endSymbol`} to change
11241              * the symbol.
11242              *
11243              * @returns {string} end symbol.
11244              */
11245             $interpolate.endSymbol = function() {
11246               return endSymbol;
11247             };
11248
11249             return $interpolate;
11250           }];
11251         }
11252
11253         function $IntervalProvider() {
11254           this.$get = ['$rootScope', '$window', '$q', '$$q',
11255                function($rootScope,   $window,   $q,   $$q) {
11256             var intervals = {};
11257
11258
11259              /**
11260               * @ngdoc service
11261               * @name $interval
11262               *
11263               * @description
11264               * Angular's wrapper for `window.setInterval`. The `fn` function is executed every `delay`
11265               * milliseconds.
11266               *
11267               * The return value of registering an interval function is a promise. This promise will be
11268               * notified upon each tick of the interval, and will be resolved after `count` iterations, or
11269               * run indefinitely if `count` is not defined. The value of the notification will be the
11270               * number of iterations that have run.
11271               * To cancel an interval, call `$interval.cancel(promise)`.
11272               *
11273               * In tests you can use {@link ngMock.$interval#flush `$interval.flush(millis)`} to
11274               * move forward by `millis` milliseconds and trigger any functions scheduled to run in that
11275               * time.
11276               *
11277               * <div class="alert alert-warning">
11278               * **Note**: Intervals created by this service must be explicitly destroyed when you are finished
11279               * with them.  In particular they are not automatically destroyed when a controller's scope or a
11280               * directive's element are destroyed.
11281               * You should take this into consideration and make sure to always cancel the interval at the
11282               * appropriate moment.  See the example below for more details on how and when to do this.
11283               * </div>
11284               *
11285               * @param {function()} fn A function that should be called repeatedly.
11286               * @param {number} delay Number of milliseconds between each function call.
11287               * @param {number=} [count=0] Number of times to repeat. If not set, or 0, will repeat
11288               *   indefinitely.
11289               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
11290               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
11291               * @param {...*=} Pass additional parameters to the executed function.
11292               * @returns {promise} A promise which will be notified on each iteration.
11293               *
11294               * @example
11295               * <example module="intervalExample">
11296               * <file name="index.html">
11297               *   <script>
11298               *     angular.module('intervalExample', [])
11299               *       .controller('ExampleController', ['$scope', '$interval',
11300               *         function($scope, $interval) {
11301               *           $scope.format = 'M/d/yy h:mm:ss a';
11302               *           $scope.blood_1 = 100;
11303               *           $scope.blood_2 = 120;
11304               *
11305               *           var stop;
11306               *           $scope.fight = function() {
11307               *             // Don't start a new fight if we are already fighting
11308               *             if ( angular.isDefined(stop) ) return;
11309               *
11310               *             stop = $interval(function() {
11311               *               if ($scope.blood_1 > 0 && $scope.blood_2 > 0) {
11312               *                 $scope.blood_1 = $scope.blood_1 - 3;
11313               *                 $scope.blood_2 = $scope.blood_2 - 4;
11314               *               } else {
11315               *                 $scope.stopFight();
11316               *               }
11317               *             }, 100);
11318               *           };
11319               *
11320               *           $scope.stopFight = function() {
11321               *             if (angular.isDefined(stop)) {
11322               *               $interval.cancel(stop);
11323               *               stop = undefined;
11324               *             }
11325               *           };
11326               *
11327               *           $scope.resetFight = function() {
11328               *             $scope.blood_1 = 100;
11329               *             $scope.blood_2 = 120;
11330               *           };
11331               *
11332               *           $scope.$on('$destroy', function() {
11333               *             // Make sure that the interval is destroyed too
11334               *             $scope.stopFight();
11335               *           });
11336               *         }])
11337               *       // Register the 'myCurrentTime' directive factory method.
11338               *       // We inject $interval and dateFilter service since the factory method is DI.
11339               *       .directive('myCurrentTime', ['$interval', 'dateFilter',
11340               *         function($interval, dateFilter) {
11341               *           // return the directive link function. (compile function not needed)
11342               *           return function(scope, element, attrs) {
11343               *             var format,  // date format
11344               *                 stopTime; // so that we can cancel the time updates
11345               *
11346               *             // used to update the UI
11347               *             function updateTime() {
11348               *               element.text(dateFilter(new Date(), format));
11349               *             }
11350               *
11351               *             // watch the expression, and update the UI on change.
11352               *             scope.$watch(attrs.myCurrentTime, function(value) {
11353               *               format = value;
11354               *               updateTime();
11355               *             });
11356               *
11357               *             stopTime = $interval(updateTime, 1000);
11358               *
11359               *             // listen on DOM destroy (removal) event, and cancel the next UI update
11360               *             // to prevent updating time after the DOM element was removed.
11361               *             element.on('$destroy', function() {
11362               *               $interval.cancel(stopTime);
11363               *             });
11364               *           }
11365               *         }]);
11366               *   </script>
11367               *
11368               *   <div>
11369               *     <div ng-controller="ExampleController">
11370               *       <label>Date format: <input ng-model="format"></label> <hr/>
11371               *       Current time is: <span my-current-time="format"></span>
11372               *       <hr/>
11373               *       Blood 1 : <font color='red'>{{blood_1}}</font>
11374               *       Blood 2 : <font color='red'>{{blood_2}}</font>
11375               *       <button type="button" data-ng-click="fight()">Fight</button>
11376               *       <button type="button" data-ng-click="stopFight()">StopFight</button>
11377               *       <button type="button" data-ng-click="resetFight()">resetFight</button>
11378               *     </div>
11379               *   </div>
11380               *
11381               * </file>
11382               * </example>
11383               */
11384             function interval(fn, delay, count, invokeApply) {
11385               var hasParams = arguments.length > 4,
11386                   args = hasParams ? sliceArgs(arguments, 4) : [],
11387                   setInterval = $window.setInterval,
11388                   clearInterval = $window.clearInterval,
11389                   iteration = 0,
11390                   skipApply = (isDefined(invokeApply) && !invokeApply),
11391                   deferred = (skipApply ? $$q : $q).defer(),
11392                   promise = deferred.promise;
11393
11394               count = isDefined(count) ? count : 0;
11395
11396               promise.then(null, null, (!hasParams) ? fn : function() {
11397                 fn.apply(null, args);
11398               });
11399
11400               promise.$$intervalId = setInterval(function tick() {
11401                 deferred.notify(iteration++);
11402
11403                 if (count > 0 && iteration >= count) {
11404                   deferred.resolve(iteration);
11405                   clearInterval(promise.$$intervalId);
11406                   delete intervals[promise.$$intervalId];
11407                 }
11408
11409                 if (!skipApply) $rootScope.$apply();
11410
11411               }, delay);
11412
11413               intervals[promise.$$intervalId] = deferred;
11414
11415               return promise;
11416             }
11417
11418
11419              /**
11420               * @ngdoc method
11421               * @name $interval#cancel
11422               *
11423               * @description
11424               * Cancels a task associated with the `promise`.
11425               *
11426               * @param {Promise=} promise returned by the `$interval` function.
11427               * @returns {boolean} Returns `true` if the task was successfully canceled.
11428               */
11429             interval.cancel = function(promise) {
11430               if (promise && promise.$$intervalId in intervals) {
11431                 intervals[promise.$$intervalId].reject('canceled');
11432                 $window.clearInterval(promise.$$intervalId);
11433                 delete intervals[promise.$$intervalId];
11434                 return true;
11435               }
11436               return false;
11437             };
11438
11439             return interval;
11440           }];
11441         }
11442
11443         /**
11444          * @ngdoc service
11445          * @name $locale
11446          *
11447          * @description
11448          * $locale service provides localization rules for various Angular components. As of right now the
11449          * only public api is:
11450          *
11451          * * `id` – `{string}` – locale id formatted as `languageId-countryId` (e.g. `en-us`)
11452          */
11453
11454         var PATH_MATCH = /^([^\?#]*)(\?([^#]*))?(#(.*))?$/,
11455             DEFAULT_PORTS = {'http': 80, 'https': 443, 'ftp': 21};
11456         var $locationMinErr = minErr('$location');
11457
11458
11459         /**
11460          * Encode path using encodeUriSegment, ignoring forward slashes
11461          *
11462          * @param {string} path Path to encode
11463          * @returns {string}
11464          */
11465         function encodePath(path) {
11466           var segments = path.split('/'),
11467               i = segments.length;
11468
11469           while (i--) {
11470             segments[i] = encodeUriSegment(segments[i]);
11471           }
11472
11473           return segments.join('/');
11474         }
11475
11476         function parseAbsoluteUrl(absoluteUrl, locationObj) {
11477           var parsedUrl = urlResolve(absoluteUrl);
11478
11479           locationObj.$$protocol = parsedUrl.protocol;
11480           locationObj.$$host = parsedUrl.hostname;
11481           locationObj.$$port = toInt(parsedUrl.port) || DEFAULT_PORTS[parsedUrl.protocol] || null;
11482         }
11483
11484
11485         function parseAppUrl(relativeUrl, locationObj) {
11486           var prefixed = (relativeUrl.charAt(0) !== '/');
11487           if (prefixed) {
11488             relativeUrl = '/' + relativeUrl;
11489           }
11490           var match = urlResolve(relativeUrl);
11491           locationObj.$$path = decodeURIComponent(prefixed && match.pathname.charAt(0) === '/' ?
11492               match.pathname.substring(1) : match.pathname);
11493           locationObj.$$search = parseKeyValue(match.search);
11494           locationObj.$$hash = decodeURIComponent(match.hash);
11495
11496           // make sure path starts with '/';
11497           if (locationObj.$$path && locationObj.$$path.charAt(0) != '/') {
11498             locationObj.$$path = '/' + locationObj.$$path;
11499           }
11500         }
11501
11502
11503         /**
11504          *
11505          * @param {string} begin
11506          * @param {string} whole
11507          * @returns {string} returns text from whole after begin or undefined if it does not begin with
11508          *                   expected string.
11509          */
11510         function beginsWith(begin, whole) {
11511           if (whole.indexOf(begin) === 0) {
11512             return whole.substr(begin.length);
11513           }
11514         }
11515
11516
11517         function stripHash(url) {
11518           var index = url.indexOf('#');
11519           return index == -1 ? url : url.substr(0, index);
11520         }
11521
11522         function trimEmptyHash(url) {
11523           return url.replace(/(#.+)|#$/, '$1');
11524         }
11525
11526
11527         function stripFile(url) {
11528           return url.substr(0, stripHash(url).lastIndexOf('/') + 1);
11529         }
11530
11531         /* return the server only (scheme://host:port) */
11532         function serverBase(url) {
11533           return url.substring(0, url.indexOf('/', url.indexOf('//') + 2));
11534         }
11535
11536
11537         /**
11538          * LocationHtml5Url represents an url
11539          * This object is exposed as $location service when HTML5 mode is enabled and supported
11540          *
11541          * @constructor
11542          * @param {string} appBase application base URL
11543          * @param {string} appBaseNoFile application base URL stripped of any filename
11544          * @param {string} basePrefix url path prefix
11545          */
11546         function LocationHtml5Url(appBase, appBaseNoFile, basePrefix) {
11547           this.$$html5 = true;
11548           basePrefix = basePrefix || '';
11549           parseAbsoluteUrl(appBase, this);
11550
11551
11552           /**
11553            * Parse given html5 (regular) url string into properties
11554            * @param {string} url HTML5 url
11555            * @private
11556            */
11557           this.$$parse = function(url) {
11558             var pathUrl = beginsWith(appBaseNoFile, url);
11559             if (!isString(pathUrl)) {
11560               throw $locationMinErr('ipthprfx', 'Invalid url "{0}", missing path prefix "{1}".', url,
11561                   appBaseNoFile);
11562             }
11563
11564             parseAppUrl(pathUrl, this);
11565
11566             if (!this.$$path) {
11567               this.$$path = '/';
11568             }
11569
11570             this.$$compose();
11571           };
11572
11573           /**
11574            * Compose url and update `absUrl` property
11575            * @private
11576            */
11577           this.$$compose = function() {
11578             var search = toKeyValue(this.$$search),
11579                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11580
11581             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11582             this.$$absUrl = appBaseNoFile + this.$$url.substr(1); // first char is always '/'
11583           };
11584
11585           this.$$parseLinkUrl = function(url, relHref) {
11586             if (relHref && relHref[0] === '#') {
11587               // special case for links to hash fragments:
11588               // keep the old url and only replace the hash fragment
11589               this.hash(relHref.slice(1));
11590               return true;
11591             }
11592             var appUrl, prevAppUrl;
11593             var rewrittenUrl;
11594
11595             if (isDefined(appUrl = beginsWith(appBase, url))) {
11596               prevAppUrl = appUrl;
11597               if (isDefined(appUrl = beginsWith(basePrefix, appUrl))) {
11598                 rewrittenUrl = appBaseNoFile + (beginsWith('/', appUrl) || appUrl);
11599               } else {
11600                 rewrittenUrl = appBase + prevAppUrl;
11601               }
11602             } else if (isDefined(appUrl = beginsWith(appBaseNoFile, url))) {
11603               rewrittenUrl = appBaseNoFile + appUrl;
11604             } else if (appBaseNoFile == url + '/') {
11605               rewrittenUrl = appBaseNoFile;
11606             }
11607             if (rewrittenUrl) {
11608               this.$$parse(rewrittenUrl);
11609             }
11610             return !!rewrittenUrl;
11611           };
11612         }
11613
11614
11615         /**
11616          * LocationHashbangUrl represents url
11617          * This object is exposed as $location service when developer doesn't opt into html5 mode.
11618          * It also serves as the base class for html5 mode fallback on legacy browsers.
11619          *
11620          * @constructor
11621          * @param {string} appBase application base URL
11622          * @param {string} appBaseNoFile application base URL stripped of any filename
11623          * @param {string} hashPrefix hashbang prefix
11624          */
11625         function LocationHashbangUrl(appBase, appBaseNoFile, hashPrefix) {
11626
11627           parseAbsoluteUrl(appBase, this);
11628
11629
11630           /**
11631            * Parse given hashbang url into properties
11632            * @param {string} url Hashbang url
11633            * @private
11634            */
11635           this.$$parse = function(url) {
11636             var withoutBaseUrl = beginsWith(appBase, url) || beginsWith(appBaseNoFile, url);
11637             var withoutHashUrl;
11638
11639             if (!isUndefined(withoutBaseUrl) && withoutBaseUrl.charAt(0) === '#') {
11640
11641               // The rest of the url starts with a hash so we have
11642               // got either a hashbang path or a plain hash fragment
11643               withoutHashUrl = beginsWith(hashPrefix, withoutBaseUrl);
11644               if (isUndefined(withoutHashUrl)) {
11645                 // There was no hashbang prefix so we just have a hash fragment
11646                 withoutHashUrl = withoutBaseUrl;
11647               }
11648
11649             } else {
11650               // There was no hashbang path nor hash fragment:
11651               // If we are in HTML5 mode we use what is left as the path;
11652               // Otherwise we ignore what is left
11653               if (this.$$html5) {
11654                 withoutHashUrl = withoutBaseUrl;
11655               } else {
11656                 withoutHashUrl = '';
11657                 if (isUndefined(withoutBaseUrl)) {
11658                   appBase = url;
11659                   this.replace();
11660                 }
11661               }
11662             }
11663
11664             parseAppUrl(withoutHashUrl, this);
11665
11666             this.$$path = removeWindowsDriveName(this.$$path, withoutHashUrl, appBase);
11667
11668             this.$$compose();
11669
11670             /*
11671              * In Windows, on an anchor node on documents loaded from
11672              * the filesystem, the browser will return a pathname
11673              * prefixed with the drive name ('/C:/path') when a
11674              * pathname without a drive is set:
11675              *  * a.setAttribute('href', '/foo')
11676              *   * a.pathname === '/C:/foo' //true
11677              *
11678              * Inside of Angular, we're always using pathnames that
11679              * do not include drive names for routing.
11680              */
11681             function removeWindowsDriveName(path, url, base) {
11682               /*
11683               Matches paths for file protocol on windows,
11684               such as /C:/foo/bar, and captures only /foo/bar.
11685               */
11686               var windowsFilePathExp = /^\/[A-Z]:(\/.*)/;
11687
11688               var firstPathSegmentMatch;
11689
11690               //Get the relative path from the input URL.
11691               if (url.indexOf(base) === 0) {
11692                 url = url.replace(base, '');
11693               }
11694
11695               // The input URL intentionally contains a first path segment that ends with a colon.
11696               if (windowsFilePathExp.exec(url)) {
11697                 return path;
11698               }
11699
11700               firstPathSegmentMatch = windowsFilePathExp.exec(path);
11701               return firstPathSegmentMatch ? firstPathSegmentMatch[1] : path;
11702             }
11703           };
11704
11705           /**
11706            * Compose hashbang url and update `absUrl` property
11707            * @private
11708            */
11709           this.$$compose = function() {
11710             var search = toKeyValue(this.$$search),
11711                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11712
11713             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11714             this.$$absUrl = appBase + (this.$$url ? hashPrefix + this.$$url : '');
11715           };
11716
11717           this.$$parseLinkUrl = function(url, relHref) {
11718             if (stripHash(appBase) == stripHash(url)) {
11719               this.$$parse(url);
11720               return true;
11721             }
11722             return false;
11723           };
11724         }
11725
11726
11727         /**
11728          * LocationHashbangUrl represents url
11729          * This object is exposed as $location service when html5 history api is enabled but the browser
11730          * does not support it.
11731          *
11732          * @constructor
11733          * @param {string} appBase application base URL
11734          * @param {string} appBaseNoFile application base URL stripped of any filename
11735          * @param {string} hashPrefix hashbang prefix
11736          */
11737         function LocationHashbangInHtml5Url(appBase, appBaseNoFile, hashPrefix) {
11738           this.$$html5 = true;
11739           LocationHashbangUrl.apply(this, arguments);
11740
11741           this.$$parseLinkUrl = function(url, relHref) {
11742             if (relHref && relHref[0] === '#') {
11743               // special case for links to hash fragments:
11744               // keep the old url and only replace the hash fragment
11745               this.hash(relHref.slice(1));
11746               return true;
11747             }
11748
11749             var rewrittenUrl;
11750             var appUrl;
11751
11752             if (appBase == stripHash(url)) {
11753               rewrittenUrl = url;
11754             } else if ((appUrl = beginsWith(appBaseNoFile, url))) {
11755               rewrittenUrl = appBase + hashPrefix + appUrl;
11756             } else if (appBaseNoFile === url + '/') {
11757               rewrittenUrl = appBaseNoFile;
11758             }
11759             if (rewrittenUrl) {
11760               this.$$parse(rewrittenUrl);
11761             }
11762             return !!rewrittenUrl;
11763           };
11764
11765           this.$$compose = function() {
11766             var search = toKeyValue(this.$$search),
11767                 hash = this.$$hash ? '#' + encodeUriSegment(this.$$hash) : '';
11768
11769             this.$$url = encodePath(this.$$path) + (search ? '?' + search : '') + hash;
11770             // include hashPrefix in $$absUrl when $$url is empty so IE9 does not reload page because of removal of '#'
11771             this.$$absUrl = appBase + hashPrefix + this.$$url;
11772           };
11773
11774         }
11775
11776
11777         var locationPrototype = {
11778
11779           /**
11780            * Are we in html5 mode?
11781            * @private
11782            */
11783           $$html5: false,
11784
11785           /**
11786            * Has any change been replacing?
11787            * @private
11788            */
11789           $$replace: false,
11790
11791           /**
11792            * @ngdoc method
11793            * @name $location#absUrl
11794            *
11795            * @description
11796            * This method is getter only.
11797            *
11798            * Return full url representation with all segments encoded according to rules specified in
11799            * [RFC 3986](http://www.ietf.org/rfc/rfc3986.txt).
11800            *
11801            *
11802            * ```js
11803            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11804            * var absUrl = $location.absUrl();
11805            * // => "http://example.com/#/some/path?foo=bar&baz=xoxo"
11806            * ```
11807            *
11808            * @return {string} full url
11809            */
11810           absUrl: locationGetter('$$absUrl'),
11811
11812           /**
11813            * @ngdoc method
11814            * @name $location#url
11815            *
11816            * @description
11817            * This method is getter / setter.
11818            *
11819            * Return url (e.g. `/path?a=b#hash`) when called without any parameter.
11820            *
11821            * Change path, search and hash, when called with parameter and return `$location`.
11822            *
11823            *
11824            * ```js
11825            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11826            * var url = $location.url();
11827            * // => "/some/path?foo=bar&baz=xoxo"
11828            * ```
11829            *
11830            * @param {string=} url New url without base prefix (e.g. `/path?a=b#hash`)
11831            * @return {string} url
11832            */
11833           url: function(url) {
11834             if (isUndefined(url)) {
11835               return this.$$url;
11836             }
11837
11838             var match = PATH_MATCH.exec(url);
11839             if (match[1] || url === '') this.path(decodeURIComponent(match[1]));
11840             if (match[2] || match[1] || url === '') this.search(match[3] || '');
11841             this.hash(match[5] || '');
11842
11843             return this;
11844           },
11845
11846           /**
11847            * @ngdoc method
11848            * @name $location#protocol
11849            *
11850            * @description
11851            * This method is getter only.
11852            *
11853            * Return protocol of current url.
11854            *
11855            *
11856            * ```js
11857            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11858            * var protocol = $location.protocol();
11859            * // => "http"
11860            * ```
11861            *
11862            * @return {string} protocol of current url
11863            */
11864           protocol: locationGetter('$$protocol'),
11865
11866           /**
11867            * @ngdoc method
11868            * @name $location#host
11869            *
11870            * @description
11871            * This method is getter only.
11872            *
11873            * Return host of current url.
11874            *
11875            * Note: compared to the non-angular version `location.host` which returns `hostname:port`, this returns the `hostname` portion only.
11876            *
11877            *
11878            * ```js
11879            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11880            * var host = $location.host();
11881            * // => "example.com"
11882            *
11883            * // given url http://user:password@example.com:8080/#/some/path?foo=bar&baz=xoxo
11884            * host = $location.host();
11885            * // => "example.com"
11886            * host = location.host;
11887            * // => "example.com:8080"
11888            * ```
11889            *
11890            * @return {string} host of current url.
11891            */
11892           host: locationGetter('$$host'),
11893
11894           /**
11895            * @ngdoc method
11896            * @name $location#port
11897            *
11898            * @description
11899            * This method is getter only.
11900            *
11901            * Return port of current url.
11902            *
11903            *
11904            * ```js
11905            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11906            * var port = $location.port();
11907            * // => 80
11908            * ```
11909            *
11910            * @return {Number} port
11911            */
11912           port: locationGetter('$$port'),
11913
11914           /**
11915            * @ngdoc method
11916            * @name $location#path
11917            *
11918            * @description
11919            * This method is getter / setter.
11920            *
11921            * Return path of current url when called without any parameter.
11922            *
11923            * Change path when called with parameter and return `$location`.
11924            *
11925            * Note: Path should always begin with forward slash (/), this method will add the forward slash
11926            * if it is missing.
11927            *
11928            *
11929            * ```js
11930            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11931            * var path = $location.path();
11932            * // => "/some/path"
11933            * ```
11934            *
11935            * @param {(string|number)=} path New path
11936            * @return {string} path
11937            */
11938           path: locationGetterSetter('$$path', function(path) {
11939             path = path !== null ? path.toString() : '';
11940             return path.charAt(0) == '/' ? path : '/' + path;
11941           }),
11942
11943           /**
11944            * @ngdoc method
11945            * @name $location#search
11946            *
11947            * @description
11948            * This method is getter / setter.
11949            *
11950            * Return search part (as object) of current url when called without any parameter.
11951            *
11952            * Change search part when called with parameter and return `$location`.
11953            *
11954            *
11955            * ```js
11956            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo
11957            * var searchObject = $location.search();
11958            * // => {foo: 'bar', baz: 'xoxo'}
11959            *
11960            * // set foo to 'yipee'
11961            * $location.search('foo', 'yipee');
11962            * // $location.search() => {foo: 'yipee', baz: 'xoxo'}
11963            * ```
11964            *
11965            * @param {string|Object.<string>|Object.<Array.<string>>} search New search params - string or
11966            * hash object.
11967            *
11968            * When called with a single argument the method acts as a setter, setting the `search` component
11969            * of `$location` to the specified value.
11970            *
11971            * If the argument is a hash object containing an array of values, these values will be encoded
11972            * as duplicate search parameters in the url.
11973            *
11974            * @param {(string|Number|Array<string>|boolean)=} paramValue If `search` is a string or number, then `paramValue`
11975            * will override only a single search property.
11976            *
11977            * If `paramValue` is an array, it will override the property of the `search` component of
11978            * `$location` specified via the first argument.
11979            *
11980            * If `paramValue` is `null`, the property specified via the first argument will be deleted.
11981            *
11982            * If `paramValue` is `true`, the property specified via the first argument will be added with no
11983            * value nor trailing equal sign.
11984            *
11985            * @return {Object} If called with no arguments returns the parsed `search` object. If called with
11986            * one or more arguments returns `$location` object itself.
11987            */
11988           search: function(search, paramValue) {
11989             switch (arguments.length) {
11990               case 0:
11991                 return this.$$search;
11992               case 1:
11993                 if (isString(search) || isNumber(search)) {
11994                   search = search.toString();
11995                   this.$$search = parseKeyValue(search);
11996                 } else if (isObject(search)) {
11997                   search = copy(search, {});
11998                   // remove object undefined or null properties
11999                   forEach(search, function(value, key) {
12000                     if (value == null) delete search[key];
12001                   });
12002
12003                   this.$$search = search;
12004                 } else {
12005                   throw $locationMinErr('isrcharg',
12006                       'The first argument of the `$location#search()` call must be a string or an object.');
12007                 }
12008                 break;
12009               default:
12010                 if (isUndefined(paramValue) || paramValue === null) {
12011                   delete this.$$search[search];
12012                 } else {
12013                   this.$$search[search] = paramValue;
12014                 }
12015             }
12016
12017             this.$$compose();
12018             return this;
12019           },
12020
12021           /**
12022            * @ngdoc method
12023            * @name $location#hash
12024            *
12025            * @description
12026            * This method is getter / setter.
12027            *
12028            * Returns the hash fragment when called without any parameters.
12029            *
12030            * Changes the hash fragment when called with a parameter and returns `$location`.
12031            *
12032            *
12033            * ```js
12034            * // given url http://example.com/#/some/path?foo=bar&baz=xoxo#hashValue
12035            * var hash = $location.hash();
12036            * // => "hashValue"
12037            * ```
12038            *
12039            * @param {(string|number)=} hash New hash fragment
12040            * @return {string} hash
12041            */
12042           hash: locationGetterSetter('$$hash', function(hash) {
12043             return hash !== null ? hash.toString() : '';
12044           }),
12045
12046           /**
12047            * @ngdoc method
12048            * @name $location#replace
12049            *
12050            * @description
12051            * If called, all changes to $location during the current `$digest` will replace the current history
12052            * record, instead of adding a new one.
12053            */
12054           replace: function() {
12055             this.$$replace = true;
12056             return this;
12057           }
12058         };
12059
12060         forEach([LocationHashbangInHtml5Url, LocationHashbangUrl, LocationHtml5Url], function(Location) {
12061           Location.prototype = Object.create(locationPrototype);
12062
12063           /**
12064            * @ngdoc method
12065            * @name $location#state
12066            *
12067            * @description
12068            * This method is getter / setter.
12069            *
12070            * Return the history state object when called without any parameter.
12071            *
12072            * Change the history state object when called with one parameter and return `$location`.
12073            * The state object is later passed to `pushState` or `replaceState`.
12074            *
12075            * NOTE: This method is supported only in HTML5 mode and only in browsers supporting
12076            * the HTML5 History API (i.e. methods `pushState` and `replaceState`). If you need to support
12077            * older browsers (like IE9 or Android < 4.0), don't use this method.
12078            *
12079            * @param {object=} state State object for pushState or replaceState
12080            * @return {object} state
12081            */
12082           Location.prototype.state = function(state) {
12083             if (!arguments.length) {
12084               return this.$$state;
12085             }
12086
12087             if (Location !== LocationHtml5Url || !this.$$html5) {
12088               throw $locationMinErr('nostate', 'History API state support is available only ' +
12089                 'in HTML5 mode and only in browsers supporting HTML5 History API');
12090             }
12091             // The user might modify `stateObject` after invoking `$location.state(stateObject)`
12092             // but we're changing the $$state reference to $browser.state() during the $digest
12093             // so the modification window is narrow.
12094             this.$$state = isUndefined(state) ? null : state;
12095
12096             return this;
12097           };
12098         });
12099
12100
12101         function locationGetter(property) {
12102           return function() {
12103             return this[property];
12104           };
12105         }
12106
12107
12108         function locationGetterSetter(property, preprocess) {
12109           return function(value) {
12110             if (isUndefined(value)) {
12111               return this[property];
12112             }
12113
12114             this[property] = preprocess(value);
12115             this.$$compose();
12116
12117             return this;
12118           };
12119         }
12120
12121
12122         /**
12123          * @ngdoc service
12124          * @name $location
12125          *
12126          * @requires $rootElement
12127          *
12128          * @description
12129          * The $location service parses the URL in the browser address bar (based on the
12130          * [window.location](https://developer.mozilla.org/en/window.location)) and makes the URL
12131          * available to your application. Changes to the URL in the address bar are reflected into
12132          * $location service and changes to $location are reflected into the browser address bar.
12133          *
12134          * **The $location service:**
12135          *
12136          * - Exposes the current URL in the browser address bar, so you can
12137          *   - Watch and observe the URL.
12138          *   - Change the URL.
12139          * - Synchronizes the URL with the browser when the user
12140          *   - Changes the address bar.
12141          *   - Clicks the back or forward button (or clicks a History link).
12142          *   - Clicks on a link.
12143          * - Represents the URL object as a set of methods (protocol, host, port, path, search, hash).
12144          *
12145          * For more information see {@link guide/$location Developer Guide: Using $location}
12146          */
12147
12148         /**
12149          * @ngdoc provider
12150          * @name $locationProvider
12151          * @description
12152          * Use the `$locationProvider` to configure how the application deep linking paths are stored.
12153          */
12154         function $LocationProvider() {
12155           var hashPrefix = '',
12156               html5Mode = {
12157                 enabled: false,
12158                 requireBase: true,
12159                 rewriteLinks: true
12160               };
12161
12162           /**
12163            * @ngdoc method
12164            * @name $locationProvider#hashPrefix
12165            * @description
12166            * @param {string=} prefix Prefix for hash part (containing path and search)
12167            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12168            */
12169           this.hashPrefix = function(prefix) {
12170             if (isDefined(prefix)) {
12171               hashPrefix = prefix;
12172               return this;
12173             } else {
12174               return hashPrefix;
12175             }
12176           };
12177
12178           /**
12179            * @ngdoc method
12180            * @name $locationProvider#html5Mode
12181            * @description
12182            * @param {(boolean|Object)=} mode If boolean, sets `html5Mode.enabled` to value.
12183            *   If object, sets `enabled`, `requireBase` and `rewriteLinks` to respective values. Supported
12184            *   properties:
12185            *   - **enabled** – `{boolean}` – (default: false) If true, will rely on `history.pushState` to
12186            *     change urls where supported. Will fall back to hash-prefixed paths in browsers that do not
12187            *     support `pushState`.
12188            *   - **requireBase** - `{boolean}` - (default: `true`) When html5Mode is enabled, specifies
12189            *     whether or not a <base> tag is required to be present. If `enabled` and `requireBase` are
12190            *     true, and a base tag is not present, an error will be thrown when `$location` is injected.
12191            *     See the {@link guide/$location $location guide for more information}
12192            *   - **rewriteLinks** - `{boolean}` - (default: `true`) When html5Mode is enabled,
12193            *     enables/disables url rewriting for relative links.
12194            *
12195            * @returns {Object} html5Mode object if used as getter or itself (chaining) if used as setter
12196            */
12197           this.html5Mode = function(mode) {
12198             if (isBoolean(mode)) {
12199               html5Mode.enabled = mode;
12200               return this;
12201             } else if (isObject(mode)) {
12202
12203               if (isBoolean(mode.enabled)) {
12204                 html5Mode.enabled = mode.enabled;
12205               }
12206
12207               if (isBoolean(mode.requireBase)) {
12208                 html5Mode.requireBase = mode.requireBase;
12209               }
12210
12211               if (isBoolean(mode.rewriteLinks)) {
12212                 html5Mode.rewriteLinks = mode.rewriteLinks;
12213               }
12214
12215               return this;
12216             } else {
12217               return html5Mode;
12218             }
12219           };
12220
12221           /**
12222            * @ngdoc event
12223            * @name $location#$locationChangeStart
12224            * @eventType broadcast on root scope
12225            * @description
12226            * Broadcasted before a URL will change.
12227            *
12228            * This change can be prevented by calling
12229            * `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on} for more
12230            * details about event object. Upon successful change
12231            * {@link ng.$location#$locationChangeSuccess $locationChangeSuccess} is fired.
12232            *
12233            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12234            * the browser supports the HTML5 History API.
12235            *
12236            * @param {Object} angularEvent Synthetic event object.
12237            * @param {string} newUrl New URL
12238            * @param {string=} oldUrl URL that was before it was changed.
12239            * @param {string=} newState New history state object
12240            * @param {string=} oldState History state object that was before it was changed.
12241            */
12242
12243           /**
12244            * @ngdoc event
12245            * @name $location#$locationChangeSuccess
12246            * @eventType broadcast on root scope
12247            * @description
12248            * Broadcasted after a URL was changed.
12249            *
12250            * The `newState` and `oldState` parameters may be defined only in HTML5 mode and when
12251            * the browser supports the HTML5 History API.
12252            *
12253            * @param {Object} angularEvent Synthetic event object.
12254            * @param {string} newUrl New URL
12255            * @param {string=} oldUrl URL that was before it was changed.
12256            * @param {string=} newState New history state object
12257            * @param {string=} oldState History state object that was before it was changed.
12258            */
12259
12260           this.$get = ['$rootScope', '$browser', '$sniffer', '$rootElement', '$window',
12261               function($rootScope, $browser, $sniffer, $rootElement, $window) {
12262             var $location,
12263                 LocationMode,
12264                 baseHref = $browser.baseHref(), // if base[href] is undefined, it defaults to ''
12265                 initialUrl = $browser.url(),
12266                 appBase;
12267
12268             if (html5Mode.enabled) {
12269               if (!baseHref && html5Mode.requireBase) {
12270                 throw $locationMinErr('nobase',
12271                   "$location in HTML5 mode requires a <base> tag to be present!");
12272               }
12273               appBase = serverBase(initialUrl) + (baseHref || '/');
12274               LocationMode = $sniffer.history ? LocationHtml5Url : LocationHashbangInHtml5Url;
12275             } else {
12276               appBase = stripHash(initialUrl);
12277               LocationMode = LocationHashbangUrl;
12278             }
12279             var appBaseNoFile = stripFile(appBase);
12280
12281             $location = new LocationMode(appBase, appBaseNoFile, '#' + hashPrefix);
12282             $location.$$parseLinkUrl(initialUrl, initialUrl);
12283
12284             $location.$$state = $browser.state();
12285
12286             var IGNORE_URI_REGEXP = /^\s*(javascript|mailto):/i;
12287
12288             function setBrowserUrlWithFallback(url, replace, state) {
12289               var oldUrl = $location.url();
12290               var oldState = $location.$$state;
12291               try {
12292                 $browser.url(url, replace, state);
12293
12294                 // Make sure $location.state() returns referentially identical (not just deeply equal)
12295                 // state object; this makes possible quick checking if the state changed in the digest
12296                 // loop. Checking deep equality would be too expensive.
12297                 $location.$$state = $browser.state();
12298               } catch (e) {
12299                 // Restore old values if pushState fails
12300                 $location.url(oldUrl);
12301                 $location.$$state = oldState;
12302
12303                 throw e;
12304               }
12305             }
12306
12307             $rootElement.on('click', function(event) {
12308               // TODO(vojta): rewrite link when opening in new tab/window (in legacy browser)
12309               // currently we open nice url link and redirect then
12310
12311               if (!html5Mode.rewriteLinks || event.ctrlKey || event.metaKey || event.shiftKey || event.which == 2 || event.button == 2) return;
12312
12313               var elm = jqLite(event.target);
12314
12315               // traverse the DOM up to find first A tag
12316               while (nodeName_(elm[0]) !== 'a') {
12317                 // ignore rewriting if no A tag (reached root element, or no parent - removed from document)
12318                 if (elm[0] === $rootElement[0] || !(elm = elm.parent())[0]) return;
12319               }
12320
12321               var absHref = elm.prop('href');
12322               // get the actual href attribute - see
12323               // http://msdn.microsoft.com/en-us/library/ie/dd347148(v=vs.85).aspx
12324               var relHref = elm.attr('href') || elm.attr('xlink:href');
12325
12326               if (isObject(absHref) && absHref.toString() === '[object SVGAnimatedString]') {
12327                 // SVGAnimatedString.animVal should be identical to SVGAnimatedString.baseVal, unless during
12328                 // an animation.
12329                 absHref = urlResolve(absHref.animVal).href;
12330               }
12331
12332               // Ignore when url is started with javascript: or mailto:
12333               if (IGNORE_URI_REGEXP.test(absHref)) return;
12334
12335               if (absHref && !elm.attr('target') && !event.isDefaultPrevented()) {
12336                 if ($location.$$parseLinkUrl(absHref, relHref)) {
12337                   // We do a preventDefault for all urls that are part of the angular application,
12338                   // in html5mode and also without, so that we are able to abort navigation without
12339                   // getting double entries in the location history.
12340                   event.preventDefault();
12341                   // update location manually
12342                   if ($location.absUrl() != $browser.url()) {
12343                     $rootScope.$apply();
12344                     // hack to work around FF6 bug 684208 when scenario runner clicks on links
12345                     $window.angular['ff-684208-preventDefault'] = true;
12346                   }
12347                 }
12348               }
12349             });
12350
12351
12352             // rewrite hashbang url <> html5 url
12353             if (trimEmptyHash($location.absUrl()) != trimEmptyHash(initialUrl)) {
12354               $browser.url($location.absUrl(), true);
12355             }
12356
12357             var initializing = true;
12358
12359             // update $location when $browser url changes
12360             $browser.onUrlChange(function(newUrl, newState) {
12361
12362               if (isUndefined(beginsWith(appBaseNoFile, newUrl))) {
12363                 // If we are navigating outside of the app then force a reload
12364                 $window.location.href = newUrl;
12365                 return;
12366               }
12367
12368               $rootScope.$evalAsync(function() {
12369                 var oldUrl = $location.absUrl();
12370                 var oldState = $location.$$state;
12371                 var defaultPrevented;
12372                 newUrl = trimEmptyHash(newUrl);
12373                 $location.$$parse(newUrl);
12374                 $location.$$state = newState;
12375
12376                 defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12377                     newState, oldState).defaultPrevented;
12378
12379                 // if the location was changed by a `$locationChangeStart` handler then stop
12380                 // processing this location change
12381                 if ($location.absUrl() !== newUrl) return;
12382
12383                 if (defaultPrevented) {
12384                   $location.$$parse(oldUrl);
12385                   $location.$$state = oldState;
12386                   setBrowserUrlWithFallback(oldUrl, false, oldState);
12387                 } else {
12388                   initializing = false;
12389                   afterLocationChange(oldUrl, oldState);
12390                 }
12391               });
12392               if (!$rootScope.$$phase) $rootScope.$digest();
12393             });
12394
12395             // update browser
12396             $rootScope.$watch(function $locationWatch() {
12397               var oldUrl = trimEmptyHash($browser.url());
12398               var newUrl = trimEmptyHash($location.absUrl());
12399               var oldState = $browser.state();
12400               var currentReplace = $location.$$replace;
12401               var urlOrStateChanged = oldUrl !== newUrl ||
12402                 ($location.$$html5 && $sniffer.history && oldState !== $location.$$state);
12403
12404               if (initializing || urlOrStateChanged) {
12405                 initializing = false;
12406
12407                 $rootScope.$evalAsync(function() {
12408                   var newUrl = $location.absUrl();
12409                   var defaultPrevented = $rootScope.$broadcast('$locationChangeStart', newUrl, oldUrl,
12410                       $location.$$state, oldState).defaultPrevented;
12411
12412                   // if the location was changed by a `$locationChangeStart` handler then stop
12413                   // processing this location change
12414                   if ($location.absUrl() !== newUrl) return;
12415
12416                   if (defaultPrevented) {
12417                     $location.$$parse(oldUrl);
12418                     $location.$$state = oldState;
12419                   } else {
12420                     if (urlOrStateChanged) {
12421                       setBrowserUrlWithFallback(newUrl, currentReplace,
12422                                                 oldState === $location.$$state ? null : $location.$$state);
12423                     }
12424                     afterLocationChange(oldUrl, oldState);
12425                   }
12426                 });
12427               }
12428
12429               $location.$$replace = false;
12430
12431               // we don't need to return anything because $evalAsync will make the digest loop dirty when
12432               // there is a change
12433             });
12434
12435             return $location;
12436
12437             function afterLocationChange(oldUrl, oldState) {
12438               $rootScope.$broadcast('$locationChangeSuccess', $location.absUrl(), oldUrl,
12439                 $location.$$state, oldState);
12440             }
12441         }];
12442         }
12443
12444         /**
12445          * @ngdoc service
12446          * @name $log
12447          * @requires $window
12448          *
12449          * @description
12450          * Simple service for logging. Default implementation safely writes the message
12451          * into the browser's console (if present).
12452          *
12453          * The main purpose of this service is to simplify debugging and troubleshooting.
12454          *
12455          * The default is to log `debug` messages. You can use
12456          * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
12457          *
12458          * @example
12459            <example module="logExample">
12460              <file name="script.js">
12461                angular.module('logExample', [])
12462                  .controller('LogController', ['$scope', '$log', function($scope, $log) {
12463                    $scope.$log = $log;
12464                    $scope.message = 'Hello World!';
12465                  }]);
12466              </file>
12467              <file name="index.html">
12468                <div ng-controller="LogController">
12469                  <p>Reload this page with open console, enter text and hit the log button...</p>
12470                  <label>Message:
12471                  <input type="text" ng-model="message" /></label>
12472                  <button ng-click="$log.log(message)">log</button>
12473                  <button ng-click="$log.warn(message)">warn</button>
12474                  <button ng-click="$log.info(message)">info</button>
12475                  <button ng-click="$log.error(message)">error</button>
12476                  <button ng-click="$log.debug(message)">debug</button>
12477                </div>
12478              </file>
12479            </example>
12480          */
12481
12482         /**
12483          * @ngdoc provider
12484          * @name $logProvider
12485          * @description
12486          * Use the `$logProvider` to configure how the application logs messages
12487          */
12488         function $LogProvider() {
12489           var debug = true,
12490               self = this;
12491
12492           /**
12493            * @ngdoc method
12494            * @name $logProvider#debugEnabled
12495            * @description
12496            * @param {boolean=} flag enable or disable debug level messages
12497            * @returns {*} current value if used as getter or itself (chaining) if used as setter
12498            */
12499           this.debugEnabled = function(flag) {
12500             if (isDefined(flag)) {
12501               debug = flag;
12502             return this;
12503             } else {
12504               return debug;
12505             }
12506           };
12507
12508           this.$get = ['$window', function($window) {
12509             return {
12510               /**
12511                * @ngdoc method
12512                * @name $log#log
12513                *
12514                * @description
12515                * Write a log message
12516                */
12517               log: consoleLog('log'),
12518
12519               /**
12520                * @ngdoc method
12521                * @name $log#info
12522                *
12523                * @description
12524                * Write an information message
12525                */
12526               info: consoleLog('info'),
12527
12528               /**
12529                * @ngdoc method
12530                * @name $log#warn
12531                *
12532                * @description
12533                * Write a warning message
12534                */
12535               warn: consoleLog('warn'),
12536
12537               /**
12538                * @ngdoc method
12539                * @name $log#error
12540                *
12541                * @description
12542                * Write an error message
12543                */
12544               error: consoleLog('error'),
12545
12546               /**
12547                * @ngdoc method
12548                * @name $log#debug
12549                *
12550                * @description
12551                * Write a debug message
12552                */
12553               debug: (function() {
12554                 var fn = consoleLog('debug');
12555
12556                 return function() {
12557                   if (debug) {
12558                     fn.apply(self, arguments);
12559                   }
12560                 };
12561               }())
12562             };
12563
12564             function formatError(arg) {
12565               if (arg instanceof Error) {
12566                 if (arg.stack) {
12567                   arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
12568                       ? 'Error: ' + arg.message + '\n' + arg.stack
12569                       : arg.stack;
12570                 } else if (arg.sourceURL) {
12571                   arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
12572                 }
12573               }
12574               return arg;
12575             }
12576
12577             function consoleLog(type) {
12578               var console = $window.console || {},
12579                   logFn = console[type] || console.log || noop,
12580                   hasApply = false;
12581
12582               // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
12583               // The reason behind this is that console.log has type "object" in IE8...
12584               try {
12585                 hasApply = !!logFn.apply;
12586               } catch (e) {}
12587
12588               if (hasApply) {
12589                 return function() {
12590                   var args = [];
12591                   forEach(arguments, function(arg) {
12592                     args.push(formatError(arg));
12593                   });
12594                   return logFn.apply(console, args);
12595                 };
12596               }
12597
12598               // we are IE which either doesn't have window.console => this is noop and we do nothing,
12599               // or we are IE where console.log doesn't have apply so we log at least first 2 args
12600               return function(arg1, arg2) {
12601                 logFn(arg1, arg2 == null ? '' : arg2);
12602               };
12603             }
12604           }];
12605         }
12606
12607         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
12608          *     Any commits to this file should be reviewed with security in mind.  *
12609          *   Changes to this file can potentially create security vulnerabilities. *
12610          *          An approval from 2 Core members with history of modifying      *
12611          *                         this file is required.                          *
12612          *                                                                         *
12613          *  Does the change somehow allow for arbitrary javascript to be executed? *
12614          *    Or allows for someone to change the prototype of built-in objects?   *
12615          *     Or gives undesired access to variables likes document or window?    *
12616          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12617
12618         var $parseMinErr = minErr('$parse');
12619
12620         // Sandboxing Angular Expressions
12621         // ------------------------------
12622         // Angular expressions are generally considered safe because these expressions only have direct
12623         // access to `$scope` and locals. However, one can obtain the ability to execute arbitrary JS code by
12624         // obtaining a reference to native JS functions such as the Function constructor.
12625         //
12626         // As an example, consider the following Angular expression:
12627         //
12628         //   {}.toString.constructor('alert("evil JS code")')
12629         //
12630         // This sandboxing technique is not perfect and doesn't aim to be. The goal is to prevent exploits
12631         // against the expression language, but not to prevent exploits that were enabled by exposing
12632         // sensitive JavaScript or browser APIs on Scope. Exposing such objects on a Scope is never a good
12633         // practice and therefore we are not even trying to protect against interaction with an object
12634         // explicitly exposed in this way.
12635         //
12636         // In general, it is not possible to access a Window object from an angular expression unless a
12637         // window or some DOM object that has a reference to window is published onto a Scope.
12638         // Similarly we prevent invocations of function known to be dangerous, as well as assignments to
12639         // native objects.
12640         //
12641         // See https://docs.angularjs.org/guide/security
12642
12643
12644         function ensureSafeMemberName(name, fullExpression) {
12645           if (name === "__defineGetter__" || name === "__defineSetter__"
12646               || name === "__lookupGetter__" || name === "__lookupSetter__"
12647               || name === "__proto__") {
12648             throw $parseMinErr('isecfld',
12649                 'Attempting to access a disallowed field in Angular expressions! '
12650                 + 'Expression: {0}', fullExpression);
12651           }
12652           return name;
12653         }
12654
12655         function getStringValue(name, fullExpression) {
12656           // From the JavaScript docs:
12657           // Property names must be strings. This means that non-string objects cannot be used
12658           // as keys in an object. Any non-string object, including a number, is typecasted
12659           // into a string via the toString method.
12660           //
12661           // So, to ensure that we are checking the same `name` that JavaScript would use,
12662           // we cast it to a string, if possible.
12663           // Doing `name + ''` can cause a repl error if the result to `toString` is not a string,
12664           // this is, this will handle objects that misbehave.
12665           name = name + '';
12666           if (!isString(name)) {
12667             throw $parseMinErr('iseccst',
12668                 'Cannot convert object to primitive value! '
12669                 + 'Expression: {0}', fullExpression);
12670           }
12671           return name;
12672         }
12673
12674         function ensureSafeObject(obj, fullExpression) {
12675           // nifty check if obj is Function that is fast and works across iframes and other contexts
12676           if (obj) {
12677             if (obj.constructor === obj) {
12678               throw $parseMinErr('isecfn',
12679                   'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12680                   fullExpression);
12681             } else if (// isWindow(obj)
12682                 obj.window === obj) {
12683               throw $parseMinErr('isecwindow',
12684                   'Referencing the Window in Angular expressions is disallowed! Expression: {0}',
12685                   fullExpression);
12686             } else if (// isElement(obj)
12687                 obj.children && (obj.nodeName || (obj.prop && obj.attr && obj.find))) {
12688               throw $parseMinErr('isecdom',
12689                   'Referencing DOM nodes in Angular expressions is disallowed! Expression: {0}',
12690                   fullExpression);
12691             } else if (// block Object so that we can't get hold of dangerous Object.* methods
12692                 obj === Object) {
12693               throw $parseMinErr('isecobj',
12694                   'Referencing Object in Angular expressions is disallowed! Expression: {0}',
12695                   fullExpression);
12696             }
12697           }
12698           return obj;
12699         }
12700
12701         var CALL = Function.prototype.call;
12702         var APPLY = Function.prototype.apply;
12703         var BIND = Function.prototype.bind;
12704
12705         function ensureSafeFunction(obj, fullExpression) {
12706           if (obj) {
12707             if (obj.constructor === obj) {
12708               throw $parseMinErr('isecfn',
12709                 'Referencing Function in Angular expressions is disallowed! Expression: {0}',
12710                 fullExpression);
12711             } else if (obj === CALL || obj === APPLY || obj === BIND) {
12712               throw $parseMinErr('isecff',
12713                 'Referencing call, apply or bind in Angular expressions is disallowed! Expression: {0}',
12714                 fullExpression);
12715             }
12716           }
12717         }
12718
12719         function ensureSafeAssignContext(obj, fullExpression) {
12720           if (obj) {
12721             if (obj === (0).constructor || obj === (false).constructor || obj === ''.constructor ||
12722                 obj === {}.constructor || obj === [].constructor || obj === Function.constructor) {
12723               throw $parseMinErr('isecaf',
12724                 'Assigning to a constructor is disallowed! Expression: {0}', fullExpression);
12725             }
12726           }
12727         }
12728
12729         var OPERATORS = createMap();
12730         forEach('+ - * / % === !== == != < > <= >= && || ! = |'.split(' '), function(operator) { OPERATORS[operator] = true; });
12731         var ESCAPE = {"n":"\n", "f":"\f", "r":"\r", "t":"\t", "v":"\v", "'":"'", '"':'"'};
12732
12733
12734         /////////////////////////////////////////
12735
12736
12737         /**
12738          * @constructor
12739          */
12740         var Lexer = function(options) {
12741           this.options = options;
12742         };
12743
12744         Lexer.prototype = {
12745           constructor: Lexer,
12746
12747           lex: function(text) {
12748             this.text = text;
12749             this.index = 0;
12750             this.tokens = [];
12751
12752             while (this.index < this.text.length) {
12753               var ch = this.text.charAt(this.index);
12754               if (ch === '"' || ch === "'") {
12755                 this.readString(ch);
12756               } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) {
12757                 this.readNumber();
12758               } else if (this.isIdent(ch)) {
12759                 this.readIdent();
12760               } else if (this.is(ch, '(){}[].,;:?')) {
12761                 this.tokens.push({index: this.index, text: ch});
12762                 this.index++;
12763               } else if (this.isWhitespace(ch)) {
12764                 this.index++;
12765               } else {
12766                 var ch2 = ch + this.peek();
12767                 var ch3 = ch2 + this.peek(2);
12768                 var op1 = OPERATORS[ch];
12769                 var op2 = OPERATORS[ch2];
12770                 var op3 = OPERATORS[ch3];
12771                 if (op1 || op2 || op3) {
12772                   var token = op3 ? ch3 : (op2 ? ch2 : ch);
12773                   this.tokens.push({index: this.index, text: token, operator: true});
12774                   this.index += token.length;
12775                 } else {
12776                   this.throwError('Unexpected next character ', this.index, this.index + 1);
12777                 }
12778               }
12779             }
12780             return this.tokens;
12781           },
12782
12783           is: function(ch, chars) {
12784             return chars.indexOf(ch) !== -1;
12785           },
12786
12787           peek: function(i) {
12788             var num = i || 1;
12789             return (this.index + num < this.text.length) ? this.text.charAt(this.index + num) : false;
12790           },
12791
12792           isNumber: function(ch) {
12793             return ('0' <= ch && ch <= '9') && typeof ch === "string";
12794           },
12795
12796           isWhitespace: function(ch) {
12797             // IE treats non-breaking space as \u00A0
12798             return (ch === ' ' || ch === '\r' || ch === '\t' ||
12799                     ch === '\n' || ch === '\v' || ch === '\u00A0');
12800           },
12801
12802           isIdent: function(ch) {
12803             return ('a' <= ch && ch <= 'z' ||
12804                     'A' <= ch && ch <= 'Z' ||
12805                     '_' === ch || ch === '$');
12806           },
12807
12808           isExpOperator: function(ch) {
12809             return (ch === '-' || ch === '+' || this.isNumber(ch));
12810           },
12811
12812           throwError: function(error, start, end) {
12813             end = end || this.index;
12814             var colStr = (isDefined(start)
12815                     ? 's ' + start +  '-' + this.index + ' [' + this.text.substring(start, end) + ']'
12816                     : ' ' + end);
12817             throw $parseMinErr('lexerr', 'Lexer Error: {0} at column{1} in expression [{2}].',
12818                 error, colStr, this.text);
12819           },
12820
12821           readNumber: function() {
12822             var number = '';
12823             var start = this.index;
12824             while (this.index < this.text.length) {
12825               var ch = lowercase(this.text.charAt(this.index));
12826               if (ch == '.' || this.isNumber(ch)) {
12827                 number += ch;
12828               } else {
12829                 var peekCh = this.peek();
12830                 if (ch == 'e' && this.isExpOperator(peekCh)) {
12831                   number += ch;
12832                 } else if (this.isExpOperator(ch) &&
12833                     peekCh && this.isNumber(peekCh) &&
12834                     number.charAt(number.length - 1) == 'e') {
12835                   number += ch;
12836                 } else if (this.isExpOperator(ch) &&
12837                     (!peekCh || !this.isNumber(peekCh)) &&
12838                     number.charAt(number.length - 1) == 'e') {
12839                   this.throwError('Invalid exponent');
12840                 } else {
12841                   break;
12842                 }
12843               }
12844               this.index++;
12845             }
12846             this.tokens.push({
12847               index: start,
12848               text: number,
12849               constant: true,
12850               value: Number(number)
12851             });
12852           },
12853
12854           readIdent: function() {
12855             var start = this.index;
12856             while (this.index < this.text.length) {
12857               var ch = this.text.charAt(this.index);
12858               if (!(this.isIdent(ch) || this.isNumber(ch))) {
12859                 break;
12860               }
12861               this.index++;
12862             }
12863             this.tokens.push({
12864               index: start,
12865               text: this.text.slice(start, this.index),
12866               identifier: true
12867             });
12868           },
12869
12870           readString: function(quote) {
12871             var start = this.index;
12872             this.index++;
12873             var string = '';
12874             var rawString = quote;
12875             var escape = false;
12876             while (this.index < this.text.length) {
12877               var ch = this.text.charAt(this.index);
12878               rawString += ch;
12879               if (escape) {
12880                 if (ch === 'u') {
12881                   var hex = this.text.substring(this.index + 1, this.index + 5);
12882                   if (!hex.match(/[\da-f]{4}/i)) {
12883                     this.throwError('Invalid unicode escape [\\u' + hex + ']');
12884                   }
12885                   this.index += 4;
12886                   string += String.fromCharCode(parseInt(hex, 16));
12887                 } else {
12888                   var rep = ESCAPE[ch];
12889                   string = string + (rep || ch);
12890                 }
12891                 escape = false;
12892               } else if (ch === '\\') {
12893                 escape = true;
12894               } else if (ch === quote) {
12895                 this.index++;
12896                 this.tokens.push({
12897                   index: start,
12898                   text: rawString,
12899                   constant: true,
12900                   value: string
12901                 });
12902                 return;
12903               } else {
12904                 string += ch;
12905               }
12906               this.index++;
12907             }
12908             this.throwError('Unterminated quote', start);
12909           }
12910         };
12911
12912         var AST = function(lexer, options) {
12913           this.lexer = lexer;
12914           this.options = options;
12915         };
12916
12917         AST.Program = 'Program';
12918         AST.ExpressionStatement = 'ExpressionStatement';
12919         AST.AssignmentExpression = 'AssignmentExpression';
12920         AST.ConditionalExpression = 'ConditionalExpression';
12921         AST.LogicalExpression = 'LogicalExpression';
12922         AST.BinaryExpression = 'BinaryExpression';
12923         AST.UnaryExpression = 'UnaryExpression';
12924         AST.CallExpression = 'CallExpression';
12925         AST.MemberExpression = 'MemberExpression';
12926         AST.Identifier = 'Identifier';
12927         AST.Literal = 'Literal';
12928         AST.ArrayExpression = 'ArrayExpression';
12929         AST.Property = 'Property';
12930         AST.ObjectExpression = 'ObjectExpression';
12931         AST.ThisExpression = 'ThisExpression';
12932
12933         // Internal use only
12934         AST.NGValueParameter = 'NGValueParameter';
12935
12936         AST.prototype = {
12937           ast: function(text) {
12938             this.text = text;
12939             this.tokens = this.lexer.lex(text);
12940
12941             var value = this.program();
12942
12943             if (this.tokens.length !== 0) {
12944               this.throwError('is an unexpected token', this.tokens[0]);
12945             }
12946
12947             return value;
12948           },
12949
12950           program: function() {
12951             var body = [];
12952             while (true) {
12953               if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
12954                 body.push(this.expressionStatement());
12955               if (!this.expect(';')) {
12956                 return { type: AST.Program, body: body};
12957               }
12958             }
12959           },
12960
12961           expressionStatement: function() {
12962             return { type: AST.ExpressionStatement, expression: this.filterChain() };
12963           },
12964
12965           filterChain: function() {
12966             var left = this.expression();
12967             var token;
12968             while ((token = this.expect('|'))) {
12969               left = this.filter(left);
12970             }
12971             return left;
12972           },
12973
12974           expression: function() {
12975             return this.assignment();
12976           },
12977
12978           assignment: function() {
12979             var result = this.ternary();
12980             if (this.expect('=')) {
12981               result = { type: AST.AssignmentExpression, left: result, right: this.assignment(), operator: '='};
12982             }
12983             return result;
12984           },
12985
12986           ternary: function() {
12987             var test = this.logicalOR();
12988             var alternate;
12989             var consequent;
12990             if (this.expect('?')) {
12991               alternate = this.expression();
12992               if (this.consume(':')) {
12993                 consequent = this.expression();
12994                 return { type: AST.ConditionalExpression, test: test, alternate: alternate, consequent: consequent};
12995               }
12996             }
12997             return test;
12998           },
12999
13000           logicalOR: function() {
13001             var left = this.logicalAND();
13002             while (this.expect('||')) {
13003               left = { type: AST.LogicalExpression, operator: '||', left: left, right: this.logicalAND() };
13004             }
13005             return left;
13006           },
13007
13008           logicalAND: function() {
13009             var left = this.equality();
13010             while (this.expect('&&')) {
13011               left = { type: AST.LogicalExpression, operator: '&&', left: left, right: this.equality()};
13012             }
13013             return left;
13014           },
13015
13016           equality: function() {
13017             var left = this.relational();
13018             var token;
13019             while ((token = this.expect('==','!=','===','!=='))) {
13020               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.relational() };
13021             }
13022             return left;
13023           },
13024
13025           relational: function() {
13026             var left = this.additive();
13027             var token;
13028             while ((token = this.expect('<', '>', '<=', '>='))) {
13029               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.additive() };
13030             }
13031             return left;
13032           },
13033
13034           additive: function() {
13035             var left = this.multiplicative();
13036             var token;
13037             while ((token = this.expect('+','-'))) {
13038               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.multiplicative() };
13039             }
13040             return left;
13041           },
13042
13043           multiplicative: function() {
13044             var left = this.unary();
13045             var token;
13046             while ((token = this.expect('*','/','%'))) {
13047               left = { type: AST.BinaryExpression, operator: token.text, left: left, right: this.unary() };
13048             }
13049             return left;
13050           },
13051
13052           unary: function() {
13053             var token;
13054             if ((token = this.expect('+', '-', '!'))) {
13055               return { type: AST.UnaryExpression, operator: token.text, prefix: true, argument: this.unary() };
13056             } else {
13057               return this.primary();
13058             }
13059           },
13060
13061           primary: function() {
13062             var primary;
13063             if (this.expect('(')) {
13064               primary = this.filterChain();
13065               this.consume(')');
13066             } else if (this.expect('[')) {
13067               primary = this.arrayDeclaration();
13068             } else if (this.expect('{')) {
13069               primary = this.object();
13070             } else if (this.constants.hasOwnProperty(this.peek().text)) {
13071               primary = copy(this.constants[this.consume().text]);
13072             } else if (this.peek().identifier) {
13073               primary = this.identifier();
13074             } else if (this.peek().constant) {
13075               primary = this.constant();
13076             } else {
13077               this.throwError('not a primary expression', this.peek());
13078             }
13079
13080             var next;
13081             while ((next = this.expect('(', '[', '.'))) {
13082               if (next.text === '(') {
13083                 primary = {type: AST.CallExpression, callee: primary, arguments: this.parseArguments() };
13084                 this.consume(')');
13085               } else if (next.text === '[') {
13086                 primary = { type: AST.MemberExpression, object: primary, property: this.expression(), computed: true };
13087                 this.consume(']');
13088               } else if (next.text === '.') {
13089                 primary = { type: AST.MemberExpression, object: primary, property: this.identifier(), computed: false };
13090               } else {
13091                 this.throwError('IMPOSSIBLE');
13092               }
13093             }
13094             return primary;
13095           },
13096
13097           filter: function(baseExpression) {
13098             var args = [baseExpression];
13099             var result = {type: AST.CallExpression, callee: this.identifier(), arguments: args, filter: true};
13100
13101             while (this.expect(':')) {
13102               args.push(this.expression());
13103             }
13104
13105             return result;
13106           },
13107
13108           parseArguments: function() {
13109             var args = [];
13110             if (this.peekToken().text !== ')') {
13111               do {
13112                 args.push(this.expression());
13113               } while (this.expect(','));
13114             }
13115             return args;
13116           },
13117
13118           identifier: function() {
13119             var token = this.consume();
13120             if (!token.identifier) {
13121               this.throwError('is not a valid identifier', token);
13122             }
13123             return { type: AST.Identifier, name: token.text };
13124           },
13125
13126           constant: function() {
13127             // TODO check that it is a constant
13128             return { type: AST.Literal, value: this.consume().value };
13129           },
13130
13131           arrayDeclaration: function() {
13132             var elements = [];
13133             if (this.peekToken().text !== ']') {
13134               do {
13135                 if (this.peek(']')) {
13136                   // Support trailing commas per ES5.1.
13137                   break;
13138                 }
13139                 elements.push(this.expression());
13140               } while (this.expect(','));
13141             }
13142             this.consume(']');
13143
13144             return { type: AST.ArrayExpression, elements: elements };
13145           },
13146
13147           object: function() {
13148             var properties = [], property;
13149             if (this.peekToken().text !== '}') {
13150               do {
13151                 if (this.peek('}')) {
13152                   // Support trailing commas per ES5.1.
13153                   break;
13154                 }
13155                 property = {type: AST.Property, kind: 'init'};
13156                 if (this.peek().constant) {
13157                   property.key = this.constant();
13158                 } else if (this.peek().identifier) {
13159                   property.key = this.identifier();
13160                 } else {
13161                   this.throwError("invalid key", this.peek());
13162                 }
13163                 this.consume(':');
13164                 property.value = this.expression();
13165                 properties.push(property);
13166               } while (this.expect(','));
13167             }
13168             this.consume('}');
13169
13170             return {type: AST.ObjectExpression, properties: properties };
13171           },
13172
13173           throwError: function(msg, token) {
13174             throw $parseMinErr('syntax',
13175                 'Syntax Error: Token \'{0}\' {1} at column {2} of the expression [{3}] starting at [{4}].',
13176                   token.text, msg, (token.index + 1), this.text, this.text.substring(token.index));
13177           },
13178
13179           consume: function(e1) {
13180             if (this.tokens.length === 0) {
13181               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13182             }
13183
13184             var token = this.expect(e1);
13185             if (!token) {
13186               this.throwError('is unexpected, expecting [' + e1 + ']', this.peek());
13187             }
13188             return token;
13189           },
13190
13191           peekToken: function() {
13192             if (this.tokens.length === 0) {
13193               throw $parseMinErr('ueoe', 'Unexpected end of expression: {0}', this.text);
13194             }
13195             return this.tokens[0];
13196           },
13197
13198           peek: function(e1, e2, e3, e4) {
13199             return this.peekAhead(0, e1, e2, e3, e4);
13200           },
13201
13202           peekAhead: function(i, e1, e2, e3, e4) {
13203             if (this.tokens.length > i) {
13204               var token = this.tokens[i];
13205               var t = token.text;
13206               if (t === e1 || t === e2 || t === e3 || t === e4 ||
13207                   (!e1 && !e2 && !e3 && !e4)) {
13208                 return token;
13209               }
13210             }
13211             return false;
13212           },
13213
13214           expect: function(e1, e2, e3, e4) {
13215             var token = this.peek(e1, e2, e3, e4);
13216             if (token) {
13217               this.tokens.shift();
13218               return token;
13219             }
13220             return false;
13221           },
13222
13223
13224           /* `undefined` is not a constant, it is an identifier,
13225            * but using it as an identifier is not supported
13226            */
13227           constants: {
13228             'true': { type: AST.Literal, value: true },
13229             'false': { type: AST.Literal, value: false },
13230             'null': { type: AST.Literal, value: null },
13231             'undefined': {type: AST.Literal, value: undefined },
13232             'this': {type: AST.ThisExpression }
13233           }
13234         };
13235
13236         function ifDefined(v, d) {
13237           return typeof v !== 'undefined' ? v : d;
13238         }
13239
13240         function plusFn(l, r) {
13241           if (typeof l === 'undefined') return r;
13242           if (typeof r === 'undefined') return l;
13243           return l + r;
13244         }
13245
13246         function isStateless($filter, filterName) {
13247           var fn = $filter(filterName);
13248           return !fn.$stateful;
13249         }
13250
13251         function findConstantAndWatchExpressions(ast, $filter) {
13252           var allConstants;
13253           var argsToWatch;
13254           switch (ast.type) {
13255           case AST.Program:
13256             allConstants = true;
13257             forEach(ast.body, function(expr) {
13258               findConstantAndWatchExpressions(expr.expression, $filter);
13259               allConstants = allConstants && expr.expression.constant;
13260             });
13261             ast.constant = allConstants;
13262             break;
13263           case AST.Literal:
13264             ast.constant = true;
13265             ast.toWatch = [];
13266             break;
13267           case AST.UnaryExpression:
13268             findConstantAndWatchExpressions(ast.argument, $filter);
13269             ast.constant = ast.argument.constant;
13270             ast.toWatch = ast.argument.toWatch;
13271             break;
13272           case AST.BinaryExpression:
13273             findConstantAndWatchExpressions(ast.left, $filter);
13274             findConstantAndWatchExpressions(ast.right, $filter);
13275             ast.constant = ast.left.constant && ast.right.constant;
13276             ast.toWatch = ast.left.toWatch.concat(ast.right.toWatch);
13277             break;
13278           case AST.LogicalExpression:
13279             findConstantAndWatchExpressions(ast.left, $filter);
13280             findConstantAndWatchExpressions(ast.right, $filter);
13281             ast.constant = ast.left.constant && ast.right.constant;
13282             ast.toWatch = ast.constant ? [] : [ast];
13283             break;
13284           case AST.ConditionalExpression:
13285             findConstantAndWatchExpressions(ast.test, $filter);
13286             findConstantAndWatchExpressions(ast.alternate, $filter);
13287             findConstantAndWatchExpressions(ast.consequent, $filter);
13288             ast.constant = ast.test.constant && ast.alternate.constant && ast.consequent.constant;
13289             ast.toWatch = ast.constant ? [] : [ast];
13290             break;
13291           case AST.Identifier:
13292             ast.constant = false;
13293             ast.toWatch = [ast];
13294             break;
13295           case AST.MemberExpression:
13296             findConstantAndWatchExpressions(ast.object, $filter);
13297             if (ast.computed) {
13298               findConstantAndWatchExpressions(ast.property, $filter);
13299             }
13300             ast.constant = ast.object.constant && (!ast.computed || ast.property.constant);
13301             ast.toWatch = [ast];
13302             break;
13303           case AST.CallExpression:
13304             allConstants = ast.filter ? isStateless($filter, ast.callee.name) : false;
13305             argsToWatch = [];
13306             forEach(ast.arguments, function(expr) {
13307               findConstantAndWatchExpressions(expr, $filter);
13308               allConstants = allConstants && expr.constant;
13309               if (!expr.constant) {
13310                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13311               }
13312             });
13313             ast.constant = allConstants;
13314             ast.toWatch = ast.filter && isStateless($filter, ast.callee.name) ? argsToWatch : [ast];
13315             break;
13316           case AST.AssignmentExpression:
13317             findConstantAndWatchExpressions(ast.left, $filter);
13318             findConstantAndWatchExpressions(ast.right, $filter);
13319             ast.constant = ast.left.constant && ast.right.constant;
13320             ast.toWatch = [ast];
13321             break;
13322           case AST.ArrayExpression:
13323             allConstants = true;
13324             argsToWatch = [];
13325             forEach(ast.elements, function(expr) {
13326               findConstantAndWatchExpressions(expr, $filter);
13327               allConstants = allConstants && expr.constant;
13328               if (!expr.constant) {
13329                 argsToWatch.push.apply(argsToWatch, expr.toWatch);
13330               }
13331             });
13332             ast.constant = allConstants;
13333             ast.toWatch = argsToWatch;
13334             break;
13335           case AST.ObjectExpression:
13336             allConstants = true;
13337             argsToWatch = [];
13338             forEach(ast.properties, function(property) {
13339               findConstantAndWatchExpressions(property.value, $filter);
13340               allConstants = allConstants && property.value.constant;
13341               if (!property.value.constant) {
13342                 argsToWatch.push.apply(argsToWatch, property.value.toWatch);
13343               }
13344             });
13345             ast.constant = allConstants;
13346             ast.toWatch = argsToWatch;
13347             break;
13348           case AST.ThisExpression:
13349             ast.constant = false;
13350             ast.toWatch = [];
13351             break;
13352           }
13353         }
13354
13355         function getInputs(body) {
13356           if (body.length != 1) return;
13357           var lastExpression = body[0].expression;
13358           var candidate = lastExpression.toWatch;
13359           if (candidate.length !== 1) return candidate;
13360           return candidate[0] !== lastExpression ? candidate : undefined;
13361         }
13362
13363         function isAssignable(ast) {
13364           return ast.type === AST.Identifier || ast.type === AST.MemberExpression;
13365         }
13366
13367         function assignableAST(ast) {
13368           if (ast.body.length === 1 && isAssignable(ast.body[0].expression)) {
13369             return {type: AST.AssignmentExpression, left: ast.body[0].expression, right: {type: AST.NGValueParameter}, operator: '='};
13370           }
13371         }
13372
13373         function isLiteral(ast) {
13374           return ast.body.length === 0 ||
13375               ast.body.length === 1 && (
13376               ast.body[0].expression.type === AST.Literal ||
13377               ast.body[0].expression.type === AST.ArrayExpression ||
13378               ast.body[0].expression.type === AST.ObjectExpression);
13379         }
13380
13381         function isConstant(ast) {
13382           return ast.constant;
13383         }
13384
13385         function ASTCompiler(astBuilder, $filter) {
13386           this.astBuilder = astBuilder;
13387           this.$filter = $filter;
13388         }
13389
13390         ASTCompiler.prototype = {
13391           compile: function(expression, expensiveChecks) {
13392             var self = this;
13393             var ast = this.astBuilder.ast(expression);
13394             this.state = {
13395               nextId: 0,
13396               filters: {},
13397               expensiveChecks: expensiveChecks,
13398               fn: {vars: [], body: [], own: {}},
13399               assign: {vars: [], body: [], own: {}},
13400               inputs: []
13401             };
13402             findConstantAndWatchExpressions(ast, self.$filter);
13403             var extra = '';
13404             var assignable;
13405             this.stage = 'assign';
13406             if ((assignable = assignableAST(ast))) {
13407               this.state.computing = 'assign';
13408               var result = this.nextId();
13409               this.recurse(assignable, result);
13410               this.return_(result);
13411               extra = 'fn.assign=' + this.generateFunction('assign', 's,v,l');
13412             }
13413             var toWatch = getInputs(ast.body);
13414             self.stage = 'inputs';
13415             forEach(toWatch, function(watch, key) {
13416               var fnKey = 'fn' + key;
13417               self.state[fnKey] = {vars: [], body: [], own: {}};
13418               self.state.computing = fnKey;
13419               var intoId = self.nextId();
13420               self.recurse(watch, intoId);
13421               self.return_(intoId);
13422               self.state.inputs.push(fnKey);
13423               watch.watchId = key;
13424             });
13425             this.state.computing = 'fn';
13426             this.stage = 'main';
13427             this.recurse(ast);
13428             var fnString =
13429               // The build and minification steps remove the string "use strict" from the code, but this is done using a regex.
13430               // This is a workaround for this until we do a better job at only removing the prefix only when we should.
13431               '"' + this.USE + ' ' + this.STRICT + '";\n' +
13432               this.filterPrefix() +
13433               'var fn=' + this.generateFunction('fn', 's,l,a,i') +
13434               extra +
13435               this.watchFns() +
13436               'return fn;';
13437
13438             /* jshint -W054 */
13439             var fn = (new Function('$filter',
13440                 'ensureSafeMemberName',
13441                 'ensureSafeObject',
13442                 'ensureSafeFunction',
13443                 'getStringValue',
13444                 'ensureSafeAssignContext',
13445                 'ifDefined',
13446                 'plus',
13447                 'text',
13448                 fnString))(
13449                   this.$filter,
13450                   ensureSafeMemberName,
13451                   ensureSafeObject,
13452                   ensureSafeFunction,
13453                   getStringValue,
13454                   ensureSafeAssignContext,
13455                   ifDefined,
13456                   plusFn,
13457                   expression);
13458             /* jshint +W054 */
13459             this.state = this.stage = undefined;
13460             fn.literal = isLiteral(ast);
13461             fn.constant = isConstant(ast);
13462             return fn;
13463           },
13464
13465           USE: 'use',
13466
13467           STRICT: 'strict',
13468
13469           watchFns: function() {
13470             var result = [];
13471             var fns = this.state.inputs;
13472             var self = this;
13473             forEach(fns, function(name) {
13474               result.push('var ' + name + '=' + self.generateFunction(name, 's'));
13475             });
13476             if (fns.length) {
13477               result.push('fn.inputs=[' + fns.join(',') + '];');
13478             }
13479             return result.join('');
13480           },
13481
13482           generateFunction: function(name, params) {
13483             return 'function(' + params + '){' +
13484                 this.varsPrefix(name) +
13485                 this.body(name) +
13486                 '};';
13487           },
13488
13489           filterPrefix: function() {
13490             var parts = [];
13491             var self = this;
13492             forEach(this.state.filters, function(id, filter) {
13493               parts.push(id + '=$filter(' + self.escape(filter) + ')');
13494             });
13495             if (parts.length) return 'var ' + parts.join(',') + ';';
13496             return '';
13497           },
13498
13499           varsPrefix: function(section) {
13500             return this.state[section].vars.length ? 'var ' + this.state[section].vars.join(',') + ';' : '';
13501           },
13502
13503           body: function(section) {
13504             return this.state[section].body.join('');
13505           },
13506
13507           recurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13508             var left, right, self = this, args, expression;
13509             recursionFn = recursionFn || noop;
13510             if (!skipWatchIdCheck && isDefined(ast.watchId)) {
13511               intoId = intoId || this.nextId();
13512               this.if_('i',
13513                 this.lazyAssign(intoId, this.computedMember('i', ast.watchId)),
13514                 this.lazyRecurse(ast, intoId, nameId, recursionFn, create, true)
13515               );
13516               return;
13517             }
13518             switch (ast.type) {
13519             case AST.Program:
13520               forEach(ast.body, function(expression, pos) {
13521                 self.recurse(expression.expression, undefined, undefined, function(expr) { right = expr; });
13522                 if (pos !== ast.body.length - 1) {
13523                   self.current().body.push(right, ';');
13524                 } else {
13525                   self.return_(right);
13526                 }
13527               });
13528               break;
13529             case AST.Literal:
13530               expression = this.escape(ast.value);
13531               this.assign(intoId, expression);
13532               recursionFn(expression);
13533               break;
13534             case AST.UnaryExpression:
13535               this.recurse(ast.argument, undefined, undefined, function(expr) { right = expr; });
13536               expression = ast.operator + '(' + this.ifDefined(right, 0) + ')';
13537               this.assign(intoId, expression);
13538               recursionFn(expression);
13539               break;
13540             case AST.BinaryExpression:
13541               this.recurse(ast.left, undefined, undefined, function(expr) { left = expr; });
13542               this.recurse(ast.right, undefined, undefined, function(expr) { right = expr; });
13543               if (ast.operator === '+') {
13544                 expression = this.plus(left, right);
13545               } else if (ast.operator === '-') {
13546                 expression = this.ifDefined(left, 0) + ast.operator + this.ifDefined(right, 0);
13547               } else {
13548                 expression = '(' + left + ')' + ast.operator + '(' + right + ')';
13549               }
13550               this.assign(intoId, expression);
13551               recursionFn(expression);
13552               break;
13553             case AST.LogicalExpression:
13554               intoId = intoId || this.nextId();
13555               self.recurse(ast.left, intoId);
13556               self.if_(ast.operator === '&&' ? intoId : self.not(intoId), self.lazyRecurse(ast.right, intoId));
13557               recursionFn(intoId);
13558               break;
13559             case AST.ConditionalExpression:
13560               intoId = intoId || this.nextId();
13561               self.recurse(ast.test, intoId);
13562               self.if_(intoId, self.lazyRecurse(ast.alternate, intoId), self.lazyRecurse(ast.consequent, intoId));
13563               recursionFn(intoId);
13564               break;
13565             case AST.Identifier:
13566               intoId = intoId || this.nextId();
13567               if (nameId) {
13568                 nameId.context = self.stage === 'inputs' ? 's' : this.assign(this.nextId(), this.getHasOwnProperty('l', ast.name) + '?l:s');
13569                 nameId.computed = false;
13570                 nameId.name = ast.name;
13571               }
13572               ensureSafeMemberName(ast.name);
13573               self.if_(self.stage === 'inputs' || self.not(self.getHasOwnProperty('l', ast.name)),
13574                 function() {
13575                   self.if_(self.stage === 'inputs' || 's', function() {
13576                     if (create && create !== 1) {
13577                       self.if_(
13578                         self.not(self.nonComputedMember('s', ast.name)),
13579                         self.lazyAssign(self.nonComputedMember('s', ast.name), '{}'));
13580                     }
13581                     self.assign(intoId, self.nonComputedMember('s', ast.name));
13582                   });
13583                 }, intoId && self.lazyAssign(intoId, self.nonComputedMember('l', ast.name))
13584                 );
13585               if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.name)) {
13586                 self.addEnsureSafeObject(intoId);
13587               }
13588               recursionFn(intoId);
13589               break;
13590             case AST.MemberExpression:
13591               left = nameId && (nameId.context = this.nextId()) || this.nextId();
13592               intoId = intoId || this.nextId();
13593               self.recurse(ast.object, left, undefined, function() {
13594                 self.if_(self.notNull(left), function() {
13595                   if (ast.computed) {
13596                     right = self.nextId();
13597                     self.recurse(ast.property, right);
13598                     self.getStringValue(right);
13599                     self.addEnsureSafeMemberName(right);
13600                     if (create && create !== 1) {
13601                       self.if_(self.not(self.computedMember(left, right)), self.lazyAssign(self.computedMember(left, right), '{}'));
13602                     }
13603                     expression = self.ensureSafeObject(self.computedMember(left, right));
13604                     self.assign(intoId, expression);
13605                     if (nameId) {
13606                       nameId.computed = true;
13607                       nameId.name = right;
13608                     }
13609                   } else {
13610                     ensureSafeMemberName(ast.property.name);
13611                     if (create && create !== 1) {
13612                       self.if_(self.not(self.nonComputedMember(left, ast.property.name)), self.lazyAssign(self.nonComputedMember(left, ast.property.name), '{}'));
13613                     }
13614                     expression = self.nonComputedMember(left, ast.property.name);
13615                     if (self.state.expensiveChecks || isPossiblyDangerousMemberName(ast.property.name)) {
13616                       expression = self.ensureSafeObject(expression);
13617                     }
13618                     self.assign(intoId, expression);
13619                     if (nameId) {
13620                       nameId.computed = false;
13621                       nameId.name = ast.property.name;
13622                     }
13623                   }
13624                 }, function() {
13625                   self.assign(intoId, 'undefined');
13626                 });
13627                 recursionFn(intoId);
13628               }, !!create);
13629               break;
13630             case AST.CallExpression:
13631               intoId = intoId || this.nextId();
13632               if (ast.filter) {
13633                 right = self.filter(ast.callee.name);
13634                 args = [];
13635                 forEach(ast.arguments, function(expr) {
13636                   var argument = self.nextId();
13637                   self.recurse(expr, argument);
13638                   args.push(argument);
13639                 });
13640                 expression = right + '(' + args.join(',') + ')';
13641                 self.assign(intoId, expression);
13642                 recursionFn(intoId);
13643               } else {
13644                 right = self.nextId();
13645                 left = {};
13646                 args = [];
13647                 self.recurse(ast.callee, right, left, function() {
13648                   self.if_(self.notNull(right), function() {
13649                     self.addEnsureSafeFunction(right);
13650                     forEach(ast.arguments, function(expr) {
13651                       self.recurse(expr, self.nextId(), undefined, function(argument) {
13652                         args.push(self.ensureSafeObject(argument));
13653                       });
13654                     });
13655                     if (left.name) {
13656                       if (!self.state.expensiveChecks) {
13657                         self.addEnsureSafeObject(left.context);
13658                       }
13659                       expression = self.member(left.context, left.name, left.computed) + '(' + args.join(',') + ')';
13660                     } else {
13661                       expression = right + '(' + args.join(',') + ')';
13662                     }
13663                     expression = self.ensureSafeObject(expression);
13664                     self.assign(intoId, expression);
13665                   }, function() {
13666                     self.assign(intoId, 'undefined');
13667                   });
13668                   recursionFn(intoId);
13669                 });
13670               }
13671               break;
13672             case AST.AssignmentExpression:
13673               right = this.nextId();
13674               left = {};
13675               if (!isAssignable(ast.left)) {
13676                 throw $parseMinErr('lval', 'Trying to assing a value to a non l-value');
13677               }
13678               this.recurse(ast.left, undefined, left, function() {
13679                 self.if_(self.notNull(left.context), function() {
13680                   self.recurse(ast.right, right);
13681                   self.addEnsureSafeObject(self.member(left.context, left.name, left.computed));
13682                   self.addEnsureSafeAssignContext(left.context);
13683                   expression = self.member(left.context, left.name, left.computed) + ast.operator + right;
13684                   self.assign(intoId, expression);
13685                   recursionFn(intoId || expression);
13686                 });
13687               }, 1);
13688               break;
13689             case AST.ArrayExpression:
13690               args = [];
13691               forEach(ast.elements, function(expr) {
13692                 self.recurse(expr, self.nextId(), undefined, function(argument) {
13693                   args.push(argument);
13694                 });
13695               });
13696               expression = '[' + args.join(',') + ']';
13697               this.assign(intoId, expression);
13698               recursionFn(expression);
13699               break;
13700             case AST.ObjectExpression:
13701               args = [];
13702               forEach(ast.properties, function(property) {
13703                 self.recurse(property.value, self.nextId(), undefined, function(expr) {
13704                   args.push(self.escape(
13705                       property.key.type === AST.Identifier ? property.key.name :
13706                         ('' + property.key.value)) +
13707                       ':' + expr);
13708                 });
13709               });
13710               expression = '{' + args.join(',') + '}';
13711               this.assign(intoId, expression);
13712               recursionFn(expression);
13713               break;
13714             case AST.ThisExpression:
13715               this.assign(intoId, 's');
13716               recursionFn('s');
13717               break;
13718             case AST.NGValueParameter:
13719               this.assign(intoId, 'v');
13720               recursionFn('v');
13721               break;
13722             }
13723           },
13724
13725           getHasOwnProperty: function(element, property) {
13726             var key = element + '.' + property;
13727             var own = this.current().own;
13728             if (!own.hasOwnProperty(key)) {
13729               own[key] = this.nextId(false, element + '&&(' + this.escape(property) + ' in ' + element + ')');
13730             }
13731             return own[key];
13732           },
13733
13734           assign: function(id, value) {
13735             if (!id) return;
13736             this.current().body.push(id, '=', value, ';');
13737             return id;
13738           },
13739
13740           filter: function(filterName) {
13741             if (!this.state.filters.hasOwnProperty(filterName)) {
13742               this.state.filters[filterName] = this.nextId(true);
13743             }
13744             return this.state.filters[filterName];
13745           },
13746
13747           ifDefined: function(id, defaultValue) {
13748             return 'ifDefined(' + id + ',' + this.escape(defaultValue) + ')';
13749           },
13750
13751           plus: function(left, right) {
13752             return 'plus(' + left + ',' + right + ')';
13753           },
13754
13755           return_: function(id) {
13756             this.current().body.push('return ', id, ';');
13757           },
13758
13759           if_: function(test, alternate, consequent) {
13760             if (test === true) {
13761               alternate();
13762             } else {
13763               var body = this.current().body;
13764               body.push('if(', test, '){');
13765               alternate();
13766               body.push('}');
13767               if (consequent) {
13768                 body.push('else{');
13769                 consequent();
13770                 body.push('}');
13771               }
13772             }
13773           },
13774
13775           not: function(expression) {
13776             return '!(' + expression + ')';
13777           },
13778
13779           notNull: function(expression) {
13780             return expression + '!=null';
13781           },
13782
13783           nonComputedMember: function(left, right) {
13784             return left + '.' + right;
13785           },
13786
13787           computedMember: function(left, right) {
13788             return left + '[' + right + ']';
13789           },
13790
13791           member: function(left, right, computed) {
13792             if (computed) return this.computedMember(left, right);
13793             return this.nonComputedMember(left, right);
13794           },
13795
13796           addEnsureSafeObject: function(item) {
13797             this.current().body.push(this.ensureSafeObject(item), ';');
13798           },
13799
13800           addEnsureSafeMemberName: function(item) {
13801             this.current().body.push(this.ensureSafeMemberName(item), ';');
13802           },
13803
13804           addEnsureSafeFunction: function(item) {
13805             this.current().body.push(this.ensureSafeFunction(item), ';');
13806           },
13807
13808           addEnsureSafeAssignContext: function(item) {
13809             this.current().body.push(this.ensureSafeAssignContext(item), ';');
13810           },
13811
13812           ensureSafeObject: function(item) {
13813             return 'ensureSafeObject(' + item + ',text)';
13814           },
13815
13816           ensureSafeMemberName: function(item) {
13817             return 'ensureSafeMemberName(' + item + ',text)';
13818           },
13819
13820           ensureSafeFunction: function(item) {
13821             return 'ensureSafeFunction(' + item + ',text)';
13822           },
13823
13824           getStringValue: function(item) {
13825             this.assign(item, 'getStringValue(' + item + ',text)');
13826           },
13827
13828           ensureSafeAssignContext: function(item) {
13829             return 'ensureSafeAssignContext(' + item + ',text)';
13830           },
13831
13832           lazyRecurse: function(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck) {
13833             var self = this;
13834             return function() {
13835               self.recurse(ast, intoId, nameId, recursionFn, create, skipWatchIdCheck);
13836             };
13837           },
13838
13839           lazyAssign: function(id, value) {
13840             var self = this;
13841             return function() {
13842               self.assign(id, value);
13843             };
13844           },
13845
13846           stringEscapeRegex: /[^ a-zA-Z0-9]/g,
13847
13848           stringEscapeFn: function(c) {
13849             return '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4);
13850           },
13851
13852           escape: function(value) {
13853             if (isString(value)) return "'" + value.replace(this.stringEscapeRegex, this.stringEscapeFn) + "'";
13854             if (isNumber(value)) return value.toString();
13855             if (value === true) return 'true';
13856             if (value === false) return 'false';
13857             if (value === null) return 'null';
13858             if (typeof value === 'undefined') return 'undefined';
13859
13860             throw $parseMinErr('esc', 'IMPOSSIBLE');
13861           },
13862
13863           nextId: function(skip, init) {
13864             var id = 'v' + (this.state.nextId++);
13865             if (!skip) {
13866               this.current().vars.push(id + (init ? '=' + init : ''));
13867             }
13868             return id;
13869           },
13870
13871           current: function() {
13872             return this.state[this.state.computing];
13873           }
13874         };
13875
13876
13877         function ASTInterpreter(astBuilder, $filter) {
13878           this.astBuilder = astBuilder;
13879           this.$filter = $filter;
13880         }
13881
13882         ASTInterpreter.prototype = {
13883           compile: function(expression, expensiveChecks) {
13884             var self = this;
13885             var ast = this.astBuilder.ast(expression);
13886             this.expression = expression;
13887             this.expensiveChecks = expensiveChecks;
13888             findConstantAndWatchExpressions(ast, self.$filter);
13889             var assignable;
13890             var assign;
13891             if ((assignable = assignableAST(ast))) {
13892               assign = this.recurse(assignable);
13893             }
13894             var toWatch = getInputs(ast.body);
13895             var inputs;
13896             if (toWatch) {
13897               inputs = [];
13898               forEach(toWatch, function(watch, key) {
13899                 var input = self.recurse(watch);
13900                 watch.input = input;
13901                 inputs.push(input);
13902                 watch.watchId = key;
13903               });
13904             }
13905             var expressions = [];
13906             forEach(ast.body, function(expression) {
13907               expressions.push(self.recurse(expression.expression));
13908             });
13909             var fn = ast.body.length === 0 ? function() {} :
13910                      ast.body.length === 1 ? expressions[0] :
13911                      function(scope, locals) {
13912                        var lastValue;
13913                        forEach(expressions, function(exp) {
13914                          lastValue = exp(scope, locals);
13915                        });
13916                        return lastValue;
13917                      };
13918             if (assign) {
13919               fn.assign = function(scope, value, locals) {
13920                 return assign(scope, locals, value);
13921               };
13922             }
13923             if (inputs) {
13924               fn.inputs = inputs;
13925             }
13926             fn.literal = isLiteral(ast);
13927             fn.constant = isConstant(ast);
13928             return fn;
13929           },
13930
13931           recurse: function(ast, context, create) {
13932             var left, right, self = this, args, expression;
13933             if (ast.input) {
13934               return this.inputs(ast.input, ast.watchId);
13935             }
13936             switch (ast.type) {
13937             case AST.Literal:
13938               return this.value(ast.value, context);
13939             case AST.UnaryExpression:
13940               right = this.recurse(ast.argument);
13941               return this['unary' + ast.operator](right, context);
13942             case AST.BinaryExpression:
13943               left = this.recurse(ast.left);
13944               right = this.recurse(ast.right);
13945               return this['binary' + ast.operator](left, right, context);
13946             case AST.LogicalExpression:
13947               left = this.recurse(ast.left);
13948               right = this.recurse(ast.right);
13949               return this['binary' + ast.operator](left, right, context);
13950             case AST.ConditionalExpression:
13951               return this['ternary?:'](
13952                 this.recurse(ast.test),
13953                 this.recurse(ast.alternate),
13954                 this.recurse(ast.consequent),
13955                 context
13956               );
13957             case AST.Identifier:
13958               ensureSafeMemberName(ast.name, self.expression);
13959               return self.identifier(ast.name,
13960                                      self.expensiveChecks || isPossiblyDangerousMemberName(ast.name),
13961                                      context, create, self.expression);
13962             case AST.MemberExpression:
13963               left = this.recurse(ast.object, false, !!create);
13964               if (!ast.computed) {
13965                 ensureSafeMemberName(ast.property.name, self.expression);
13966                 right = ast.property.name;
13967               }
13968               if (ast.computed) right = this.recurse(ast.property);
13969               return ast.computed ?
13970                 this.computedMember(left, right, context, create, self.expression) :
13971                 this.nonComputedMember(left, right, self.expensiveChecks, context, create, self.expression);
13972             case AST.CallExpression:
13973               args = [];
13974               forEach(ast.arguments, function(expr) {
13975                 args.push(self.recurse(expr));
13976               });
13977               if (ast.filter) right = this.$filter(ast.callee.name);
13978               if (!ast.filter) right = this.recurse(ast.callee, true);
13979               return ast.filter ?
13980                 function(scope, locals, assign, inputs) {
13981                   var values = [];
13982                   for (var i = 0; i < args.length; ++i) {
13983                     values.push(args[i](scope, locals, assign, inputs));
13984                   }
13985                   var value = right.apply(undefined, values, inputs);
13986                   return context ? {context: undefined, name: undefined, value: value} : value;
13987                 } :
13988                 function(scope, locals, assign, inputs) {
13989                   var rhs = right(scope, locals, assign, inputs);
13990                   var value;
13991                   if (rhs.value != null) {
13992                     ensureSafeObject(rhs.context, self.expression);
13993                     ensureSafeFunction(rhs.value, self.expression);
13994                     var values = [];
13995                     for (var i = 0; i < args.length; ++i) {
13996                       values.push(ensureSafeObject(args[i](scope, locals, assign, inputs), self.expression));
13997                     }
13998                     value = ensureSafeObject(rhs.value.apply(rhs.context, values), self.expression);
13999                   }
14000                   return context ? {value: value} : value;
14001                 };
14002             case AST.AssignmentExpression:
14003               left = this.recurse(ast.left, true, 1);
14004               right = this.recurse(ast.right);
14005               return function(scope, locals, assign, inputs) {
14006                 var lhs = left(scope, locals, assign, inputs);
14007                 var rhs = right(scope, locals, assign, inputs);
14008                 ensureSafeObject(lhs.value, self.expression);
14009                 ensureSafeAssignContext(lhs.context);
14010                 lhs.context[lhs.name] = rhs;
14011                 return context ? {value: rhs} : rhs;
14012               };
14013             case AST.ArrayExpression:
14014               args = [];
14015               forEach(ast.elements, function(expr) {
14016                 args.push(self.recurse(expr));
14017               });
14018               return function(scope, locals, assign, inputs) {
14019                 var value = [];
14020                 for (var i = 0; i < args.length; ++i) {
14021                   value.push(args[i](scope, locals, assign, inputs));
14022                 }
14023                 return context ? {value: value} : value;
14024               };
14025             case AST.ObjectExpression:
14026               args = [];
14027               forEach(ast.properties, function(property) {
14028                 args.push({key: property.key.type === AST.Identifier ?
14029                                 property.key.name :
14030                                 ('' + property.key.value),
14031                            value: self.recurse(property.value)
14032                 });
14033               });
14034               return function(scope, locals, assign, inputs) {
14035                 var value = {};
14036                 for (var i = 0; i < args.length; ++i) {
14037                   value[args[i].key] = args[i].value(scope, locals, assign, inputs);
14038                 }
14039                 return context ? {value: value} : value;
14040               };
14041             case AST.ThisExpression:
14042               return function(scope) {
14043                 return context ? {value: scope} : scope;
14044               };
14045             case AST.NGValueParameter:
14046               return function(scope, locals, assign, inputs) {
14047                 return context ? {value: assign} : assign;
14048               };
14049             }
14050           },
14051
14052           'unary+': function(argument, context) {
14053             return function(scope, locals, assign, inputs) {
14054               var arg = argument(scope, locals, assign, inputs);
14055               if (isDefined(arg)) {
14056                 arg = +arg;
14057               } else {
14058                 arg = 0;
14059               }
14060               return context ? {value: arg} : arg;
14061             };
14062           },
14063           'unary-': function(argument, context) {
14064             return function(scope, locals, assign, inputs) {
14065               var arg = argument(scope, locals, assign, inputs);
14066               if (isDefined(arg)) {
14067                 arg = -arg;
14068               } else {
14069                 arg = 0;
14070               }
14071               return context ? {value: arg} : arg;
14072             };
14073           },
14074           'unary!': function(argument, context) {
14075             return function(scope, locals, assign, inputs) {
14076               var arg = !argument(scope, locals, assign, inputs);
14077               return context ? {value: arg} : arg;
14078             };
14079           },
14080           'binary+': function(left, right, context) {
14081             return function(scope, locals, assign, inputs) {
14082               var lhs = left(scope, locals, assign, inputs);
14083               var rhs = right(scope, locals, assign, inputs);
14084               var arg = plusFn(lhs, rhs);
14085               return context ? {value: arg} : arg;
14086             };
14087           },
14088           'binary-': function(left, right, context) {
14089             return function(scope, locals, assign, inputs) {
14090               var lhs = left(scope, locals, assign, inputs);
14091               var rhs = right(scope, locals, assign, inputs);
14092               var arg = (isDefined(lhs) ? lhs : 0) - (isDefined(rhs) ? rhs : 0);
14093               return context ? {value: arg} : arg;
14094             };
14095           },
14096           'binary*': function(left, right, context) {
14097             return function(scope, locals, assign, inputs) {
14098               var arg = left(scope, locals, assign, inputs) * right(scope, locals, assign, inputs);
14099               return context ? {value: arg} : arg;
14100             };
14101           },
14102           'binary/': function(left, right, context) {
14103             return function(scope, locals, assign, inputs) {
14104               var arg = left(scope, locals, assign, inputs) / right(scope, locals, assign, inputs);
14105               return context ? {value: arg} : arg;
14106             };
14107           },
14108           'binary%': function(left, right, context) {
14109             return function(scope, locals, assign, inputs) {
14110               var arg = left(scope, locals, assign, inputs) % right(scope, locals, assign, inputs);
14111               return context ? {value: arg} : arg;
14112             };
14113           },
14114           'binary===': function(left, right, context) {
14115             return function(scope, locals, assign, inputs) {
14116               var arg = left(scope, locals, assign, inputs) === right(scope, locals, assign, inputs);
14117               return context ? {value: arg} : arg;
14118             };
14119           },
14120           'binary!==': function(left, right, context) {
14121             return function(scope, locals, assign, inputs) {
14122               var arg = left(scope, locals, assign, inputs) !== right(scope, locals, assign, inputs);
14123               return context ? {value: arg} : arg;
14124             };
14125           },
14126           'binary==': function(left, right, context) {
14127             return function(scope, locals, assign, inputs) {
14128               var arg = left(scope, locals, assign, inputs) == right(scope, locals, assign, inputs);
14129               return context ? {value: arg} : arg;
14130             };
14131           },
14132           'binary!=': function(left, right, context) {
14133             return function(scope, locals, assign, inputs) {
14134               var arg = left(scope, locals, assign, inputs) != right(scope, locals, assign, inputs);
14135               return context ? {value: arg} : arg;
14136             };
14137           },
14138           'binary<': function(left, right, context) {
14139             return function(scope, locals, assign, inputs) {
14140               var arg = left(scope, locals, assign, inputs) < right(scope, locals, assign, inputs);
14141               return context ? {value: arg} : arg;
14142             };
14143           },
14144           'binary>': function(left, right, context) {
14145             return function(scope, locals, assign, inputs) {
14146               var arg = left(scope, locals, assign, inputs) > right(scope, locals, assign, inputs);
14147               return context ? {value: arg} : arg;
14148             };
14149           },
14150           'binary<=': function(left, right, context) {
14151             return function(scope, locals, assign, inputs) {
14152               var arg = left(scope, locals, assign, inputs) <= right(scope, locals, assign, inputs);
14153               return context ? {value: arg} : arg;
14154             };
14155           },
14156           'binary>=': function(left, right, context) {
14157             return function(scope, locals, assign, inputs) {
14158               var arg = left(scope, locals, assign, inputs) >= right(scope, locals, assign, inputs);
14159               return context ? {value: arg} : arg;
14160             };
14161           },
14162           'binary&&': function(left, right, context) {
14163             return function(scope, locals, assign, inputs) {
14164               var arg = left(scope, locals, assign, inputs) && right(scope, locals, assign, inputs);
14165               return context ? {value: arg} : arg;
14166             };
14167           },
14168           'binary||': function(left, right, context) {
14169             return function(scope, locals, assign, inputs) {
14170               var arg = left(scope, locals, assign, inputs) || right(scope, locals, assign, inputs);
14171               return context ? {value: arg} : arg;
14172             };
14173           },
14174           'ternary?:': function(test, alternate, consequent, context) {
14175             return function(scope, locals, assign, inputs) {
14176               var arg = test(scope, locals, assign, inputs) ? alternate(scope, locals, assign, inputs) : consequent(scope, locals, assign, inputs);
14177               return context ? {value: arg} : arg;
14178             };
14179           },
14180           value: function(value, context) {
14181             return function() { return context ? {context: undefined, name: undefined, value: value} : value; };
14182           },
14183           identifier: function(name, expensiveChecks, context, create, expression) {
14184             return function(scope, locals, assign, inputs) {
14185               var base = locals && (name in locals) ? locals : scope;
14186               if (create && create !== 1 && base && !(base[name])) {
14187                 base[name] = {};
14188               }
14189               var value = base ? base[name] : undefined;
14190               if (expensiveChecks) {
14191                 ensureSafeObject(value, expression);
14192               }
14193               if (context) {
14194                 return {context: base, name: name, value: value};
14195               } else {
14196                 return value;
14197               }
14198             };
14199           },
14200           computedMember: function(left, right, context, create, expression) {
14201             return function(scope, locals, assign, inputs) {
14202               var lhs = left(scope, locals, assign, inputs);
14203               var rhs;
14204               var value;
14205               if (lhs != null) {
14206                 rhs = right(scope, locals, assign, inputs);
14207                 rhs = getStringValue(rhs);
14208                 ensureSafeMemberName(rhs, expression);
14209                 if (create && create !== 1 && lhs && !(lhs[rhs])) {
14210                   lhs[rhs] = {};
14211                 }
14212                 value = lhs[rhs];
14213                 ensureSafeObject(value, expression);
14214               }
14215               if (context) {
14216                 return {context: lhs, name: rhs, value: value};
14217               } else {
14218                 return value;
14219               }
14220             };
14221           },
14222           nonComputedMember: function(left, right, expensiveChecks, context, create, expression) {
14223             return function(scope, locals, assign, inputs) {
14224               var lhs = left(scope, locals, assign, inputs);
14225               if (create && create !== 1 && lhs && !(lhs[right])) {
14226                 lhs[right] = {};
14227               }
14228               var value = lhs != null ? lhs[right] : undefined;
14229               if (expensiveChecks || isPossiblyDangerousMemberName(right)) {
14230                 ensureSafeObject(value, expression);
14231               }
14232               if (context) {
14233                 return {context: lhs, name: right, value: value};
14234               } else {
14235                 return value;
14236               }
14237             };
14238           },
14239           inputs: function(input, watchId) {
14240             return function(scope, value, locals, inputs) {
14241               if (inputs) return inputs[watchId];
14242               return input(scope, value, locals);
14243             };
14244           }
14245         };
14246
14247         /**
14248          * @constructor
14249          */
14250         var Parser = function(lexer, $filter, options) {
14251           this.lexer = lexer;
14252           this.$filter = $filter;
14253           this.options = options;
14254           this.ast = new AST(this.lexer);
14255           this.astCompiler = options.csp ? new ASTInterpreter(this.ast, $filter) :
14256                                            new ASTCompiler(this.ast, $filter);
14257         };
14258
14259         Parser.prototype = {
14260           constructor: Parser,
14261
14262           parse: function(text) {
14263             return this.astCompiler.compile(text, this.options.expensiveChecks);
14264           }
14265         };
14266
14267         var getterFnCacheDefault = createMap();
14268         var getterFnCacheExpensive = createMap();
14269
14270         function isPossiblyDangerousMemberName(name) {
14271           return name == 'constructor';
14272         }
14273
14274         var objectValueOf = Object.prototype.valueOf;
14275
14276         function getValueOf(value) {
14277           return isFunction(value.valueOf) ? value.valueOf() : objectValueOf.call(value);
14278         }
14279
14280         ///////////////////////////////////
14281
14282         /**
14283          * @ngdoc service
14284          * @name $parse
14285          * @kind function
14286          *
14287          * @description
14288          *
14289          * Converts Angular {@link guide/expression expression} into a function.
14290          *
14291          * ```js
14292          *   var getter = $parse('user.name');
14293          *   var setter = getter.assign;
14294          *   var context = {user:{name:'angular'}};
14295          *   var locals = {user:{name:'local'}};
14296          *
14297          *   expect(getter(context)).toEqual('angular');
14298          *   setter(context, 'newValue');
14299          *   expect(context.user.name).toEqual('newValue');
14300          *   expect(getter(context, locals)).toEqual('local');
14301          * ```
14302          *
14303          *
14304          * @param {string} expression String expression to compile.
14305          * @returns {function(context, locals)} a function which represents the compiled expression:
14306          *
14307          *    * `context` – `{object}` – an object against which any expressions embedded in the strings
14308          *      are evaluated against (typically a scope object).
14309          *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
14310          *      `context`.
14311          *
14312          *    The returned function also has the following properties:
14313          *      * `literal` – `{boolean}` – whether the expression's top-level node is a JavaScript
14314          *        literal.
14315          *      * `constant` – `{boolean}` – whether the expression is made entirely of JavaScript
14316          *        constant literals.
14317          *      * `assign` – `{?function(context, value)}` – if the expression is assignable, this will be
14318          *        set to a function to change its value on the given context.
14319          *
14320          */
14321
14322
14323         /**
14324          * @ngdoc provider
14325          * @name $parseProvider
14326          *
14327          * @description
14328          * `$parseProvider` can be used for configuring the default behavior of the {@link ng.$parse $parse}
14329          *  service.
14330          */
14331         function $ParseProvider() {
14332           var cacheDefault = createMap();
14333           var cacheExpensive = createMap();
14334
14335           this.$get = ['$filter', function($filter) {
14336             var noUnsafeEval = csp().noUnsafeEval;
14337             var $parseOptions = {
14338                   csp: noUnsafeEval,
14339                   expensiveChecks: false
14340                 },
14341                 $parseOptionsExpensive = {
14342                   csp: noUnsafeEval,
14343                   expensiveChecks: true
14344                 };
14345
14346             return function $parse(exp, interceptorFn, expensiveChecks) {
14347               var parsedExpression, oneTime, cacheKey;
14348
14349               switch (typeof exp) {
14350                 case 'string':
14351                   exp = exp.trim();
14352                   cacheKey = exp;
14353
14354                   var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
14355                   parsedExpression = cache[cacheKey];
14356
14357                   if (!parsedExpression) {
14358                     if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
14359                       oneTime = true;
14360                       exp = exp.substring(2);
14361                     }
14362                     var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
14363                     var lexer = new Lexer(parseOptions);
14364                     var parser = new Parser(lexer, $filter, parseOptions);
14365                     parsedExpression = parser.parse(exp);
14366                     if (parsedExpression.constant) {
14367                       parsedExpression.$$watchDelegate = constantWatchDelegate;
14368                     } else if (oneTime) {
14369                       parsedExpression.$$watchDelegate = parsedExpression.literal ?
14370                           oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
14371                     } else if (parsedExpression.inputs) {
14372                       parsedExpression.$$watchDelegate = inputsWatchDelegate;
14373                     }
14374                     cache[cacheKey] = parsedExpression;
14375                   }
14376                   return addInterceptor(parsedExpression, interceptorFn);
14377
14378                 case 'function':
14379                   return addInterceptor(exp, interceptorFn);
14380
14381                 default:
14382                   return noop;
14383               }
14384             };
14385
14386             function expressionInputDirtyCheck(newValue, oldValueOfValue) {
14387
14388               if (newValue == null || oldValueOfValue == null) { // null/undefined
14389                 return newValue === oldValueOfValue;
14390               }
14391
14392               if (typeof newValue === 'object') {
14393
14394                 // attempt to convert the value to a primitive type
14395                 // TODO(docs): add a note to docs that by implementing valueOf even objects and arrays can
14396                 //             be cheaply dirty-checked
14397                 newValue = getValueOf(newValue);
14398
14399                 if (typeof newValue === 'object') {
14400                   // objects/arrays are not supported - deep-watching them would be too expensive
14401                   return false;
14402                 }
14403
14404                 // fall-through to the primitive equality check
14405               }
14406
14407               //Primitive or NaN
14408               return newValue === oldValueOfValue || (newValue !== newValue && oldValueOfValue !== oldValueOfValue);
14409             }
14410
14411             function inputsWatchDelegate(scope, listener, objectEquality, parsedExpression, prettyPrintExpression) {
14412               var inputExpressions = parsedExpression.inputs;
14413               var lastResult;
14414
14415               if (inputExpressions.length === 1) {
14416                 var oldInputValueOf = expressionInputDirtyCheck; // init to something unique so that equals check fails
14417                 inputExpressions = inputExpressions[0];
14418                 return scope.$watch(function expressionInputWatch(scope) {
14419                   var newInputValue = inputExpressions(scope);
14420                   if (!expressionInputDirtyCheck(newInputValue, oldInputValueOf)) {
14421                     lastResult = parsedExpression(scope, undefined, undefined, [newInputValue]);
14422                     oldInputValueOf = newInputValue && getValueOf(newInputValue);
14423                   }
14424                   return lastResult;
14425                 }, listener, objectEquality, prettyPrintExpression);
14426               }
14427
14428               var oldInputValueOfValues = [];
14429               var oldInputValues = [];
14430               for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14431                 oldInputValueOfValues[i] = expressionInputDirtyCheck; // init to something unique so that equals check fails
14432                 oldInputValues[i] = null;
14433               }
14434
14435               return scope.$watch(function expressionInputsWatch(scope) {
14436                 var changed = false;
14437
14438                 for (var i = 0, ii = inputExpressions.length; i < ii; i++) {
14439                   var newInputValue = inputExpressions[i](scope);
14440                   if (changed || (changed = !expressionInputDirtyCheck(newInputValue, oldInputValueOfValues[i]))) {
14441                     oldInputValues[i] = newInputValue;
14442                     oldInputValueOfValues[i] = newInputValue && getValueOf(newInputValue);
14443                   }
14444                 }
14445
14446                 if (changed) {
14447                   lastResult = parsedExpression(scope, undefined, undefined, oldInputValues);
14448                 }
14449
14450                 return lastResult;
14451               }, listener, objectEquality, prettyPrintExpression);
14452             }
14453
14454             function oneTimeWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14455               var unwatch, lastValue;
14456               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14457                 return parsedExpression(scope);
14458               }, function oneTimeListener(value, old, scope) {
14459                 lastValue = value;
14460                 if (isFunction(listener)) {
14461                   listener.apply(this, arguments);
14462                 }
14463                 if (isDefined(value)) {
14464                   scope.$$postDigest(function() {
14465                     if (isDefined(lastValue)) {
14466                       unwatch();
14467                     }
14468                   });
14469                 }
14470               }, objectEquality);
14471             }
14472
14473             function oneTimeLiteralWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14474               var unwatch, lastValue;
14475               return unwatch = scope.$watch(function oneTimeWatch(scope) {
14476                 return parsedExpression(scope);
14477               }, function oneTimeListener(value, old, scope) {
14478                 lastValue = value;
14479                 if (isFunction(listener)) {
14480                   listener.call(this, value, old, scope);
14481                 }
14482                 if (isAllDefined(value)) {
14483                   scope.$$postDigest(function() {
14484                     if (isAllDefined(lastValue)) unwatch();
14485                   });
14486                 }
14487               }, objectEquality);
14488
14489               function isAllDefined(value) {
14490                 var allDefined = true;
14491                 forEach(value, function(val) {
14492                   if (!isDefined(val)) allDefined = false;
14493                 });
14494                 return allDefined;
14495               }
14496             }
14497
14498             function constantWatchDelegate(scope, listener, objectEquality, parsedExpression) {
14499               var unwatch;
14500               return unwatch = scope.$watch(function constantWatch(scope) {
14501                 return parsedExpression(scope);
14502               }, function constantListener(value, old, scope) {
14503                 if (isFunction(listener)) {
14504                   listener.apply(this, arguments);
14505                 }
14506                 unwatch();
14507               }, objectEquality);
14508             }
14509
14510             function addInterceptor(parsedExpression, interceptorFn) {
14511               if (!interceptorFn) return parsedExpression;
14512               var watchDelegate = parsedExpression.$$watchDelegate;
14513               var useInputs = false;
14514
14515               var regularWatch =
14516                   watchDelegate !== oneTimeLiteralWatchDelegate &&
14517                   watchDelegate !== oneTimeWatchDelegate;
14518
14519               var fn = regularWatch ? function regularInterceptedExpression(scope, locals, assign, inputs) {
14520                 var value = useInputs && inputs ? inputs[0] : parsedExpression(scope, locals, assign, inputs);
14521                 return interceptorFn(value, scope, locals);
14522               } : function oneTimeInterceptedExpression(scope, locals, assign, inputs) {
14523                 var value = parsedExpression(scope, locals, assign, inputs);
14524                 var result = interceptorFn(value, scope, locals);
14525                 // we only return the interceptor's result if the
14526                 // initial value is defined (for bind-once)
14527                 return isDefined(value) ? result : value;
14528               };
14529
14530               // Propagate $$watchDelegates other then inputsWatchDelegate
14531               if (parsedExpression.$$watchDelegate &&
14532                   parsedExpression.$$watchDelegate !== inputsWatchDelegate) {
14533                 fn.$$watchDelegate = parsedExpression.$$watchDelegate;
14534               } else if (!interceptorFn.$stateful) {
14535                 // If there is an interceptor, but no watchDelegate then treat the interceptor like
14536                 // we treat filters - it is assumed to be a pure function unless flagged with $stateful
14537                 fn.$$watchDelegate = inputsWatchDelegate;
14538                 useInputs = !parsedExpression.inputs;
14539                 fn.inputs = parsedExpression.inputs ? parsedExpression.inputs : [parsedExpression];
14540               }
14541
14542               return fn;
14543             }
14544           }];
14545         }
14546
14547         /**
14548          * @ngdoc service
14549          * @name $q
14550          * @requires $rootScope
14551          *
14552          * @description
14553          * A service that helps you run functions asynchronously, and use their return values (or exceptions)
14554          * when they are done processing.
14555          *
14556          * This is an implementation of promises/deferred objects inspired by
14557          * [Kris Kowal's Q](https://github.com/kriskowal/q).
14558          *
14559          * $q can be used in two fashions --- one which is more similar to Kris Kowal's Q or jQuery's Deferred
14560          * implementations, and the other which resembles ES6 promises to some degree.
14561          *
14562          * # $q constructor
14563          *
14564          * The streamlined ES6 style promise is essentially just using $q as a constructor which takes a `resolver`
14565          * function as the first argument. This is similar to the native Promise implementation from ES6 Harmony,
14566          * see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
14567          *
14568          * While the constructor-style use is supported, not all of the supporting methods from ES6 Harmony promises are
14569          * available yet.
14570          *
14571          * It can be used like so:
14572          *
14573          * ```js
14574          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14575          *   // are available in the current lexical scope (they could have been injected or passed in).
14576          *
14577          *   function asyncGreet(name) {
14578          *     // perform some asynchronous operation, resolve or reject the promise when appropriate.
14579          *     return $q(function(resolve, reject) {
14580          *       setTimeout(function() {
14581          *         if (okToGreet(name)) {
14582          *           resolve('Hello, ' + name + '!');
14583          *         } else {
14584          *           reject('Greeting ' + name + ' is not allowed.');
14585          *         }
14586          *       }, 1000);
14587          *     });
14588          *   }
14589          *
14590          *   var promise = asyncGreet('Robin Hood');
14591          *   promise.then(function(greeting) {
14592          *     alert('Success: ' + greeting);
14593          *   }, function(reason) {
14594          *     alert('Failed: ' + reason);
14595          *   });
14596          * ```
14597          *
14598          * Note: progress/notify callbacks are not currently supported via the ES6-style interface.
14599          *
14600          * Note: unlike ES6 behaviour, an exception thrown in the constructor function will NOT implicitly reject the promise.
14601          *
14602          * However, the more traditional CommonJS-style usage is still available, and documented below.
14603          *
14604          * [The CommonJS Promise proposal](http://wiki.commonjs.org/wiki/Promises) describes a promise as an
14605          * interface for interacting with an object that represents the result of an action that is
14606          * performed asynchronously, and may or may not be finished at any given point in time.
14607          *
14608          * From the perspective of dealing with error handling, deferred and promise APIs are to
14609          * asynchronous programming what `try`, `catch` and `throw` keywords are to synchronous programming.
14610          *
14611          * ```js
14612          *   // for the purpose of this example let's assume that variables `$q` and `okToGreet`
14613          *   // are available in the current lexical scope (they could have been injected or passed in).
14614          *
14615          *   function asyncGreet(name) {
14616          *     var deferred = $q.defer();
14617          *
14618          *     setTimeout(function() {
14619          *       deferred.notify('About to greet ' + name + '.');
14620          *
14621          *       if (okToGreet(name)) {
14622          *         deferred.resolve('Hello, ' + name + '!');
14623          *       } else {
14624          *         deferred.reject('Greeting ' + name + ' is not allowed.');
14625          *       }
14626          *     }, 1000);
14627          *
14628          *     return deferred.promise;
14629          *   }
14630          *
14631          *   var promise = asyncGreet('Robin Hood');
14632          *   promise.then(function(greeting) {
14633          *     alert('Success: ' + greeting);
14634          *   }, function(reason) {
14635          *     alert('Failed: ' + reason);
14636          *   }, function(update) {
14637          *     alert('Got notification: ' + update);
14638          *   });
14639          * ```
14640          *
14641          * At first it might not be obvious why this extra complexity is worth the trouble. The payoff
14642          * comes in the way of guarantees that promise and deferred APIs make, see
14643          * https://github.com/kriskowal/uncommonjs/blob/master/promises/specification.md.
14644          *
14645          * Additionally the promise api allows for composition that is very hard to do with the
14646          * traditional callback ([CPS](http://en.wikipedia.org/wiki/Continuation-passing_style)) approach.
14647          * For more on this please see the [Q documentation](https://github.com/kriskowal/q) especially the
14648          * section on serial or parallel joining of promises.
14649          *
14650          * # The Deferred API
14651          *
14652          * A new instance of deferred is constructed by calling `$q.defer()`.
14653          *
14654          * The purpose of the deferred object is to expose the associated Promise instance as well as APIs
14655          * that can be used for signaling the successful or unsuccessful completion, as well as the status
14656          * of the task.
14657          *
14658          * **Methods**
14659          *
14660          * - `resolve(value)` – resolves the derived promise with the `value`. If the value is a rejection
14661          *   constructed via `$q.reject`, the promise will be rejected instead.
14662          * - `reject(reason)` – rejects the derived promise with the `reason`. This is equivalent to
14663          *   resolving it with a rejection constructed via `$q.reject`.
14664          * - `notify(value)` - provides updates on the status of the promise's execution. This may be called
14665          *   multiple times before the promise is either resolved or rejected.
14666          *
14667          * **Properties**
14668          *
14669          * - promise – `{Promise}` – promise object associated with this deferred.
14670          *
14671          *
14672          * # The Promise API
14673          *
14674          * A new promise instance is created when a deferred instance is created and can be retrieved by
14675          * calling `deferred.promise`.
14676          *
14677          * The purpose of the promise object is to allow for interested parties to get access to the result
14678          * of the deferred task when it completes.
14679          *
14680          * **Methods**
14681          *
14682          * - `then(successCallback, errorCallback, notifyCallback)` – regardless of when the promise was or
14683          *   will be resolved or rejected, `then` calls one of the success or error callbacks asynchronously
14684          *   as soon as the result is available. The callbacks are called with a single argument: the result
14685          *   or rejection reason. Additionally, the notify callback may be called zero or more times to
14686          *   provide a progress indication, before the promise is resolved or rejected.
14687          *
14688          *   This method *returns a new promise* which is resolved or rejected via the return value of the
14689          *   `successCallback`, `errorCallback` (unless that value is a promise, in which case it is resolved
14690          *   with the value which is resolved in that promise using
14691          *   [promise chaining](http://www.html5rocks.com/en/tutorials/es6/promises/#toc-promises-queues)).
14692          *   It also notifies via the return value of the `notifyCallback` method. The promise cannot be
14693          *   resolved or rejected from the notifyCallback method.
14694          *
14695          * - `catch(errorCallback)` – shorthand for `promise.then(null, errorCallback)`
14696          *
14697          * - `finally(callback, notifyCallback)` – allows you to observe either the fulfillment or rejection of a promise,
14698          *   but to do so without modifying the final value. This is useful to release resources or do some
14699          *   clean-up that needs to be done whether the promise was rejected or resolved. See the [full
14700          *   specification](https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback) for
14701          *   more information.
14702          *
14703          * # Chaining promises
14704          *
14705          * Because calling the `then` method of a promise returns a new derived promise, it is easily
14706          * possible to create a chain of promises:
14707          *
14708          * ```js
14709          *   promiseB = promiseA.then(function(result) {
14710          *     return result + 1;
14711          *   });
14712          *
14713          *   // promiseB will be resolved immediately after promiseA is resolved and its value
14714          *   // will be the result of promiseA incremented by 1
14715          * ```
14716          *
14717          * It is possible to create chains of any length and since a promise can be resolved with another
14718          * promise (which will defer its resolution further), it is possible to pause/defer resolution of
14719          * the promises at any point in the chain. This makes it possible to implement powerful APIs like
14720          * $http's response interceptors.
14721          *
14722          *
14723          * # Differences between Kris Kowal's Q and $q
14724          *
14725          *  There are two main differences:
14726          *
14727          * - $q is integrated with the {@link ng.$rootScope.Scope} Scope model observation
14728          *   mechanism in angular, which means faster propagation of resolution or rejection into your
14729          *   models and avoiding unnecessary browser repaints, which would result in flickering UI.
14730          * - Q has many more features than $q, but that comes at a cost of bytes. $q is tiny, but contains
14731          *   all the important functionality needed for common async tasks.
14732          *
14733          *  # Testing
14734          *
14735          *  ```js
14736          *    it('should simulate promise', inject(function($q, $rootScope) {
14737          *      var deferred = $q.defer();
14738          *      var promise = deferred.promise;
14739          *      var resolvedValue;
14740          *
14741          *      promise.then(function(value) { resolvedValue = value; });
14742          *      expect(resolvedValue).toBeUndefined();
14743          *
14744          *      // Simulate resolving of promise
14745          *      deferred.resolve(123);
14746          *      // Note that the 'then' function does not get called synchronously.
14747          *      // This is because we want the promise API to always be async, whether or not
14748          *      // it got called synchronously or asynchronously.
14749          *      expect(resolvedValue).toBeUndefined();
14750          *
14751          *      // Propagate promise resolution to 'then' functions using $apply().
14752          *      $rootScope.$apply();
14753          *      expect(resolvedValue).toEqual(123);
14754          *    }));
14755          *  ```
14756          *
14757          * @param {function(function, function)} resolver Function which is responsible for resolving or
14758          *   rejecting the newly created promise. The first parameter is a function which resolves the
14759          *   promise, the second parameter is a function which rejects the promise.
14760          *
14761          * @returns {Promise} The newly created promise.
14762          */
14763         function $QProvider() {
14764
14765           this.$get = ['$rootScope', '$exceptionHandler', function($rootScope, $exceptionHandler) {
14766             return qFactory(function(callback) {
14767               $rootScope.$evalAsync(callback);
14768             }, $exceptionHandler);
14769           }];
14770         }
14771
14772         function $$QProvider() {
14773           this.$get = ['$browser', '$exceptionHandler', function($browser, $exceptionHandler) {
14774             return qFactory(function(callback) {
14775               $browser.defer(callback);
14776             }, $exceptionHandler);
14777           }];
14778         }
14779
14780         /**
14781          * Constructs a promise manager.
14782          *
14783          * @param {function(function)} nextTick Function for executing functions in the next turn.
14784          * @param {function(...*)} exceptionHandler Function into which unexpected exceptions are passed for
14785          *     debugging purposes.
14786          * @returns {object} Promise manager.
14787          */
14788         function qFactory(nextTick, exceptionHandler) {
14789           var $qMinErr = minErr('$q', TypeError);
14790           function callOnce(self, resolveFn, rejectFn) {
14791             var called = false;
14792             function wrap(fn) {
14793               return function(value) {
14794                 if (called) return;
14795                 called = true;
14796                 fn.call(self, value);
14797               };
14798             }
14799
14800             return [wrap(resolveFn), wrap(rejectFn)];
14801           }
14802
14803           /**
14804            * @ngdoc method
14805            * @name ng.$q#defer
14806            * @kind function
14807            *
14808            * @description
14809            * Creates a `Deferred` object which represents a task which will finish in the future.
14810            *
14811            * @returns {Deferred} Returns a new instance of deferred.
14812            */
14813           var defer = function() {
14814             return new Deferred();
14815           };
14816
14817           function Promise() {
14818             this.$$state = { status: 0 };
14819           }
14820
14821           extend(Promise.prototype, {
14822             then: function(onFulfilled, onRejected, progressBack) {
14823               if (isUndefined(onFulfilled) && isUndefined(onRejected) && isUndefined(progressBack)) {
14824                 return this;
14825               }
14826               var result = new Deferred();
14827
14828               this.$$state.pending = this.$$state.pending || [];
14829               this.$$state.pending.push([result, onFulfilled, onRejected, progressBack]);
14830               if (this.$$state.status > 0) scheduleProcessQueue(this.$$state);
14831
14832               return result.promise;
14833             },
14834
14835             "catch": function(callback) {
14836               return this.then(null, callback);
14837             },
14838
14839             "finally": function(callback, progressBack) {
14840               return this.then(function(value) {
14841                 return handleCallback(value, true, callback);
14842               }, function(error) {
14843                 return handleCallback(error, false, callback);
14844               }, progressBack);
14845             }
14846           });
14847
14848           //Faster, more basic than angular.bind http://jsperf.com/angular-bind-vs-custom-vs-native
14849           function simpleBind(context, fn) {
14850             return function(value) {
14851               fn.call(context, value);
14852             };
14853           }
14854
14855           function processQueue(state) {
14856             var fn, deferred, pending;
14857
14858             pending = state.pending;
14859             state.processScheduled = false;
14860             state.pending = undefined;
14861             for (var i = 0, ii = pending.length; i < ii; ++i) {
14862               deferred = pending[i][0];
14863               fn = pending[i][state.status];
14864               try {
14865                 if (isFunction(fn)) {
14866                   deferred.resolve(fn(state.value));
14867                 } else if (state.status === 1) {
14868                   deferred.resolve(state.value);
14869                 } else {
14870                   deferred.reject(state.value);
14871                 }
14872               } catch (e) {
14873                 deferred.reject(e);
14874                 exceptionHandler(e);
14875               }
14876             }
14877           }
14878
14879           function scheduleProcessQueue(state) {
14880             if (state.processScheduled || !state.pending) return;
14881             state.processScheduled = true;
14882             nextTick(function() { processQueue(state); });
14883           }
14884
14885           function Deferred() {
14886             this.promise = new Promise();
14887             //Necessary to support unbound execution :/
14888             this.resolve = simpleBind(this, this.resolve);
14889             this.reject = simpleBind(this, this.reject);
14890             this.notify = simpleBind(this, this.notify);
14891           }
14892
14893           extend(Deferred.prototype, {
14894             resolve: function(val) {
14895               if (this.promise.$$state.status) return;
14896               if (val === this.promise) {
14897                 this.$$reject($qMinErr(
14898                   'qcycle',
14899                   "Expected promise to be resolved with value other than itself '{0}'",
14900                   val));
14901               } else {
14902                 this.$$resolve(val);
14903               }
14904
14905             },
14906
14907             $$resolve: function(val) {
14908               var then, fns;
14909
14910               fns = callOnce(this, this.$$resolve, this.$$reject);
14911               try {
14912                 if ((isObject(val) || isFunction(val))) then = val && val.then;
14913                 if (isFunction(then)) {
14914                   this.promise.$$state.status = -1;
14915                   then.call(val, fns[0], fns[1], this.notify);
14916                 } else {
14917                   this.promise.$$state.value = val;
14918                   this.promise.$$state.status = 1;
14919                   scheduleProcessQueue(this.promise.$$state);
14920                 }
14921               } catch (e) {
14922                 fns[1](e);
14923                 exceptionHandler(e);
14924               }
14925             },
14926
14927             reject: function(reason) {
14928               if (this.promise.$$state.status) return;
14929               this.$$reject(reason);
14930             },
14931
14932             $$reject: function(reason) {
14933               this.promise.$$state.value = reason;
14934               this.promise.$$state.status = 2;
14935               scheduleProcessQueue(this.promise.$$state);
14936             },
14937
14938             notify: function(progress) {
14939               var callbacks = this.promise.$$state.pending;
14940
14941               if ((this.promise.$$state.status <= 0) && callbacks && callbacks.length) {
14942                 nextTick(function() {
14943                   var callback, result;
14944                   for (var i = 0, ii = callbacks.length; i < ii; i++) {
14945                     result = callbacks[i][0];
14946                     callback = callbacks[i][3];
14947                     try {
14948                       result.notify(isFunction(callback) ? callback(progress) : progress);
14949                     } catch (e) {
14950                       exceptionHandler(e);
14951                     }
14952                   }
14953                 });
14954               }
14955             }
14956           });
14957
14958           /**
14959            * @ngdoc method
14960            * @name $q#reject
14961            * @kind function
14962            *
14963            * @description
14964            * Creates a promise that is resolved as rejected with the specified `reason`. This api should be
14965            * used to forward rejection in a chain of promises. If you are dealing with the last promise in
14966            * a promise chain, you don't need to worry about it.
14967            *
14968            * When comparing deferreds/promises to the familiar behavior of try/catch/throw, think of
14969            * `reject` as the `throw` keyword in JavaScript. This also means that if you "catch" an error via
14970            * a promise error callback and you want to forward the error to the promise derived from the
14971            * current promise, you have to "rethrow" the error by returning a rejection constructed via
14972            * `reject`.
14973            *
14974            * ```js
14975            *   promiseB = promiseA.then(function(result) {
14976            *     // success: do something and resolve promiseB
14977            *     //          with the old or a new result
14978            *     return result;
14979            *   }, function(reason) {
14980            *     // error: handle the error if possible and
14981            *     //        resolve promiseB with newPromiseOrValue,
14982            *     //        otherwise forward the rejection to promiseB
14983            *     if (canHandle(reason)) {
14984            *      // handle the error and recover
14985            *      return newPromiseOrValue;
14986            *     }
14987            *     return $q.reject(reason);
14988            *   });
14989            * ```
14990            *
14991            * @param {*} reason Constant, message, exception or an object representing the rejection reason.
14992            * @returns {Promise} Returns a promise that was already resolved as rejected with the `reason`.
14993            */
14994           var reject = function(reason) {
14995             var result = new Deferred();
14996             result.reject(reason);
14997             return result.promise;
14998           };
14999
15000           var makePromise = function makePromise(value, resolved) {
15001             var result = new Deferred();
15002             if (resolved) {
15003               result.resolve(value);
15004             } else {
15005               result.reject(value);
15006             }
15007             return result.promise;
15008           };
15009
15010           var handleCallback = function handleCallback(value, isResolved, callback) {
15011             var callbackOutput = null;
15012             try {
15013               if (isFunction(callback)) callbackOutput = callback();
15014             } catch (e) {
15015               return makePromise(e, false);
15016             }
15017             if (isPromiseLike(callbackOutput)) {
15018               return callbackOutput.then(function() {
15019                 return makePromise(value, isResolved);
15020               }, function(error) {
15021                 return makePromise(error, false);
15022               });
15023             } else {
15024               return makePromise(value, isResolved);
15025             }
15026           };
15027
15028           /**
15029            * @ngdoc method
15030            * @name $q#when
15031            * @kind function
15032            *
15033            * @description
15034            * Wraps an object that might be a value or a (3rd party) then-able promise into a $q promise.
15035            * This is useful when you are dealing with an object that might or might not be a promise, or if
15036            * the promise comes from a source that can't be trusted.
15037            *
15038            * @param {*} value Value or a promise
15039            * @param {Function=} successCallback
15040            * @param {Function=} errorCallback
15041            * @param {Function=} progressCallback
15042            * @returns {Promise} Returns a promise of the passed value or promise
15043            */
15044
15045
15046           var when = function(value, callback, errback, progressBack) {
15047             var result = new Deferred();
15048             result.resolve(value);
15049             return result.promise.then(callback, errback, progressBack);
15050           };
15051
15052           /**
15053            * @ngdoc method
15054            * @name $q#resolve
15055            * @kind function
15056            *
15057            * @description
15058            * Alias of {@link ng.$q#when when} to maintain naming consistency with ES6.
15059            *
15060            * @param {*} value Value or a promise
15061            * @param {Function=} successCallback
15062            * @param {Function=} errorCallback
15063            * @param {Function=} progressCallback
15064            * @returns {Promise} Returns a promise of the passed value or promise
15065            */
15066           var resolve = when;
15067
15068           /**
15069            * @ngdoc method
15070            * @name $q#all
15071            * @kind function
15072            *
15073            * @description
15074            * Combines multiple promises into a single promise that is resolved when all of the input
15075            * promises are resolved.
15076            *
15077            * @param {Array.<Promise>|Object.<Promise>} promises An array or hash of promises.
15078            * @returns {Promise} Returns a single promise that will be resolved with an array/hash of values,
15079            *   each value corresponding to the promise at the same index/key in the `promises` array/hash.
15080            *   If any of the promises is resolved with a rejection, this resulting promise will be rejected
15081            *   with the same rejection value.
15082            */
15083
15084           function all(promises) {
15085             var deferred = new Deferred(),
15086                 counter = 0,
15087                 results = isArray(promises) ? [] : {};
15088
15089             forEach(promises, function(promise, key) {
15090               counter++;
15091               when(promise).then(function(value) {
15092                 if (results.hasOwnProperty(key)) return;
15093                 results[key] = value;
15094                 if (!(--counter)) deferred.resolve(results);
15095               }, function(reason) {
15096                 if (results.hasOwnProperty(key)) return;
15097                 deferred.reject(reason);
15098               });
15099             });
15100
15101             if (counter === 0) {
15102               deferred.resolve(results);
15103             }
15104
15105             return deferred.promise;
15106           }
15107
15108           var $Q = function Q(resolver) {
15109             if (!isFunction(resolver)) {
15110               throw $qMinErr('norslvr', "Expected resolverFn, got '{0}'", resolver);
15111             }
15112
15113             if (!(this instanceof Q)) {
15114               // More useful when $Q is the Promise itself.
15115               return new Q(resolver);
15116             }
15117
15118             var deferred = new Deferred();
15119
15120             function resolveFn(value) {
15121               deferred.resolve(value);
15122             }
15123
15124             function rejectFn(reason) {
15125               deferred.reject(reason);
15126             }
15127
15128             resolver(resolveFn, rejectFn);
15129
15130             return deferred.promise;
15131           };
15132
15133           $Q.defer = defer;
15134           $Q.reject = reject;
15135           $Q.when = when;
15136           $Q.resolve = resolve;
15137           $Q.all = all;
15138
15139           return $Q;
15140         }
15141
15142         function $$RAFProvider() { //rAF
15143           this.$get = ['$window', '$timeout', function($window, $timeout) {
15144             var requestAnimationFrame = $window.requestAnimationFrame ||
15145                                         $window.webkitRequestAnimationFrame;
15146
15147             var cancelAnimationFrame = $window.cancelAnimationFrame ||
15148                                        $window.webkitCancelAnimationFrame ||
15149                                        $window.webkitCancelRequestAnimationFrame;
15150
15151             var rafSupported = !!requestAnimationFrame;
15152             var raf = rafSupported
15153               ? function(fn) {
15154                   var id = requestAnimationFrame(fn);
15155                   return function() {
15156                     cancelAnimationFrame(id);
15157                   };
15158                 }
15159               : function(fn) {
15160                   var timer = $timeout(fn, 16.66, false); // 1000 / 60 = 16.666
15161                   return function() {
15162                     $timeout.cancel(timer);
15163                   };
15164                 };
15165
15166             raf.supported = rafSupported;
15167
15168             return raf;
15169           }];
15170         }
15171
15172         /**
15173          * DESIGN NOTES
15174          *
15175          * The design decisions behind the scope are heavily favored for speed and memory consumption.
15176          *
15177          * The typical use of scope is to watch the expressions, which most of the time return the same
15178          * value as last time so we optimize the operation.
15179          *
15180          * Closures construction is expensive in terms of speed as well as memory:
15181          *   - No closures, instead use prototypical inheritance for API
15182          *   - Internal state needs to be stored on scope directly, which means that private state is
15183          *     exposed as $$____ properties
15184          *
15185          * Loop operations are optimized by using while(count--) { ... }
15186          *   - This means that in order to keep the same order of execution as addition we have to add
15187          *     items to the array at the beginning (unshift) instead of at the end (push)
15188          *
15189          * Child scopes are created and removed often
15190          *   - Using an array would be slow since inserts in the middle are expensive; so we use linked lists
15191          *
15192          * There are fewer watches than observers. This is why you don't want the observer to be implemented
15193          * in the same way as watch. Watch requires return of the initialization function which is expensive
15194          * to construct.
15195          */
15196
15197
15198         /**
15199          * @ngdoc provider
15200          * @name $rootScopeProvider
15201          * @description
15202          *
15203          * Provider for the $rootScope service.
15204          */
15205
15206         /**
15207          * @ngdoc method
15208          * @name $rootScopeProvider#digestTtl
15209          * @description
15210          *
15211          * Sets the number of `$digest` iterations the scope should attempt to execute before giving up and
15212          * assuming that the model is unstable.
15213          *
15214          * The current default is 10 iterations.
15215          *
15216          * In complex applications it's possible that the dependencies between `$watch`s will result in
15217          * several digest iterations. However if an application needs more than the default 10 digest
15218          * iterations for its model to stabilize then you should investigate what is causing the model to
15219          * continuously change during the digest.
15220          *
15221          * Increasing the TTL could have performance implications, so you should not change it without
15222          * proper justification.
15223          *
15224          * @param {number} limit The number of digest iterations.
15225          */
15226
15227
15228         /**
15229          * @ngdoc service
15230          * @name $rootScope
15231          * @description
15232          *
15233          * Every application has a single root {@link ng.$rootScope.Scope scope}.
15234          * All other scopes are descendant scopes of the root scope. Scopes provide separation
15235          * between the model and the view, via a mechanism for watching the model for changes.
15236          * They also provide event emission/broadcast and subscription facility. See the
15237          * {@link guide/scope developer guide on scopes}.
15238          */
15239         function $RootScopeProvider() {
15240           var TTL = 10;
15241           var $rootScopeMinErr = minErr('$rootScope');
15242           var lastDirtyWatch = null;
15243           var applyAsyncId = null;
15244
15245           this.digestTtl = function(value) {
15246             if (arguments.length) {
15247               TTL = value;
15248             }
15249             return TTL;
15250           };
15251
15252           function createChildScopeClass(parent) {
15253             function ChildScope() {
15254               this.$$watchers = this.$$nextSibling =
15255                   this.$$childHead = this.$$childTail = null;
15256               this.$$listeners = {};
15257               this.$$listenerCount = {};
15258               this.$$watchersCount = 0;
15259               this.$id = nextUid();
15260               this.$$ChildScope = null;
15261             }
15262             ChildScope.prototype = parent;
15263             return ChildScope;
15264           }
15265
15266           this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
15267               function($injector, $exceptionHandler, $parse, $browser) {
15268
15269             function destroyChildScope($event) {
15270                 $event.currentScope.$$destroyed = true;
15271             }
15272
15273             function cleanUpScope($scope) {
15274
15275               if (msie === 9) {
15276                 // There is a memory leak in IE9 if all child scopes are not disconnected
15277                 // completely when a scope is destroyed. So this code will recurse up through
15278                 // all this scopes children
15279                 //
15280                 // See issue https://github.com/angular/angular.js/issues/10706
15281                 $scope.$$childHead && cleanUpScope($scope.$$childHead);
15282                 $scope.$$nextSibling && cleanUpScope($scope.$$nextSibling);
15283               }
15284
15285               // The code below works around IE9 and V8's memory leaks
15286               //
15287               // See:
15288               // - https://code.google.com/p/v8/issues/detail?id=2073#c26
15289               // - https://github.com/angular/angular.js/issues/6794#issuecomment-38648909
15290               // - https://github.com/angular/angular.js/issues/1313#issuecomment-10378451
15291
15292               $scope.$parent = $scope.$$nextSibling = $scope.$$prevSibling = $scope.$$childHead =
15293                   $scope.$$childTail = $scope.$root = $scope.$$watchers = null;
15294             }
15295
15296             /**
15297              * @ngdoc type
15298              * @name $rootScope.Scope
15299              *
15300              * @description
15301              * A root scope can be retrieved using the {@link ng.$rootScope $rootScope} key from the
15302              * {@link auto.$injector $injector}. Child scopes are created using the
15303              * {@link ng.$rootScope.Scope#$new $new()} method. (Most scopes are created automatically when
15304              * compiled HTML template is executed.) See also the {@link guide/scope Scopes guide} for
15305              * an in-depth introduction and usage examples.
15306              *
15307              *
15308              * # Inheritance
15309              * A scope can inherit from a parent scope, as in this example:
15310              * ```js
15311                  var parent = $rootScope;
15312                  var child = parent.$new();
15313
15314                  parent.salutation = "Hello";
15315                  expect(child.salutation).toEqual('Hello');
15316
15317                  child.salutation = "Welcome";
15318                  expect(child.salutation).toEqual('Welcome');
15319                  expect(parent.salutation).toEqual('Hello');
15320              * ```
15321              *
15322              * When interacting with `Scope` in tests, additional helper methods are available on the
15323              * instances of `Scope` type. See {@link ngMock.$rootScope.Scope ngMock Scope} for additional
15324              * details.
15325              *
15326              *
15327              * @param {Object.<string, function()>=} providers Map of service factory which need to be
15328              *                                       provided for the current scope. Defaults to {@link ng}.
15329              * @param {Object.<string, *>=} instanceCache Provides pre-instantiated services which should
15330              *                              append/override services provided by `providers`. This is handy
15331              *                              when unit-testing and having the need to override a default
15332              *                              service.
15333              * @returns {Object} Newly created scope.
15334              *
15335              */
15336             function Scope() {
15337               this.$id = nextUid();
15338               this.$$phase = this.$parent = this.$$watchers =
15339                              this.$$nextSibling = this.$$prevSibling =
15340                              this.$$childHead = this.$$childTail = null;
15341               this.$root = this;
15342               this.$$destroyed = false;
15343               this.$$listeners = {};
15344               this.$$listenerCount = {};
15345               this.$$watchersCount = 0;
15346               this.$$isolateBindings = null;
15347             }
15348
15349             /**
15350              * @ngdoc property
15351              * @name $rootScope.Scope#$id
15352              *
15353              * @description
15354              * Unique scope ID (monotonically increasing) useful for debugging.
15355              */
15356
15357              /**
15358               * @ngdoc property
15359               * @name $rootScope.Scope#$parent
15360               *
15361               * @description
15362               * Reference to the parent scope.
15363               */
15364
15365               /**
15366                * @ngdoc property
15367                * @name $rootScope.Scope#$root
15368                *
15369                * @description
15370                * Reference to the root scope.
15371                */
15372
15373             Scope.prototype = {
15374               constructor: Scope,
15375               /**
15376                * @ngdoc method
15377                * @name $rootScope.Scope#$new
15378                * @kind function
15379                *
15380                * @description
15381                * Creates a new child {@link ng.$rootScope.Scope scope}.
15382                *
15383                * The parent scope will propagate the {@link ng.$rootScope.Scope#$digest $digest()} event.
15384                * The scope can be removed from the scope hierarchy using {@link ng.$rootScope.Scope#$destroy $destroy()}.
15385                *
15386                * {@link ng.$rootScope.Scope#$destroy $destroy()} must be called on a scope when it is
15387                * desired for the scope and its child scopes to be permanently detached from the parent and
15388                * thus stop participating in model change detection and listener notification by invoking.
15389                *
15390                * @param {boolean} isolate If true, then the scope does not prototypically inherit from the
15391                *         parent scope. The scope is isolated, as it can not see parent scope properties.
15392                *         When creating widgets, it is useful for the widget to not accidentally read parent
15393                *         state.
15394                *
15395                * @param {Scope} [parent=this] The {@link ng.$rootScope.Scope `Scope`} that will be the `$parent`
15396                *                              of the newly created scope. Defaults to `this` scope if not provided.
15397                *                              This is used when creating a transclude scope to correctly place it
15398                *                              in the scope hierarchy while maintaining the correct prototypical
15399                *                              inheritance.
15400                *
15401                * @returns {Object} The newly created child scope.
15402                *
15403                */
15404               $new: function(isolate, parent) {
15405                 var child;
15406
15407                 parent = parent || this;
15408
15409                 if (isolate) {
15410                   child = new Scope();
15411                   child.$root = this.$root;
15412                 } else {
15413                   // Only create a child scope class if somebody asks for one,
15414                   // but cache it to allow the VM to optimize lookups.
15415                   if (!this.$$ChildScope) {
15416                     this.$$ChildScope = createChildScopeClass(this);
15417                   }
15418                   child = new this.$$ChildScope();
15419                 }
15420                 child.$parent = parent;
15421                 child.$$prevSibling = parent.$$childTail;
15422                 if (parent.$$childHead) {
15423                   parent.$$childTail.$$nextSibling = child;
15424                   parent.$$childTail = child;
15425                 } else {
15426                   parent.$$childHead = parent.$$childTail = child;
15427                 }
15428
15429                 // When the new scope is not isolated or we inherit from `this`, and
15430                 // the parent scope is destroyed, the property `$$destroyed` is inherited
15431                 // prototypically. In all other cases, this property needs to be set
15432                 // when the parent scope is destroyed.
15433                 // The listener needs to be added after the parent is set
15434                 if (isolate || parent != this) child.$on('$destroy', destroyChildScope);
15435
15436                 return child;
15437               },
15438
15439               /**
15440                * @ngdoc method
15441                * @name $rootScope.Scope#$watch
15442                * @kind function
15443                *
15444                * @description
15445                * Registers a `listener` callback to be executed whenever the `watchExpression` changes.
15446                *
15447                * - The `watchExpression` is called on every call to {@link ng.$rootScope.Scope#$digest
15448                *   $digest()} and should return the value that will be watched. (`watchExpression` should not change
15449                *   its value when executed multiple times with the same input because it may be executed multiple
15450                *   times by {@link ng.$rootScope.Scope#$digest $digest()}. That is, `watchExpression` should be
15451                *   [idempotent](http://en.wikipedia.org/wiki/Idempotence).
15452                * - The `listener` is called only when the value from the current `watchExpression` and the
15453                *   previous call to `watchExpression` are not equal (with the exception of the initial run,
15454                *   see below). Inequality is determined according to reference inequality,
15455                *   [strict comparison](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Comparison_Operators)
15456                *    via the `!==` Javascript operator, unless `objectEquality == true`
15457                *   (see next point)
15458                * - When `objectEquality == true`, inequality of the `watchExpression` is determined
15459                *   according to the {@link angular.equals} function. To save the value of the object for
15460                *   later comparison, the {@link angular.copy} function is used. This therefore means that
15461                *   watching complex objects will have adverse memory and performance implications.
15462                * - The watch `listener` may change the model, which may trigger other `listener`s to fire.
15463                *   This is achieved by rerunning the watchers until no changes are detected. The rerun
15464                *   iteration limit is 10 to prevent an infinite loop deadlock.
15465                *
15466                *
15467                * If you want to be notified whenever {@link ng.$rootScope.Scope#$digest $digest} is called,
15468                * you can register a `watchExpression` function with no `listener`. (Be prepared for
15469                * multiple calls to your `watchExpression` because it will execute multiple times in a
15470                * single {@link ng.$rootScope.Scope#$digest $digest} cycle if a change is detected.)
15471                *
15472                * After a watcher is registered with the scope, the `listener` fn is called asynchronously
15473                * (via {@link ng.$rootScope.Scope#$evalAsync $evalAsync}) to initialize the
15474                * watcher. In rare cases, this is undesirable because the listener is called when the result
15475                * of `watchExpression` didn't change. To detect this scenario within the `listener` fn, you
15476                * can compare the `newVal` and `oldVal`. If these two values are identical (`===`) then the
15477                * listener was called due to initialization.
15478                *
15479                *
15480                *
15481                * # Example
15482                * ```js
15483                    // let's assume that scope was dependency injected as the $rootScope
15484                    var scope = $rootScope;
15485                    scope.name = 'misko';
15486                    scope.counter = 0;
15487
15488                    expect(scope.counter).toEqual(0);
15489                    scope.$watch('name', function(newValue, oldValue) {
15490                      scope.counter = scope.counter + 1;
15491                    });
15492                    expect(scope.counter).toEqual(0);
15493
15494                    scope.$digest();
15495                    // the listener is always called during the first $digest loop after it was registered
15496                    expect(scope.counter).toEqual(1);
15497
15498                    scope.$digest();
15499                    // but now it will not be called unless the value changes
15500                    expect(scope.counter).toEqual(1);
15501
15502                    scope.name = 'adam';
15503                    scope.$digest();
15504                    expect(scope.counter).toEqual(2);
15505
15506
15507
15508                    // Using a function as a watchExpression
15509                    var food;
15510                    scope.foodCounter = 0;
15511                    expect(scope.foodCounter).toEqual(0);
15512                    scope.$watch(
15513                      // This function returns the value being watched. It is called for each turn of the $digest loop
15514                      function() { return food; },
15515                      // This is the change listener, called when the value returned from the above function changes
15516                      function(newValue, oldValue) {
15517                        if ( newValue !== oldValue ) {
15518                          // Only increment the counter if the value changed
15519                          scope.foodCounter = scope.foodCounter + 1;
15520                        }
15521                      }
15522                    );
15523                    // No digest has been run so the counter will be zero
15524                    expect(scope.foodCounter).toEqual(0);
15525
15526                    // Run the digest but since food has not changed count will still be zero
15527                    scope.$digest();
15528                    expect(scope.foodCounter).toEqual(0);
15529
15530                    // Update food and run digest.  Now the counter will increment
15531                    food = 'cheeseburger';
15532                    scope.$digest();
15533                    expect(scope.foodCounter).toEqual(1);
15534
15535                * ```
15536                *
15537                *
15538                *
15539                * @param {(function()|string)} watchExpression Expression that is evaluated on each
15540                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. A change in the return value triggers
15541                *    a call to the `listener`.
15542                *
15543                *    - `string`: Evaluated as {@link guide/expression expression}
15544                *    - `function(scope)`: called with current `scope` as a parameter.
15545                * @param {function(newVal, oldVal, scope)} listener Callback called whenever the value
15546                *    of `watchExpression` changes.
15547                *
15548                *    - `newVal` contains the current value of the `watchExpression`
15549                *    - `oldVal` contains the previous value of the `watchExpression`
15550                *    - `scope` refers to the current scope
15551                * @param {boolean=} objectEquality Compare for object equality using {@link angular.equals} instead of
15552                *     comparing for reference equality.
15553                * @returns {function()} Returns a deregistration function for this listener.
15554                */
15555               $watch: function(watchExp, listener, objectEquality, prettyPrintExpression) {
15556                 var get = $parse(watchExp);
15557
15558                 if (get.$$watchDelegate) {
15559                   return get.$$watchDelegate(this, listener, objectEquality, get, watchExp);
15560                 }
15561                 var scope = this,
15562                     array = scope.$$watchers,
15563                     watcher = {
15564                       fn: listener,
15565                       last: initWatchVal,
15566                       get: get,
15567                       exp: prettyPrintExpression || watchExp,
15568                       eq: !!objectEquality
15569                     };
15570
15571                 lastDirtyWatch = null;
15572
15573                 if (!isFunction(listener)) {
15574                   watcher.fn = noop;
15575                 }
15576
15577                 if (!array) {
15578                   array = scope.$$watchers = [];
15579                 }
15580                 // we use unshift since we use a while loop in $digest for speed.
15581                 // the while loop reads in reverse order.
15582                 array.unshift(watcher);
15583                 incrementWatchersCount(this, 1);
15584
15585                 return function deregisterWatch() {
15586                   if (arrayRemove(array, watcher) >= 0) {
15587                     incrementWatchersCount(scope, -1);
15588                   }
15589                   lastDirtyWatch = null;
15590                 };
15591               },
15592
15593               /**
15594                * @ngdoc method
15595                * @name $rootScope.Scope#$watchGroup
15596                * @kind function
15597                *
15598                * @description
15599                * A variant of {@link ng.$rootScope.Scope#$watch $watch()} where it watches an array of `watchExpressions`.
15600                * If any one expression in the collection changes the `listener` is executed.
15601                *
15602                * - The items in the `watchExpressions` array are observed via standard $watch operation and are examined on every
15603                *   call to $digest() to see if any items changes.
15604                * - The `listener` is called whenever any expression in the `watchExpressions` array changes.
15605                *
15606                * @param {Array.<string|Function(scope)>} watchExpressions Array of expressions that will be individually
15607                * watched using {@link ng.$rootScope.Scope#$watch $watch()}
15608                *
15609                * @param {function(newValues, oldValues, scope)} listener Callback called whenever the return value of any
15610                *    expression in `watchExpressions` changes
15611                *    The `newValues` array contains the current values of the `watchExpressions`, with the indexes matching
15612                *    those of `watchExpression`
15613                *    and the `oldValues` array contains the previous values of the `watchExpressions`, with the indexes matching
15614                *    those of `watchExpression`
15615                *    The `scope` refers to the current scope.
15616                * @returns {function()} Returns a de-registration function for all listeners.
15617                */
15618               $watchGroup: function(watchExpressions, listener) {
15619                 var oldValues = new Array(watchExpressions.length);
15620                 var newValues = new Array(watchExpressions.length);
15621                 var deregisterFns = [];
15622                 var self = this;
15623                 var changeReactionScheduled = false;
15624                 var firstRun = true;
15625
15626                 if (!watchExpressions.length) {
15627                   // No expressions means we call the listener ASAP
15628                   var shouldCall = true;
15629                   self.$evalAsync(function() {
15630                     if (shouldCall) listener(newValues, newValues, self);
15631                   });
15632                   return function deregisterWatchGroup() {
15633                     shouldCall = false;
15634                   };
15635                 }
15636
15637                 if (watchExpressions.length === 1) {
15638                   // Special case size of one
15639                   return this.$watch(watchExpressions[0], function watchGroupAction(value, oldValue, scope) {
15640                     newValues[0] = value;
15641                     oldValues[0] = oldValue;
15642                     listener(newValues, (value === oldValue) ? newValues : oldValues, scope);
15643                   });
15644                 }
15645
15646                 forEach(watchExpressions, function(expr, i) {
15647                   var unwatchFn = self.$watch(expr, function watchGroupSubAction(value, oldValue) {
15648                     newValues[i] = value;
15649                     oldValues[i] = oldValue;
15650                     if (!changeReactionScheduled) {
15651                       changeReactionScheduled = true;
15652                       self.$evalAsync(watchGroupAction);
15653                     }
15654                   });
15655                   deregisterFns.push(unwatchFn);
15656                 });
15657
15658                 function watchGroupAction() {
15659                   changeReactionScheduled = false;
15660
15661                   if (firstRun) {
15662                     firstRun = false;
15663                     listener(newValues, newValues, self);
15664                   } else {
15665                     listener(newValues, oldValues, self);
15666                   }
15667                 }
15668
15669                 return function deregisterWatchGroup() {
15670                   while (deregisterFns.length) {
15671                     deregisterFns.shift()();
15672                   }
15673                 };
15674               },
15675
15676
15677               /**
15678                * @ngdoc method
15679                * @name $rootScope.Scope#$watchCollection
15680                * @kind function
15681                *
15682                * @description
15683                * Shallow watches the properties of an object and fires whenever any of the properties change
15684                * (for arrays, this implies watching the array items; for object maps, this implies watching
15685                * the properties). If a change is detected, the `listener` callback is fired.
15686                *
15687                * - The `obj` collection is observed via standard $watch operation and is examined on every
15688                *   call to $digest() to see if any items have been added, removed, or moved.
15689                * - The `listener` is called whenever anything within the `obj` has changed. Examples include
15690                *   adding, removing, and moving items belonging to an object or array.
15691                *
15692                *
15693                * # Example
15694                * ```js
15695                   $scope.names = ['igor', 'matias', 'misko', 'james'];
15696                   $scope.dataCount = 4;
15697
15698                   $scope.$watchCollection('names', function(newNames, oldNames) {
15699                     $scope.dataCount = newNames.length;
15700                   });
15701
15702                   expect($scope.dataCount).toEqual(4);
15703                   $scope.$digest();
15704
15705                   //still at 4 ... no changes
15706                   expect($scope.dataCount).toEqual(4);
15707
15708                   $scope.names.pop();
15709                   $scope.$digest();
15710
15711                   //now there's been a change
15712                   expect($scope.dataCount).toEqual(3);
15713                * ```
15714                *
15715                *
15716                * @param {string|function(scope)} obj Evaluated as {@link guide/expression expression}. The
15717                *    expression value should evaluate to an object or an array which is observed on each
15718                *    {@link ng.$rootScope.Scope#$digest $digest} cycle. Any shallow change within the
15719                *    collection will trigger a call to the `listener`.
15720                *
15721                * @param {function(newCollection, oldCollection, scope)} listener a callback function called
15722                *    when a change is detected.
15723                *    - The `newCollection` object is the newly modified data obtained from the `obj` expression
15724                *    - The `oldCollection` object is a copy of the former collection data.
15725                *      Due to performance considerations, the`oldCollection` value is computed only if the
15726                *      `listener` function declares two or more arguments.
15727                *    - The `scope` argument refers to the current scope.
15728                *
15729                * @returns {function()} Returns a de-registration function for this listener. When the
15730                *    de-registration function is executed, the internal watch operation is terminated.
15731                */
15732               $watchCollection: function(obj, listener) {
15733                 $watchCollectionInterceptor.$stateful = true;
15734
15735                 var self = this;
15736                 // the current value, updated on each dirty-check run
15737                 var newValue;
15738                 // a shallow copy of the newValue from the last dirty-check run,
15739                 // updated to match newValue during dirty-check run
15740                 var oldValue;
15741                 // a shallow copy of the newValue from when the last change happened
15742                 var veryOldValue;
15743                 // only track veryOldValue if the listener is asking for it
15744                 var trackVeryOldValue = (listener.length > 1);
15745                 var changeDetected = 0;
15746                 var changeDetector = $parse(obj, $watchCollectionInterceptor);
15747                 var internalArray = [];
15748                 var internalObject = {};
15749                 var initRun = true;
15750                 var oldLength = 0;
15751
15752                 function $watchCollectionInterceptor(_value) {
15753                   newValue = _value;
15754                   var newLength, key, bothNaN, newItem, oldItem;
15755
15756                   // If the new value is undefined, then return undefined as the watch may be a one-time watch
15757                   if (isUndefined(newValue)) return;
15758
15759                   if (!isObject(newValue)) { // if primitive
15760                     if (oldValue !== newValue) {
15761                       oldValue = newValue;
15762                       changeDetected++;
15763                     }
15764                   } else if (isArrayLike(newValue)) {
15765                     if (oldValue !== internalArray) {
15766                       // we are transitioning from something which was not an array into array.
15767                       oldValue = internalArray;
15768                       oldLength = oldValue.length = 0;
15769                       changeDetected++;
15770                     }
15771
15772                     newLength = newValue.length;
15773
15774                     if (oldLength !== newLength) {
15775                       // if lengths do not match we need to trigger change notification
15776                       changeDetected++;
15777                       oldValue.length = oldLength = newLength;
15778                     }
15779                     // copy the items to oldValue and look for changes.
15780                     for (var i = 0; i < newLength; i++) {
15781                       oldItem = oldValue[i];
15782                       newItem = newValue[i];
15783
15784                       bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15785                       if (!bothNaN && (oldItem !== newItem)) {
15786                         changeDetected++;
15787                         oldValue[i] = newItem;
15788                       }
15789                     }
15790                   } else {
15791                     if (oldValue !== internalObject) {
15792                       // we are transitioning from something which was not an object into object.
15793                       oldValue = internalObject = {};
15794                       oldLength = 0;
15795                       changeDetected++;
15796                     }
15797                     // copy the items to oldValue and look for changes.
15798                     newLength = 0;
15799                     for (key in newValue) {
15800                       if (hasOwnProperty.call(newValue, key)) {
15801                         newLength++;
15802                         newItem = newValue[key];
15803                         oldItem = oldValue[key];
15804
15805                         if (key in oldValue) {
15806                           bothNaN = (oldItem !== oldItem) && (newItem !== newItem);
15807                           if (!bothNaN && (oldItem !== newItem)) {
15808                             changeDetected++;
15809                             oldValue[key] = newItem;
15810                           }
15811                         } else {
15812                           oldLength++;
15813                           oldValue[key] = newItem;
15814                           changeDetected++;
15815                         }
15816                       }
15817                     }
15818                     if (oldLength > newLength) {
15819                       // we used to have more keys, need to find them and destroy them.
15820                       changeDetected++;
15821                       for (key in oldValue) {
15822                         if (!hasOwnProperty.call(newValue, key)) {
15823                           oldLength--;
15824                           delete oldValue[key];
15825                         }
15826                       }
15827                     }
15828                   }
15829                   return changeDetected;
15830                 }
15831
15832                 function $watchCollectionAction() {
15833                   if (initRun) {
15834                     initRun = false;
15835                     listener(newValue, newValue, self);
15836                   } else {
15837                     listener(newValue, veryOldValue, self);
15838                   }
15839
15840                   // make a copy for the next time a collection is changed
15841                   if (trackVeryOldValue) {
15842                     if (!isObject(newValue)) {
15843                       //primitive
15844                       veryOldValue = newValue;
15845                     } else if (isArrayLike(newValue)) {
15846                       veryOldValue = new Array(newValue.length);
15847                       for (var i = 0; i < newValue.length; i++) {
15848                         veryOldValue[i] = newValue[i];
15849                       }
15850                     } else { // if object
15851                       veryOldValue = {};
15852                       for (var key in newValue) {
15853                         if (hasOwnProperty.call(newValue, key)) {
15854                           veryOldValue[key] = newValue[key];
15855                         }
15856                       }
15857                     }
15858                   }
15859                 }
15860
15861                 return this.$watch(changeDetector, $watchCollectionAction);
15862               },
15863
15864               /**
15865                * @ngdoc method
15866                * @name $rootScope.Scope#$digest
15867                * @kind function
15868                *
15869                * @description
15870                * Processes all of the {@link ng.$rootScope.Scope#$watch watchers} of the current scope and
15871                * its children. Because a {@link ng.$rootScope.Scope#$watch watcher}'s listener can change
15872                * the model, the `$digest()` keeps calling the {@link ng.$rootScope.Scope#$watch watchers}
15873                * until no more listeners are firing. This means that it is possible to get into an infinite
15874                * loop. This function will throw `'Maximum iteration limit exceeded.'` if the number of
15875                * iterations exceeds 10.
15876                *
15877                * Usually, you don't call `$digest()` directly in
15878                * {@link ng.directive:ngController controllers} or in
15879                * {@link ng.$compileProvider#directive directives}.
15880                * Instead, you should call {@link ng.$rootScope.Scope#$apply $apply()} (typically from within
15881                * a {@link ng.$compileProvider#directive directive}), which will force a `$digest()`.
15882                *
15883                * If you want to be notified whenever `$digest()` is called,
15884                * you can register a `watchExpression` function with
15885                * {@link ng.$rootScope.Scope#$watch $watch()} with no `listener`.
15886                *
15887                * In unit tests, you may need to call `$digest()` to simulate the scope life cycle.
15888                *
15889                * # Example
15890                * ```js
15891                    var scope = ...;
15892                    scope.name = 'misko';
15893                    scope.counter = 0;
15894
15895                    expect(scope.counter).toEqual(0);
15896                    scope.$watch('name', function(newValue, oldValue) {
15897                      scope.counter = scope.counter + 1;
15898                    });
15899                    expect(scope.counter).toEqual(0);
15900
15901                    scope.$digest();
15902                    // the listener is always called during the first $digest loop after it was registered
15903                    expect(scope.counter).toEqual(1);
15904
15905                    scope.$digest();
15906                    // but now it will not be called unless the value changes
15907                    expect(scope.counter).toEqual(1);
15908
15909                    scope.name = 'adam';
15910                    scope.$digest();
15911                    expect(scope.counter).toEqual(2);
15912                * ```
15913                *
15914                */
15915               $digest: function() {
15916                 var watch, value, last,
15917                     watchers,
15918                     length,
15919                     dirty, ttl = TTL,
15920                     next, current, target = this,
15921                     watchLog = [],
15922                     logIdx, logMsg, asyncTask;
15923
15924                 beginPhase('$digest');
15925                 // Check for changes to browser url that happened in sync before the call to $digest
15926                 $browser.$$checkUrlChange();
15927
15928                 if (this === $rootScope && applyAsyncId !== null) {
15929                   // If this is the root scope, and $applyAsync has scheduled a deferred $apply(), then
15930                   // cancel the scheduled $apply and flush the queue of expressions to be evaluated.
15931                   $browser.defer.cancel(applyAsyncId);
15932                   flushApplyAsync();
15933                 }
15934
15935                 lastDirtyWatch = null;
15936
15937                 do { // "while dirty" loop
15938                   dirty = false;
15939                   current = target;
15940
15941                   while (asyncQueue.length) {
15942                     try {
15943                       asyncTask = asyncQueue.shift();
15944                       asyncTask.scope.$eval(asyncTask.expression, asyncTask.locals);
15945                     } catch (e) {
15946                       $exceptionHandler(e);
15947                     }
15948                     lastDirtyWatch = null;
15949                   }
15950
15951                   traverseScopesLoop:
15952                   do { // "traverse the scopes" loop
15953                     if ((watchers = current.$$watchers)) {
15954                       // process our watches
15955                       length = watchers.length;
15956                       while (length--) {
15957                         try {
15958                           watch = watchers[length];
15959                           // Most common watches are on primitives, in which case we can short
15960                           // circuit it with === operator, only when === fails do we use .equals
15961                           if (watch) {
15962                             if ((value = watch.get(current)) !== (last = watch.last) &&
15963                                 !(watch.eq
15964                                     ? equals(value, last)
15965                                     : (typeof value === 'number' && typeof last === 'number'
15966                                        && isNaN(value) && isNaN(last)))) {
15967                               dirty = true;
15968                               lastDirtyWatch = watch;
15969                               watch.last = watch.eq ? copy(value, null) : value;
15970                               watch.fn(value, ((last === initWatchVal) ? value : last), current);
15971                               if (ttl < 5) {
15972                                 logIdx = 4 - ttl;
15973                                 if (!watchLog[logIdx]) watchLog[logIdx] = [];
15974                                 watchLog[logIdx].push({
15975                                   msg: isFunction(watch.exp) ? 'fn: ' + (watch.exp.name || watch.exp.toString()) : watch.exp,
15976                                   newVal: value,
15977                                   oldVal: last
15978                                 });
15979                               }
15980                             } else if (watch === lastDirtyWatch) {
15981                               // If the most recently dirty watcher is now clean, short circuit since the remaining watchers
15982                               // have already been tested.
15983                               dirty = false;
15984                               break traverseScopesLoop;
15985                             }
15986                           }
15987                         } catch (e) {
15988                           $exceptionHandler(e);
15989                         }
15990                       }
15991                     }
15992
15993                     // Insanity Warning: scope depth-first traversal
15994                     // yes, this code is a bit crazy, but it works and we have tests to prove it!
15995                     // this piece should be kept in sync with the traversal in $broadcast
15996                     if (!(next = ((current.$$watchersCount && current.$$childHead) ||
15997                         (current !== target && current.$$nextSibling)))) {
15998                       while (current !== target && !(next = current.$$nextSibling)) {
15999                         current = current.$parent;
16000                       }
16001                     }
16002                   } while ((current = next));
16003
16004                   // `break traverseScopesLoop;` takes us to here
16005
16006                   if ((dirty || asyncQueue.length) && !(ttl--)) {
16007                     clearPhase();
16008                     throw $rootScopeMinErr('infdig',
16009                         '{0} $digest() iterations reached. Aborting!\n' +
16010                         'Watchers fired in the last 5 iterations: {1}',
16011                         TTL, watchLog);
16012                   }
16013
16014                 } while (dirty || asyncQueue.length);
16015
16016                 clearPhase();
16017
16018                 while (postDigestQueue.length) {
16019                   try {
16020                     postDigestQueue.shift()();
16021                   } catch (e) {
16022                     $exceptionHandler(e);
16023                   }
16024                 }
16025               },
16026
16027
16028               /**
16029                * @ngdoc event
16030                * @name $rootScope.Scope#$destroy
16031                * @eventType broadcast on scope being destroyed
16032                *
16033                * @description
16034                * Broadcasted when a scope and its children are being destroyed.
16035                *
16036                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16037                * clean up DOM bindings before an element is removed from the DOM.
16038                */
16039
16040               /**
16041                * @ngdoc method
16042                * @name $rootScope.Scope#$destroy
16043                * @kind function
16044                *
16045                * @description
16046                * Removes the current scope (and all of its children) from the parent scope. Removal implies
16047                * that calls to {@link ng.$rootScope.Scope#$digest $digest()} will no longer
16048                * propagate to the current scope and its children. Removal also implies that the current
16049                * scope is eligible for garbage collection.
16050                *
16051                * The `$destroy()` is usually used by directives such as
16052                * {@link ng.directive:ngRepeat ngRepeat} for managing the
16053                * unrolling of the loop.
16054                *
16055                * Just before a scope is destroyed, a `$destroy` event is broadcasted on this scope.
16056                * Application code can register a `$destroy` event handler that will give it a chance to
16057                * perform any necessary cleanup.
16058                *
16059                * Note that, in AngularJS, there is also a `$destroy` jQuery event, which can be used to
16060                * clean up DOM bindings before an element is removed from the DOM.
16061                */
16062               $destroy: function() {
16063                 // We can't destroy a scope that has been already destroyed.
16064                 if (this.$$destroyed) return;
16065                 var parent = this.$parent;
16066
16067                 this.$broadcast('$destroy');
16068                 this.$$destroyed = true;
16069
16070                 if (this === $rootScope) {
16071                   //Remove handlers attached to window when $rootScope is removed
16072                   $browser.$$applicationDestroyed();
16073                 }
16074
16075                 incrementWatchersCount(this, -this.$$watchersCount);
16076                 for (var eventName in this.$$listenerCount) {
16077                   decrementListenerCount(this, this.$$listenerCount[eventName], eventName);
16078                 }
16079
16080                 // sever all the references to parent scopes (after this cleanup, the current scope should
16081                 // not be retained by any of our references and should be eligible for garbage collection)
16082                 if (parent && parent.$$childHead == this) parent.$$childHead = this.$$nextSibling;
16083                 if (parent && parent.$$childTail == this) parent.$$childTail = this.$$prevSibling;
16084                 if (this.$$prevSibling) this.$$prevSibling.$$nextSibling = this.$$nextSibling;
16085                 if (this.$$nextSibling) this.$$nextSibling.$$prevSibling = this.$$prevSibling;
16086
16087                 // Disable listeners, watchers and apply/digest methods
16088                 this.$destroy = this.$digest = this.$apply = this.$evalAsync = this.$applyAsync = noop;
16089                 this.$on = this.$watch = this.$watchGroup = function() { return noop; };
16090                 this.$$listeners = {};
16091
16092                 // Disconnect the next sibling to prevent `cleanUpScope` destroying those too
16093                 this.$$nextSibling = null;
16094                 cleanUpScope(this);
16095               },
16096
16097               /**
16098                * @ngdoc method
16099                * @name $rootScope.Scope#$eval
16100                * @kind function
16101                *
16102                * @description
16103                * Executes the `expression` on the current scope and returns the result. Any exceptions in
16104                * the expression are propagated (uncaught). This is useful when evaluating Angular
16105                * expressions.
16106                *
16107                * # Example
16108                * ```js
16109                    var scope = ng.$rootScope.Scope();
16110                    scope.a = 1;
16111                    scope.b = 2;
16112
16113                    expect(scope.$eval('a+b')).toEqual(3);
16114                    expect(scope.$eval(function(scope){ return scope.a + scope.b; })).toEqual(3);
16115                * ```
16116                *
16117                * @param {(string|function())=} expression An angular expression to be executed.
16118                *
16119                *    - `string`: execute using the rules as defined in  {@link guide/expression expression}.
16120                *    - `function(scope)`: execute the function with the current `scope` parameter.
16121                *
16122                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16123                * @returns {*} The result of evaluating the expression.
16124                */
16125               $eval: function(expr, locals) {
16126                 return $parse(expr)(this, locals);
16127               },
16128
16129               /**
16130                * @ngdoc method
16131                * @name $rootScope.Scope#$evalAsync
16132                * @kind function
16133                *
16134                * @description
16135                * Executes the expression on the current scope at a later point in time.
16136                *
16137                * The `$evalAsync` makes no guarantees as to when the `expression` will be executed, only
16138                * that:
16139                *
16140                *   - it will execute after the function that scheduled the evaluation (preferably before DOM
16141                *     rendering).
16142                *   - at least one {@link ng.$rootScope.Scope#$digest $digest cycle} will be performed after
16143                *     `expression` execution.
16144                *
16145                * Any exceptions from the execution of the expression are forwarded to the
16146                * {@link ng.$exceptionHandler $exceptionHandler} service.
16147                *
16148                * __Note:__ if this function is called outside of a `$digest` cycle, a new `$digest` cycle
16149                * will be scheduled. However, it is encouraged to always call code that changes the model
16150                * from within an `$apply` call. That includes code evaluated via `$evalAsync`.
16151                *
16152                * @param {(string|function())=} expression An angular expression to be executed.
16153                *
16154                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16155                *    - `function(scope)`: execute the function with the current `scope` parameter.
16156                *
16157                * @param {(object)=} locals Local variables object, useful for overriding values in scope.
16158                */
16159               $evalAsync: function(expr, locals) {
16160                 // if we are outside of an $digest loop and this is the first time we are scheduling async
16161                 // task also schedule async auto-flush
16162                 if (!$rootScope.$$phase && !asyncQueue.length) {
16163                   $browser.defer(function() {
16164                     if (asyncQueue.length) {
16165                       $rootScope.$digest();
16166                     }
16167                   });
16168                 }
16169
16170                 asyncQueue.push({scope: this, expression: expr, locals: locals});
16171               },
16172
16173               $$postDigest: function(fn) {
16174                 postDigestQueue.push(fn);
16175               },
16176
16177               /**
16178                * @ngdoc method
16179                * @name $rootScope.Scope#$apply
16180                * @kind function
16181                *
16182                * @description
16183                * `$apply()` is used to execute an expression in angular from outside of the angular
16184                * framework. (For example from browser DOM events, setTimeout, XHR or third party libraries).
16185                * Because we are calling into the angular framework we need to perform proper scope life
16186                * cycle of {@link ng.$exceptionHandler exception handling},
16187                * {@link ng.$rootScope.Scope#$digest executing watches}.
16188                *
16189                * ## Life cycle
16190                *
16191                * # Pseudo-Code of `$apply()`
16192                * ```js
16193                    function $apply(expr) {
16194                      try {
16195                        return $eval(expr);
16196                      } catch (e) {
16197                        $exceptionHandler(e);
16198                      } finally {
16199                        $root.$digest();
16200                      }
16201                    }
16202                * ```
16203                *
16204                *
16205                * Scope's `$apply()` method transitions through the following stages:
16206                *
16207                * 1. The {@link guide/expression expression} is executed using the
16208                *    {@link ng.$rootScope.Scope#$eval $eval()} method.
16209                * 2. Any exceptions from the execution of the expression are forwarded to the
16210                *    {@link ng.$exceptionHandler $exceptionHandler} service.
16211                * 3. The {@link ng.$rootScope.Scope#$watch watch} listeners are fired immediately after the
16212                *    expression was executed using the {@link ng.$rootScope.Scope#$digest $digest()} method.
16213                *
16214                *
16215                * @param {(string|function())=} exp An angular expression to be executed.
16216                *
16217                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16218                *    - `function(scope)`: execute the function with current `scope` parameter.
16219                *
16220                * @returns {*} The result of evaluating the expression.
16221                */
16222               $apply: function(expr) {
16223                 try {
16224                   beginPhase('$apply');
16225                   try {
16226                     return this.$eval(expr);
16227                   } finally {
16228                     clearPhase();
16229                   }
16230                 } catch (e) {
16231                   $exceptionHandler(e);
16232                 } finally {
16233                   try {
16234                     $rootScope.$digest();
16235                   } catch (e) {
16236                     $exceptionHandler(e);
16237                     throw e;
16238                   }
16239                 }
16240               },
16241
16242               /**
16243                * @ngdoc method
16244                * @name $rootScope.Scope#$applyAsync
16245                * @kind function
16246                *
16247                * @description
16248                * Schedule the invocation of $apply to occur at a later time. The actual time difference
16249                * varies across browsers, but is typically around ~10 milliseconds.
16250                *
16251                * This can be used to queue up multiple expressions which need to be evaluated in the same
16252                * digest.
16253                *
16254                * @param {(string|function())=} exp An angular expression to be executed.
16255                *
16256                *    - `string`: execute using the rules as defined in {@link guide/expression expression}.
16257                *    - `function(scope)`: execute the function with current `scope` parameter.
16258                */
16259               $applyAsync: function(expr) {
16260                 var scope = this;
16261                 expr && applyAsyncQueue.push($applyAsyncExpression);
16262                 scheduleApplyAsync();
16263
16264                 function $applyAsyncExpression() {
16265                   scope.$eval(expr);
16266                 }
16267               },
16268
16269               /**
16270                * @ngdoc method
16271                * @name $rootScope.Scope#$on
16272                * @kind function
16273                *
16274                * @description
16275                * Listens on events of a given type. See {@link ng.$rootScope.Scope#$emit $emit} for
16276                * discussion of event life cycle.
16277                *
16278                * The event listener function format is: `function(event, args...)`. The `event` object
16279                * passed into the listener has the following attributes:
16280                *
16281                *   - `targetScope` - `{Scope}`: the scope on which the event was `$emit`-ed or
16282                *     `$broadcast`-ed.
16283                *   - `currentScope` - `{Scope}`: the scope that is currently handling the event. Once the
16284                *     event propagates through the scope hierarchy, this property is set to null.
16285                *   - `name` - `{string}`: name of the event.
16286                *   - `stopPropagation` - `{function=}`: calling `stopPropagation` function will cancel
16287                *     further event propagation (available only for events that were `$emit`-ed).
16288                *   - `preventDefault` - `{function}`: calling `preventDefault` sets `defaultPrevented` flag
16289                *     to true.
16290                *   - `defaultPrevented` - `{boolean}`: true if `preventDefault` was called.
16291                *
16292                * @param {string} name Event name to listen on.
16293                * @param {function(event, ...args)} listener Function to call when the event is emitted.
16294                * @returns {function()} Returns a deregistration function for this listener.
16295                */
16296               $on: function(name, listener) {
16297                 var namedListeners = this.$$listeners[name];
16298                 if (!namedListeners) {
16299                   this.$$listeners[name] = namedListeners = [];
16300                 }
16301                 namedListeners.push(listener);
16302
16303                 var current = this;
16304                 do {
16305                   if (!current.$$listenerCount[name]) {
16306                     current.$$listenerCount[name] = 0;
16307                   }
16308                   current.$$listenerCount[name]++;
16309                 } while ((current = current.$parent));
16310
16311                 var self = this;
16312                 return function() {
16313                   var indexOfListener = namedListeners.indexOf(listener);
16314                   if (indexOfListener !== -1) {
16315                     namedListeners[indexOfListener] = null;
16316                     decrementListenerCount(self, 1, name);
16317                   }
16318                 };
16319               },
16320
16321
16322               /**
16323                * @ngdoc method
16324                * @name $rootScope.Scope#$emit
16325                * @kind function
16326                *
16327                * @description
16328                * Dispatches an event `name` upwards through the scope hierarchy notifying the
16329                * registered {@link ng.$rootScope.Scope#$on} listeners.
16330                *
16331                * The event life cycle starts at the scope on which `$emit` was called. All
16332                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16333                * notified. Afterwards, the event traverses upwards toward the root scope and calls all
16334                * registered listeners along the way. The event will stop propagating if one of the listeners
16335                * cancels it.
16336                *
16337                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16338                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16339                *
16340                * @param {string} name Event name to emit.
16341                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16342                * @return {Object} Event object (see {@link ng.$rootScope.Scope#$on}).
16343                */
16344               $emit: function(name, args) {
16345                 var empty = [],
16346                     namedListeners,
16347                     scope = this,
16348                     stopPropagation = false,
16349                     event = {
16350                       name: name,
16351                       targetScope: scope,
16352                       stopPropagation: function() {stopPropagation = true;},
16353                       preventDefault: function() {
16354                         event.defaultPrevented = true;
16355                       },
16356                       defaultPrevented: false
16357                     },
16358                     listenerArgs = concat([event], arguments, 1),
16359                     i, length;
16360
16361                 do {
16362                   namedListeners = scope.$$listeners[name] || empty;
16363                   event.currentScope = scope;
16364                   for (i = 0, length = namedListeners.length; i < length; i++) {
16365
16366                     // if listeners were deregistered, defragment the array
16367                     if (!namedListeners[i]) {
16368                       namedListeners.splice(i, 1);
16369                       i--;
16370                       length--;
16371                       continue;
16372                     }
16373                     try {
16374                       //allow all listeners attached to the current scope to run
16375                       namedListeners[i].apply(null, listenerArgs);
16376                     } catch (e) {
16377                       $exceptionHandler(e);
16378                     }
16379                   }
16380                   //if any listener on the current scope stops propagation, prevent bubbling
16381                   if (stopPropagation) {
16382                     event.currentScope = null;
16383                     return event;
16384                   }
16385                   //traverse upwards
16386                   scope = scope.$parent;
16387                 } while (scope);
16388
16389                 event.currentScope = null;
16390
16391                 return event;
16392               },
16393
16394
16395               /**
16396                * @ngdoc method
16397                * @name $rootScope.Scope#$broadcast
16398                * @kind function
16399                *
16400                * @description
16401                * Dispatches an event `name` downwards to all child scopes (and their children) notifying the
16402                * registered {@link ng.$rootScope.Scope#$on} listeners.
16403                *
16404                * The event life cycle starts at the scope on which `$broadcast` was called. All
16405                * {@link ng.$rootScope.Scope#$on listeners} listening for `name` event on this scope get
16406                * notified. Afterwards, the event propagates to all direct and indirect scopes of the current
16407                * scope and calls all registered listeners along the way. The event cannot be canceled.
16408                *
16409                * Any exception emitted from the {@link ng.$rootScope.Scope#$on listeners} will be passed
16410                * onto the {@link ng.$exceptionHandler $exceptionHandler} service.
16411                *
16412                * @param {string} name Event name to broadcast.
16413                * @param {...*} args Optional one or more arguments which will be passed onto the event listeners.
16414                * @return {Object} Event object, see {@link ng.$rootScope.Scope#$on}
16415                */
16416               $broadcast: function(name, args) {
16417                 var target = this,
16418                     current = target,
16419                     next = target,
16420                     event = {
16421                       name: name,
16422                       targetScope: target,
16423                       preventDefault: function() {
16424                         event.defaultPrevented = true;
16425                       },
16426                       defaultPrevented: false
16427                     };
16428
16429                 if (!target.$$listenerCount[name]) return event;
16430
16431                 var listenerArgs = concat([event], arguments, 1),
16432                     listeners, i, length;
16433
16434                 //down while you can, then up and next sibling or up and next sibling until back at root
16435                 while ((current = next)) {
16436                   event.currentScope = current;
16437                   listeners = current.$$listeners[name] || [];
16438                   for (i = 0, length = listeners.length; i < length; i++) {
16439                     // if listeners were deregistered, defragment the array
16440                     if (!listeners[i]) {
16441                       listeners.splice(i, 1);
16442                       i--;
16443                       length--;
16444                       continue;
16445                     }
16446
16447                     try {
16448                       listeners[i].apply(null, listenerArgs);
16449                     } catch (e) {
16450                       $exceptionHandler(e);
16451                     }
16452                   }
16453
16454                   // Insanity Warning: scope depth-first traversal
16455                   // yes, this code is a bit crazy, but it works and we have tests to prove it!
16456                   // this piece should be kept in sync with the traversal in $digest
16457                   // (though it differs due to having the extra check for $$listenerCount)
16458                   if (!(next = ((current.$$listenerCount[name] && current.$$childHead) ||
16459                       (current !== target && current.$$nextSibling)))) {
16460                     while (current !== target && !(next = current.$$nextSibling)) {
16461                       current = current.$parent;
16462                     }
16463                   }
16464                 }
16465
16466                 event.currentScope = null;
16467                 return event;
16468               }
16469             };
16470
16471             var $rootScope = new Scope();
16472
16473             //The internal queues. Expose them on the $rootScope for debugging/testing purposes.
16474             var asyncQueue = $rootScope.$$asyncQueue = [];
16475             var postDigestQueue = $rootScope.$$postDigestQueue = [];
16476             var applyAsyncQueue = $rootScope.$$applyAsyncQueue = [];
16477
16478             return $rootScope;
16479
16480
16481             function beginPhase(phase) {
16482               if ($rootScope.$$phase) {
16483                 throw $rootScopeMinErr('inprog', '{0} already in progress', $rootScope.$$phase);
16484               }
16485
16486               $rootScope.$$phase = phase;
16487             }
16488
16489             function clearPhase() {
16490               $rootScope.$$phase = null;
16491             }
16492
16493             function incrementWatchersCount(current, count) {
16494               do {
16495                 current.$$watchersCount += count;
16496               } while ((current = current.$parent));
16497             }
16498
16499             function decrementListenerCount(current, count, name) {
16500               do {
16501                 current.$$listenerCount[name] -= count;
16502
16503                 if (current.$$listenerCount[name] === 0) {
16504                   delete current.$$listenerCount[name];
16505                 }
16506               } while ((current = current.$parent));
16507             }
16508
16509             /**
16510              * function used as an initial value for watchers.
16511              * because it's unique we can easily tell it apart from other values
16512              */
16513             function initWatchVal() {}
16514
16515             function flushApplyAsync() {
16516               while (applyAsyncQueue.length) {
16517                 try {
16518                   applyAsyncQueue.shift()();
16519                 } catch (e) {
16520                   $exceptionHandler(e);
16521                 }
16522               }
16523               applyAsyncId = null;
16524             }
16525
16526             function scheduleApplyAsync() {
16527               if (applyAsyncId === null) {
16528                 applyAsyncId = $browser.defer(function() {
16529                   $rootScope.$apply(flushApplyAsync);
16530                 });
16531               }
16532             }
16533           }];
16534         }
16535
16536         /**
16537          * @description
16538          * Private service to sanitize uris for links and images. Used by $compile and $sanitize.
16539          */
16540         function $$SanitizeUriProvider() {
16541           var aHrefSanitizationWhitelist = /^\s*(https?|ftp|mailto|tel|file):/,
16542             imgSrcSanitizationWhitelist = /^\s*((https?|ftp|file|blob):|data:image\/)/;
16543
16544           /**
16545            * @description
16546            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16547            * urls during a[href] sanitization.
16548            *
16549            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16550            *
16551            * Any url about to be assigned to a[href] via data-binding is first normalized and turned into
16552            * an absolute url. Afterwards, the url is matched against the `aHrefSanitizationWhitelist`
16553            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16554            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16555            *
16556            * @param {RegExp=} regexp New regexp to whitelist urls with.
16557            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16558            *    chaining otherwise.
16559            */
16560           this.aHrefSanitizationWhitelist = function(regexp) {
16561             if (isDefined(regexp)) {
16562               aHrefSanitizationWhitelist = regexp;
16563               return this;
16564             }
16565             return aHrefSanitizationWhitelist;
16566           };
16567
16568
16569           /**
16570            * @description
16571            * Retrieves or overrides the default regular expression that is used for whitelisting of safe
16572            * urls during img[src] sanitization.
16573            *
16574            * The sanitization is a security measure aimed at prevent XSS attacks via html links.
16575            *
16576            * Any url about to be assigned to img[src] via data-binding is first normalized and turned into
16577            * an absolute url. Afterwards, the url is matched against the `imgSrcSanitizationWhitelist`
16578            * regular expression. If a match is found, the original url is written into the dom. Otherwise,
16579            * the absolute url is prefixed with `'unsafe:'` string and only then is it written into the DOM.
16580            *
16581            * @param {RegExp=} regexp New regexp to whitelist urls with.
16582            * @returns {RegExp|ng.$compileProvider} Current RegExp if called without value or self for
16583            *    chaining otherwise.
16584            */
16585           this.imgSrcSanitizationWhitelist = function(regexp) {
16586             if (isDefined(regexp)) {
16587               imgSrcSanitizationWhitelist = regexp;
16588               return this;
16589             }
16590             return imgSrcSanitizationWhitelist;
16591           };
16592
16593           this.$get = function() {
16594             return function sanitizeUri(uri, isImage) {
16595               var regex = isImage ? imgSrcSanitizationWhitelist : aHrefSanitizationWhitelist;
16596               var normalizedVal;
16597               normalizedVal = urlResolve(uri).href;
16598               if (normalizedVal !== '' && !normalizedVal.match(regex)) {
16599                 return 'unsafe:' + normalizedVal;
16600               }
16601               return uri;
16602             };
16603           };
16604         }
16605
16606         /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
16607          *     Any commits to this file should be reviewed with security in mind.  *
16608          *   Changes to this file can potentially create security vulnerabilities. *
16609          *          An approval from 2 Core members with history of modifying      *
16610          *                         this file is required.                          *
16611          *                                                                         *
16612          *  Does the change somehow allow for arbitrary javascript to be executed? *
16613          *    Or allows for someone to change the prototype of built-in objects?   *
16614          *     Or gives undesired access to variables likes document or window?    *
16615          * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
16616
16617         var $sceMinErr = minErr('$sce');
16618
16619         var SCE_CONTEXTS = {
16620           HTML: 'html',
16621           CSS: 'css',
16622           URL: 'url',
16623           // RESOURCE_URL is a subtype of URL used in contexts where a privileged resource is sourced from a
16624           // url.  (e.g. ng-include, script src, templateUrl)
16625           RESOURCE_URL: 'resourceUrl',
16626           JS: 'js'
16627         };
16628
16629         // Helper functions follow.
16630
16631         function adjustMatcher(matcher) {
16632           if (matcher === 'self') {
16633             return matcher;
16634           } else if (isString(matcher)) {
16635             // Strings match exactly except for 2 wildcards - '*' and '**'.
16636             // '*' matches any character except those from the set ':/.?&'.
16637             // '**' matches any character (like .* in a RegExp).
16638             // More than 2 *'s raises an error as it's ill defined.
16639             if (matcher.indexOf('***') > -1) {
16640               throw $sceMinErr('iwcard',
16641                   'Illegal sequence *** in string matcher.  String: {0}', matcher);
16642             }
16643             matcher = escapeForRegexp(matcher).
16644                           replace('\\*\\*', '.*').
16645                           replace('\\*', '[^:/.?&;]*');
16646             return new RegExp('^' + matcher + '$');
16647           } else if (isRegExp(matcher)) {
16648             // The only other type of matcher allowed is a Regexp.
16649             // Match entire URL / disallow partial matches.
16650             // Flags are reset (i.e. no global, ignoreCase or multiline)
16651             return new RegExp('^' + matcher.source + '$');
16652           } else {
16653             throw $sceMinErr('imatcher',
16654                 'Matchers may only be "self", string patterns or RegExp objects');
16655           }
16656         }
16657
16658
16659         function adjustMatchers(matchers) {
16660           var adjustedMatchers = [];
16661           if (isDefined(matchers)) {
16662             forEach(matchers, function(matcher) {
16663               adjustedMatchers.push(adjustMatcher(matcher));
16664             });
16665           }
16666           return adjustedMatchers;
16667         }
16668
16669
16670         /**
16671          * @ngdoc service
16672          * @name $sceDelegate
16673          * @kind function
16674          *
16675          * @description
16676          *
16677          * `$sceDelegate` is a service that is used by the `$sce` service to provide {@link ng.$sce Strict
16678          * Contextual Escaping (SCE)} services to AngularJS.
16679          *
16680          * Typically, you would configure or override the {@link ng.$sceDelegate $sceDelegate} instead of
16681          * the `$sce` service to customize the way Strict Contextual Escaping works in AngularJS.  This is
16682          * because, while the `$sce` provides numerous shorthand methods, etc., you really only need to
16683          * override 3 core functions (`trustAs`, `getTrusted` and `valueOf`) to replace the way things
16684          * work because `$sce` delegates to `$sceDelegate` for these operations.
16685          *
16686          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} to configure this service.
16687          *
16688          * The default instance of `$sceDelegate` should work out of the box with little pain.  While you
16689          * can override it completely to change the behavior of `$sce`, the common case would
16690          * involve configuring the {@link ng.$sceDelegateProvider $sceDelegateProvider} instead by setting
16691          * your own whitelists and blacklists for trusting URLs used for loading AngularJS resources such as
16692          * templates.  Refer {@link ng.$sceDelegateProvider#resourceUrlWhitelist
16693          * $sceDelegateProvider.resourceUrlWhitelist} and {@link
16694          * ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16695          */
16696
16697         /**
16698          * @ngdoc provider
16699          * @name $sceDelegateProvider
16700          * @description
16701          *
16702          * The `$sceDelegateProvider` provider allows developers to configure the {@link ng.$sceDelegate
16703          * $sceDelegate} service.  This allows one to get/set the whitelists and blacklists used to ensure
16704          * that the URLs used for sourcing Angular templates are safe.  Refer {@link
16705          * ng.$sceDelegateProvider#resourceUrlWhitelist $sceDelegateProvider.resourceUrlWhitelist} and
16706          * {@link ng.$sceDelegateProvider#resourceUrlBlacklist $sceDelegateProvider.resourceUrlBlacklist}
16707          *
16708          * For the general details about this service in Angular, read the main page for {@link ng.$sce
16709          * Strict Contextual Escaping (SCE)}.
16710          *
16711          * **Example**:  Consider the following case. <a name="example"></a>
16712          *
16713          * - your app is hosted at url `http://myapp.example.com/`
16714          * - but some of your templates are hosted on other domains you control such as
16715          *   `http://srv01.assets.example.com/`,  `http://srv02.assets.example.com/`, etc.
16716          * - and you have an open redirect at `http://myapp.example.com/clickThru?...`.
16717          *
16718          * Here is what a secure configuration for this scenario might look like:
16719          *
16720          * ```
16721          *  angular.module('myApp', []).config(function($sceDelegateProvider) {
16722          *    $sceDelegateProvider.resourceUrlWhitelist([
16723          *      // Allow same origin resource loads.
16724          *      'self',
16725          *      // Allow loading from our assets domain.  Notice the difference between * and **.
16726          *      'http://srv*.assets.example.com/**'
16727          *    ]);
16728          *
16729          *    // The blacklist overrides the whitelist so the open redirect here is blocked.
16730          *    $sceDelegateProvider.resourceUrlBlacklist([
16731          *      'http://myapp.example.com/clickThru**'
16732          *    ]);
16733          *  });
16734          * ```
16735          */
16736
16737         function $SceDelegateProvider() {
16738           this.SCE_CONTEXTS = SCE_CONTEXTS;
16739
16740           // Resource URLs can also be trusted by policy.
16741           var resourceUrlWhitelist = ['self'],
16742               resourceUrlBlacklist = [];
16743
16744           /**
16745            * @ngdoc method
16746            * @name $sceDelegateProvider#resourceUrlWhitelist
16747            * @kind function
16748            *
16749            * @param {Array=} whitelist When provided, replaces the resourceUrlWhitelist with the value
16750            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16751            *     changes to the array are ignored.
16752            *
16753            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16754            *     allowed in this array.
16755            *
16756            *     Note: **an empty whitelist array will block all URLs**!
16757            *
16758            * @return {Array} the currently set whitelist array.
16759            *
16760            * The **default value** when no whitelist has been explicitly set is `['self']` allowing only
16761            * same origin resource requests.
16762            *
16763            * @description
16764            * Sets/Gets the whitelist of trusted resource URLs.
16765            */
16766           this.resourceUrlWhitelist = function(value) {
16767             if (arguments.length) {
16768               resourceUrlWhitelist = adjustMatchers(value);
16769             }
16770             return resourceUrlWhitelist;
16771           };
16772
16773           /**
16774            * @ngdoc method
16775            * @name $sceDelegateProvider#resourceUrlBlacklist
16776            * @kind function
16777            *
16778            * @param {Array=} blacklist When provided, replaces the resourceUrlBlacklist with the value
16779            *     provided.  This must be an array or null.  A snapshot of this array is used so further
16780            *     changes to the array are ignored.
16781            *
16782            *     Follow {@link ng.$sce#resourceUrlPatternItem this link} for a description of the items
16783            *     allowed in this array.
16784            *
16785            *     The typical usage for the blacklist is to **block
16786            *     [open redirects](http://cwe.mitre.org/data/definitions/601.html)** served by your domain as
16787            *     these would otherwise be trusted but actually return content from the redirected domain.
16788            *
16789            *     Finally, **the blacklist overrides the whitelist** and has the final say.
16790            *
16791            * @return {Array} the currently set blacklist array.
16792            *
16793            * The **default value** when no whitelist has been explicitly set is the empty array (i.e. there
16794            * is no blacklist.)
16795            *
16796            * @description
16797            * Sets/Gets the blacklist of trusted resource URLs.
16798            */
16799
16800           this.resourceUrlBlacklist = function(value) {
16801             if (arguments.length) {
16802               resourceUrlBlacklist = adjustMatchers(value);
16803             }
16804             return resourceUrlBlacklist;
16805           };
16806
16807           this.$get = ['$injector', function($injector) {
16808
16809             var htmlSanitizer = function htmlSanitizer(html) {
16810               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16811             };
16812
16813             if ($injector.has('$sanitize')) {
16814               htmlSanitizer = $injector.get('$sanitize');
16815             }
16816
16817
16818             function matchUrl(matcher, parsedUrl) {
16819               if (matcher === 'self') {
16820                 return urlIsSameOrigin(parsedUrl);
16821               } else {
16822                 // definitely a regex.  See adjustMatchers()
16823                 return !!matcher.exec(parsedUrl.href);
16824               }
16825             }
16826
16827             function isResourceUrlAllowedByPolicy(url) {
16828               var parsedUrl = urlResolve(url.toString());
16829               var i, n, allowed = false;
16830               // Ensure that at least one item from the whitelist allows this url.
16831               for (i = 0, n = resourceUrlWhitelist.length; i < n; i++) {
16832                 if (matchUrl(resourceUrlWhitelist[i], parsedUrl)) {
16833                   allowed = true;
16834                   break;
16835                 }
16836               }
16837               if (allowed) {
16838                 // Ensure that no item from the blacklist blocked this url.
16839                 for (i = 0, n = resourceUrlBlacklist.length; i < n; i++) {
16840                   if (matchUrl(resourceUrlBlacklist[i], parsedUrl)) {
16841                     allowed = false;
16842                     break;
16843                   }
16844                 }
16845               }
16846               return allowed;
16847             }
16848
16849             function generateHolderType(Base) {
16850               var holderType = function TrustedValueHolderType(trustedValue) {
16851                 this.$$unwrapTrustedValue = function() {
16852                   return trustedValue;
16853                 };
16854               };
16855               if (Base) {
16856                 holderType.prototype = new Base();
16857               }
16858               holderType.prototype.valueOf = function sceValueOf() {
16859                 return this.$$unwrapTrustedValue();
16860               };
16861               holderType.prototype.toString = function sceToString() {
16862                 return this.$$unwrapTrustedValue().toString();
16863               };
16864               return holderType;
16865             }
16866
16867             var trustedValueHolderBase = generateHolderType(),
16868                 byType = {};
16869
16870             byType[SCE_CONTEXTS.HTML] = generateHolderType(trustedValueHolderBase);
16871             byType[SCE_CONTEXTS.CSS] = generateHolderType(trustedValueHolderBase);
16872             byType[SCE_CONTEXTS.URL] = generateHolderType(trustedValueHolderBase);
16873             byType[SCE_CONTEXTS.JS] = generateHolderType(trustedValueHolderBase);
16874             byType[SCE_CONTEXTS.RESOURCE_URL] = generateHolderType(byType[SCE_CONTEXTS.URL]);
16875
16876             /**
16877              * @ngdoc method
16878              * @name $sceDelegate#trustAs
16879              *
16880              * @description
16881              * Returns an object that is trusted by angular for use in specified strict
16882              * contextual escaping contexts (such as ng-bind-html, ng-include, any src
16883              * attribute interpolation, any dom event binding attribute interpolation
16884              * such as for onclick,  etc.) that uses the provided value.
16885              * See {@link ng.$sce $sce} for enabling strict contextual escaping.
16886              *
16887              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
16888              *   resourceUrl, html, js and css.
16889              * @param {*} value The value that that should be considered trusted/safe.
16890              * @returns {*} A value that can be used to stand in for the provided `value` in places
16891              * where Angular expects a $sce.trustAs() return value.
16892              */
16893             function trustAs(type, trustedValue) {
16894               var Constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16895               if (!Constructor) {
16896                 throw $sceMinErr('icontext',
16897                     'Attempted to trust a value in invalid context. Context: {0}; Value: {1}',
16898                     type, trustedValue);
16899               }
16900               if (trustedValue === null || isUndefined(trustedValue) || trustedValue === '') {
16901                 return trustedValue;
16902               }
16903               // All the current contexts in SCE_CONTEXTS happen to be strings.  In order to avoid trusting
16904               // mutable objects, we ensure here that the value passed in is actually a string.
16905               if (typeof trustedValue !== 'string') {
16906                 throw $sceMinErr('itype',
16907                     'Attempted to trust a non-string value in a content requiring a string: Context: {0}',
16908                     type);
16909               }
16910               return new Constructor(trustedValue);
16911             }
16912
16913             /**
16914              * @ngdoc method
16915              * @name $sceDelegate#valueOf
16916              *
16917              * @description
16918              * If the passed parameter had been returned by a prior call to {@link ng.$sceDelegate#trustAs
16919              * `$sceDelegate.trustAs`}, returns the value that had been passed to {@link
16920              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.
16921              *
16922              * If the passed parameter is not a value that had been returned by {@link
16923              * ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}, returns it as-is.
16924              *
16925              * @param {*} value The result of a prior {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}
16926              *      call or anything else.
16927              * @returns {*} The `value` that was originally provided to {@link ng.$sceDelegate#trustAs
16928              *     `$sceDelegate.trustAs`} if `value` is the result of such a call.  Otherwise, returns
16929              *     `value` unchanged.
16930              */
16931             function valueOf(maybeTrusted) {
16932               if (maybeTrusted instanceof trustedValueHolderBase) {
16933                 return maybeTrusted.$$unwrapTrustedValue();
16934               } else {
16935                 return maybeTrusted;
16936               }
16937             }
16938
16939             /**
16940              * @ngdoc method
16941              * @name $sceDelegate#getTrusted
16942              *
16943              * @description
16944              * Takes the result of a {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`} call and
16945              * returns the originally supplied value if the queried context type is a supertype of the
16946              * created type.  If this condition isn't satisfied, throws an exception.
16947              *
16948              * @param {string} type The kind of context in which this value is to be used.
16949              * @param {*} maybeTrusted The result of a prior {@link ng.$sceDelegate#trustAs
16950              *     `$sceDelegate.trustAs`} call.
16951              * @returns {*} The value the was originally provided to {@link ng.$sceDelegate#trustAs
16952              *     `$sceDelegate.trustAs`} if valid in this context.  Otherwise, throws an exception.
16953              */
16954             function getTrusted(type, maybeTrusted) {
16955               if (maybeTrusted === null || isUndefined(maybeTrusted) || maybeTrusted === '') {
16956                 return maybeTrusted;
16957               }
16958               var constructor = (byType.hasOwnProperty(type) ? byType[type] : null);
16959               if (constructor && maybeTrusted instanceof constructor) {
16960                 return maybeTrusted.$$unwrapTrustedValue();
16961               }
16962               // If we get here, then we may only take one of two actions.
16963               // 1. sanitize the value for the requested type, or
16964               // 2. throw an exception.
16965               if (type === SCE_CONTEXTS.RESOURCE_URL) {
16966                 if (isResourceUrlAllowedByPolicy(maybeTrusted)) {
16967                   return maybeTrusted;
16968                 } else {
16969                   throw $sceMinErr('insecurl',
16970                       'Blocked loading resource from url not allowed by $sceDelegate policy.  URL: {0}',
16971                       maybeTrusted.toString());
16972                 }
16973               } else if (type === SCE_CONTEXTS.HTML) {
16974                 return htmlSanitizer(maybeTrusted);
16975               }
16976               throw $sceMinErr('unsafe', 'Attempting to use an unsafe value in a safe context.');
16977             }
16978
16979             return { trustAs: trustAs,
16980                      getTrusted: getTrusted,
16981                      valueOf: valueOf };
16982           }];
16983         }
16984
16985
16986         /**
16987          * @ngdoc provider
16988          * @name $sceProvider
16989          * @description
16990          *
16991          * The $sceProvider provider allows developers to configure the {@link ng.$sce $sce} service.
16992          * -   enable/disable Strict Contextual Escaping (SCE) in a module
16993          * -   override the default implementation with a custom delegate
16994          *
16995          * Read more about {@link ng.$sce Strict Contextual Escaping (SCE)}.
16996          */
16997
16998         /* jshint maxlen: false*/
16999
17000         /**
17001          * @ngdoc service
17002          * @name $sce
17003          * @kind function
17004          *
17005          * @description
17006          *
17007          * `$sce` is a service that provides Strict Contextual Escaping services to AngularJS.
17008          *
17009          * # Strict Contextual Escaping
17010          *
17011          * Strict Contextual Escaping (SCE) is a mode in which AngularJS requires bindings in certain
17012          * contexts to result in a value that is marked as safe to use for that context.  One example of
17013          * such a context is binding arbitrary html controlled by the user via `ng-bind-html`.  We refer
17014          * to these contexts as privileged or SCE contexts.
17015          *
17016          * As of version 1.2, Angular ships with SCE enabled by default.
17017          *
17018          * Note:  When enabled (the default), IE<11 in quirks mode is not supported.  In this mode, IE<11 allow
17019          * one to execute arbitrary javascript by the use of the expression() syntax.  Refer
17020          * <http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx> to learn more about them.
17021          * You can ensure your document is in standards mode and not quirks mode by adding `<!doctype html>`
17022          * to the top of your HTML document.
17023          *
17024          * SCE assists in writing code in way that (a) is secure by default and (b) makes auditing for
17025          * security vulnerabilities such as XSS, clickjacking, etc. a lot easier.
17026          *
17027          * Here's an example of a binding in a privileged context:
17028          *
17029          * ```
17030          * <input ng-model="userHtml" aria-label="User input">
17031          * <div ng-bind-html="userHtml"></div>
17032          * ```
17033          *
17034          * Notice that `ng-bind-html` is bound to `userHtml` controlled by the user.  With SCE
17035          * disabled, this application allows the user to render arbitrary HTML into the DIV.
17036          * In a more realistic example, one may be rendering user comments, blog articles, etc. via
17037          * bindings.  (HTML is just one example of a context where rendering user controlled input creates
17038          * security vulnerabilities.)
17039          *
17040          * For the case of HTML, you might use a library, either on the client side, or on the server side,
17041          * to sanitize unsafe HTML before binding to the value and rendering it in the document.
17042          *
17043          * How would you ensure that every place that used these types of bindings was bound to a value that
17044          * was sanitized by your library (or returned as safe for rendering by your server?)  How can you
17045          * ensure that you didn't accidentally delete the line that sanitized the value, or renamed some
17046          * properties/fields and forgot to update the binding to the sanitized value?
17047          *
17048          * To be secure by default, you want to ensure that any such bindings are disallowed unless you can
17049          * determine that something explicitly says it's safe to use a value for binding in that
17050          * context.  You can then audit your code (a simple grep would do) to ensure that this is only done
17051          * for those values that you can easily tell are safe - because they were received from your server,
17052          * sanitized by your library, etc.  You can organize your codebase to help with this - perhaps
17053          * allowing only the files in a specific directory to do this.  Ensuring that the internal API
17054          * exposed by that code doesn't markup arbitrary values as safe then becomes a more manageable task.
17055          *
17056          * In the case of AngularJS' SCE service, one uses {@link ng.$sce#trustAs $sce.trustAs}
17057          * (and shorthand methods such as {@link ng.$sce#trustAsHtml $sce.trustAsHtml}, etc.) to
17058          * obtain values that will be accepted by SCE / privileged contexts.
17059          *
17060          *
17061          * ## How does it work?
17062          *
17063          * In privileged contexts, directives and code will bind to the result of {@link ng.$sce#getTrusted
17064          * $sce.getTrusted(context, value)} rather than to the value directly.  Directives use {@link
17065          * ng.$sce#parseAs $sce.parseAs} rather than `$parse` to watch attribute bindings, which performs the
17066          * {@link ng.$sce#getTrusted $sce.getTrusted} behind the scenes on non-constant literals.
17067          *
17068          * As an example, {@link ng.directive:ngBindHtml ngBindHtml} uses {@link
17069          * ng.$sce#parseAsHtml $sce.parseAsHtml(binding expression)}.  Here's the actual code (slightly
17070          * simplified):
17071          *
17072          * ```
17073          * var ngBindHtmlDirective = ['$sce', function($sce) {
17074          *   return function(scope, element, attr) {
17075          *     scope.$watch($sce.parseAsHtml(attr.ngBindHtml), function(value) {
17076          *       element.html(value || '');
17077          *     });
17078          *   };
17079          * }];
17080          * ```
17081          *
17082          * ## Impact on loading templates
17083          *
17084          * This applies both to the {@link ng.directive:ngInclude `ng-include`} directive as well as
17085          * `templateUrl`'s specified by {@link guide/directive directives}.
17086          *
17087          * By default, Angular only loads templates from the same domain and protocol as the application
17088          * document.  This is done by calling {@link ng.$sce#getTrustedResourceUrl
17089          * $sce.getTrustedResourceUrl} on the template URL.  To load templates from other domains and/or
17090          * protocols, you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist
17091          * them} or {@link ng.$sce#trustAsResourceUrl wrap it} into a trusted value.
17092          *
17093          * *Please note*:
17094          * The browser's
17095          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
17096          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
17097          * policy apply in addition to this and may further restrict whether the template is successfully
17098          * loaded.  This means that without the right CORS policy, loading templates from a different domain
17099          * won't work on all browsers.  Also, loading templates from `file://` URL does not work on some
17100          * browsers.
17101          *
17102          * ## This feels like too much overhead
17103          *
17104          * It's important to remember that SCE only applies to interpolation expressions.
17105          *
17106          * If your expressions are constant literals, they're automatically trusted and you don't need to
17107          * call `$sce.trustAs` on them (remember to include the `ngSanitize` module) (e.g.
17108          * `<div ng-bind-html="'<b>implicitly trusted</b>'"></div>`) just works.
17109          *
17110          * Additionally, `a[href]` and `img[src]` automatically sanitize their URLs and do not pass them
17111          * through {@link ng.$sce#getTrusted $sce.getTrusted}.  SCE doesn't play a role here.
17112          *
17113          * The included {@link ng.$sceDelegate $sceDelegate} comes with sane defaults to allow you to load
17114          * templates in `ng-include` from your application's domain without having to even know about SCE.
17115          * It blocks loading templates from other domains or loading templates over http from an https
17116          * served document.  You can change these by setting your own custom {@link
17117          * ng.$sceDelegateProvider#resourceUrlWhitelist whitelists} and {@link
17118          * ng.$sceDelegateProvider#resourceUrlBlacklist blacklists} for matching such URLs.
17119          *
17120          * This significantly reduces the overhead.  It is far easier to pay the small overhead and have an
17121          * application that's secure and can be audited to verify that with much more ease than bolting
17122          * security onto an application later.
17123          *
17124          * <a name="contexts"></a>
17125          * ## What trusted context types are supported?
17126          *
17127          * | Context             | Notes          |
17128          * |---------------------|----------------|
17129          * | `$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. |
17130          * | `$sce.CSS`          | For CSS that's safe to source into the application.  Currently unused.  Feel free to use it in your own directives. |
17131          * | `$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. |
17132          * | `$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. |
17133          * | `$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. |
17134          *
17135          * ## Format of items in {@link ng.$sceDelegateProvider#resourceUrlWhitelist resourceUrlWhitelist}/{@link ng.$sceDelegateProvider#resourceUrlBlacklist Blacklist} <a name="resourceUrlPatternItem"></a>
17136          *
17137          *  Each element in these arrays must be one of the following:
17138          *
17139          *  - **'self'**
17140          *    - The special **string**, `'self'`, can be used to match against all URLs of the **same
17141          *      domain** as the application document using the **same protocol**.
17142          *  - **String** (except the special value `'self'`)
17143          *    - The string is matched against the full *normalized / absolute URL* of the resource
17144          *      being tested (substring matches are not good enough.)
17145          *    - There are exactly **two wildcard sequences** - `*` and `**`.  All other characters
17146          *      match themselves.
17147          *    - `*`: matches zero or more occurrences of any character other than one of the following 6
17148          *      characters: '`:`', '`/`', '`.`', '`?`', '`&`' and '`;`'.  It's a useful wildcard for use
17149          *      in a whitelist.
17150          *    - `**`: matches zero or more occurrences of *any* character.  As such, it's not
17151          *      appropriate for use in a scheme, domain, etc. as it would match too much.  (e.g.
17152          *      http://**.example.com/ would match http://evil.com/?ignore=.example.com/ and that might
17153          *      not have been the intention.)  Its usage at the very end of the path is ok.  (e.g.
17154          *      http://foo.example.com/templates/**).
17155          *  - **RegExp** (*see caveat below*)
17156          *    - *Caveat*:  While regular expressions are powerful and offer great flexibility,  their syntax
17157          *      (and all the inevitable escaping) makes them *harder to maintain*.  It's easy to
17158          *      accidentally introduce a bug when one updates a complex expression (imho, all regexes should
17159          *      have good test coverage).  For instance, the use of `.` in the regex is correct only in a
17160          *      small number of cases.  A `.` character in the regex used when matching the scheme or a
17161          *      subdomain could be matched against a `:` or literal `.` that was likely not intended.   It
17162          *      is highly recommended to use the string patterns and only fall back to regular expressions
17163          *      as a last resort.
17164          *    - The regular expression must be an instance of RegExp (i.e. not a string.)  It is
17165          *      matched against the **entire** *normalized / absolute URL* of the resource being tested
17166          *      (even when the RegExp did not have the `^` and `$` codes.)  In addition, any flags
17167          *      present on the RegExp (such as multiline, global, ignoreCase) are ignored.
17168          *    - If you are generating your JavaScript from some other templating engine (not
17169          *      recommended, e.g. in issue [#4006](https://github.com/angular/angular.js/issues/4006)),
17170          *      remember to escape your regular expression (and be aware that you might need more than
17171          *      one level of escaping depending on your templating engine and the way you interpolated
17172          *      the value.)  Do make use of your platform's escaping mechanism as it might be good
17173          *      enough before coding your own.  E.g. Ruby has
17174          *      [Regexp.escape(str)](http://www.ruby-doc.org/core-2.0.0/Regexp.html#method-c-escape)
17175          *      and Python has [re.escape](http://docs.python.org/library/re.html#re.escape).
17176          *      Javascript lacks a similar built in function for escaping.  Take a look at Google
17177          *      Closure library's [goog.string.regExpEscape(s)](
17178          *      http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html#line962).
17179          *
17180          * Refer {@link ng.$sceDelegateProvider $sceDelegateProvider} for an example.
17181          *
17182          * ## Show me an example using SCE.
17183          *
17184          * <example module="mySceApp" deps="angular-sanitize.js">
17185          * <file name="index.html">
17186          *   <div ng-controller="AppController as myCtrl">
17187          *     <i ng-bind-html="myCtrl.explicitlyTrustedHtml" id="explicitlyTrustedHtml"></i><br><br>
17188          *     <b>User comments</b><br>
17189          *     By default, HTML that isn't explicitly trusted (e.g. Alice's comment) is sanitized when
17190          *     $sanitize is available.  If $sanitize isn't available, this results in an error instead of an
17191          *     exploit.
17192          *     <div class="well">
17193          *       <div ng-repeat="userComment in myCtrl.userComments">
17194          *         <b>{{userComment.name}}</b>:
17195          *         <span ng-bind-html="userComment.htmlComment" class="htmlComment"></span>
17196          *         <br>
17197          *       </div>
17198          *     </div>
17199          *   </div>
17200          * </file>
17201          *
17202          * <file name="script.js">
17203          *   angular.module('mySceApp', ['ngSanitize'])
17204          *     .controller('AppController', ['$http', '$templateCache', '$sce',
17205          *       function($http, $templateCache, $sce) {
17206          *         var self = this;
17207          *         $http.get("test_data.json", {cache: $templateCache}).success(function(userComments) {
17208          *           self.userComments = userComments;
17209          *         });
17210          *         self.explicitlyTrustedHtml = $sce.trustAsHtml(
17211          *             '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17212          *             'sanitization.&quot;">Hover over this text.</span>');
17213          *       }]);
17214          * </file>
17215          *
17216          * <file name="test_data.json">
17217          * [
17218          *   { "name": "Alice",
17219          *     "htmlComment":
17220          *         "<span onmouseover='this.textContent=\"PWN3D!\"'>Is <i>anyone</i> reading this?</span>"
17221          *   },
17222          *   { "name": "Bob",
17223          *     "htmlComment": "<i>Yes!</i>  Am I the only other one?"
17224          *   }
17225          * ]
17226          * </file>
17227          *
17228          * <file name="protractor.js" type="protractor">
17229          *   describe('SCE doc demo', function() {
17230          *     it('should sanitize untrusted values', function() {
17231          *       expect(element.all(by.css('.htmlComment')).first().getInnerHtml())
17232          *           .toBe('<span>Is <i>anyone</i> reading this?</span>');
17233          *     });
17234          *
17235          *     it('should NOT sanitize explicitly trusted values', function() {
17236          *       expect(element(by.id('explicitlyTrustedHtml')).getInnerHtml()).toBe(
17237          *           '<span onmouseover="this.textContent=&quot;Explicitly trusted HTML bypasses ' +
17238          *           'sanitization.&quot;">Hover over this text.</span>');
17239          *     });
17240          *   });
17241          * </file>
17242          * </example>
17243          *
17244          *
17245          *
17246          * ## Can I disable SCE completely?
17247          *
17248          * Yes, you can.  However, this is strongly discouraged.  SCE gives you a lot of security benefits
17249          * for little coding overhead.  It will be much harder to take an SCE disabled application and
17250          * either secure it on your own or enable SCE at a later stage.  It might make sense to disable SCE
17251          * for cases where you have a lot of existing code that was written before SCE was introduced and
17252          * you're migrating them a module at a time.
17253          *
17254          * That said, here's how you can completely disable SCE:
17255          *
17256          * ```
17257          * angular.module('myAppWithSceDisabledmyApp', []).config(function($sceProvider) {
17258          *   // Completely disable SCE.  For demonstration purposes only!
17259          *   // Do not use in new projects.
17260          *   $sceProvider.enabled(false);
17261          * });
17262          * ```
17263          *
17264          */
17265         /* jshint maxlen: 100 */
17266
17267         function $SceProvider() {
17268           var enabled = true;
17269
17270           /**
17271            * @ngdoc method
17272            * @name $sceProvider#enabled
17273            * @kind function
17274            *
17275            * @param {boolean=} value If provided, then enables/disables SCE.
17276            * @return {boolean} true if SCE is enabled, false otherwise.
17277            *
17278            * @description
17279            * Enables/disables SCE and returns the current value.
17280            */
17281           this.enabled = function(value) {
17282             if (arguments.length) {
17283               enabled = !!value;
17284             }
17285             return enabled;
17286           };
17287
17288
17289           /* Design notes on the default implementation for SCE.
17290            *
17291            * The API contract for the SCE delegate
17292            * -------------------------------------
17293            * The SCE delegate object must provide the following 3 methods:
17294            *
17295            * - trustAs(contextEnum, value)
17296            *     This method is used to tell the SCE service that the provided value is OK to use in the
17297            *     contexts specified by contextEnum.  It must return an object that will be accepted by
17298            *     getTrusted() for a compatible contextEnum and return this value.
17299            *
17300            * - valueOf(value)
17301            *     For values that were not produced by trustAs(), return them as is.  For values that were
17302            *     produced by trustAs(), return the corresponding input value to trustAs.  Basically, if
17303            *     trustAs is wrapping the given values into some type, this operation unwraps it when given
17304            *     such a value.
17305            *
17306            * - getTrusted(contextEnum, value)
17307            *     This function should return the a value that is safe to use in the context specified by
17308            *     contextEnum or throw and exception otherwise.
17309            *
17310            * NOTE: This contract deliberately does NOT state that values returned by trustAs() must be
17311            * opaque or wrapped in some holder object.  That happens to be an implementation detail.  For
17312            * instance, an implementation could maintain a registry of all trusted objects by context.  In
17313            * such a case, trustAs() would return the same object that was passed in.  getTrusted() would
17314            * return the same object passed in if it was found in the registry under a compatible context or
17315            * throw an exception otherwise.  An implementation might only wrap values some of the time based
17316            * on some criteria.  getTrusted() might return a value and not throw an exception for special
17317            * constants or objects even if not wrapped.  All such implementations fulfill this contract.
17318            *
17319            *
17320            * A note on the inheritance model for SCE contexts
17321            * ------------------------------------------------
17322            * I've used inheritance and made RESOURCE_URL wrapped types a subtype of URL wrapped types.  This
17323            * is purely an implementation details.
17324            *
17325            * The contract is simply this:
17326            *
17327            *     getTrusted($sce.RESOURCE_URL, value) succeeding implies that getTrusted($sce.URL, value)
17328            *     will also succeed.
17329            *
17330            * Inheritance happens to capture this in a natural way.  In some future, we
17331            * may not use inheritance anymore.  That is OK because no code outside of
17332            * sce.js and sceSpecs.js would need to be aware of this detail.
17333            */
17334
17335           this.$get = ['$parse', '$sceDelegate', function(
17336                         $parse,   $sceDelegate) {
17337             // Prereq: Ensure that we're not running in IE<11 quirks mode.  In that mode, IE < 11 allow
17338             // the "expression(javascript expression)" syntax which is insecure.
17339             if (enabled && msie < 8) {
17340               throw $sceMinErr('iequirks',
17341                 'Strict Contextual Escaping does not support Internet Explorer version < 11 in quirks ' +
17342                 'mode.  You can fix this by adding the text <!doctype html> to the top of your HTML ' +
17343                 'document.  See http://docs.angularjs.org/api/ng.$sce for more information.');
17344             }
17345
17346             var sce = shallowCopy(SCE_CONTEXTS);
17347
17348             /**
17349              * @ngdoc method
17350              * @name $sce#isEnabled
17351              * @kind function
17352              *
17353              * @return {Boolean} true if SCE is enabled, false otherwise.  If you want to set the value, you
17354              * have to do it at module config time on {@link ng.$sceProvider $sceProvider}.
17355              *
17356              * @description
17357              * Returns a boolean indicating if SCE is enabled.
17358              */
17359             sce.isEnabled = function() {
17360               return enabled;
17361             };
17362             sce.trustAs = $sceDelegate.trustAs;
17363             sce.getTrusted = $sceDelegate.getTrusted;
17364             sce.valueOf = $sceDelegate.valueOf;
17365
17366             if (!enabled) {
17367               sce.trustAs = sce.getTrusted = function(type, value) { return value; };
17368               sce.valueOf = identity;
17369             }
17370
17371             /**
17372              * @ngdoc method
17373              * @name $sce#parseAs
17374              *
17375              * @description
17376              * Converts Angular {@link guide/expression expression} into a function.  This is like {@link
17377              * ng.$parse $parse} and is identical when the expression is a literal constant.  Otherwise, it
17378              * wraps the expression in a call to {@link ng.$sce#getTrusted $sce.getTrusted(*type*,
17379              * *result*)}
17380              *
17381              * @param {string} type The kind of SCE context in which this result will be used.
17382              * @param {string} expression String expression to compile.
17383              * @returns {function(context, locals)} a function which represents the compiled expression:
17384              *
17385              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17386              *      are evaluated against (typically a scope object).
17387              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17388              *      `context`.
17389              */
17390             sce.parseAs = function sceParseAs(type, expr) {
17391               var parsed = $parse(expr);
17392               if (parsed.literal && parsed.constant) {
17393                 return parsed;
17394               } else {
17395                 return $parse(expr, function(value) {
17396                   return sce.getTrusted(type, value);
17397                 });
17398               }
17399             };
17400
17401             /**
17402              * @ngdoc method
17403              * @name $sce#trustAs
17404              *
17405              * @description
17406              * Delegates to {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs`}.  As such,
17407              * returns an object that is trusted by angular for use in specified strict contextual
17408              * escaping contexts (such as ng-bind-html, ng-include, any src attribute
17409              * interpolation, any dom event binding attribute interpolation such as for onclick,  etc.)
17410              * that uses the provided value.  See * {@link ng.$sce $sce} for enabling strict contextual
17411              * escaping.
17412              *
17413              * @param {string} type The kind of context in which this value is safe for use.  e.g. url,
17414              *   resourceUrl, html, js and css.
17415              * @param {*} value The value that that should be considered trusted/safe.
17416              * @returns {*} A value that can be used to stand in for the provided `value` in places
17417              * where Angular expects a $sce.trustAs() return value.
17418              */
17419
17420             /**
17421              * @ngdoc method
17422              * @name $sce#trustAsHtml
17423              *
17424              * @description
17425              * Shorthand method.  `$sce.trustAsHtml(value)` →
17426              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.HTML, value)`}
17427              *
17428              * @param {*} value The value to trustAs.
17429              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedHtml
17430              *     $sce.getTrustedHtml(value)} to obtain the original value.  (privileged directives
17431              *     only accept expressions that are either literal constants or are the
17432              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17433              */
17434
17435             /**
17436              * @ngdoc method
17437              * @name $sce#trustAsUrl
17438              *
17439              * @description
17440              * Shorthand method.  `$sce.trustAsUrl(value)` →
17441              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.URL, value)`}
17442              *
17443              * @param {*} value The value to trustAs.
17444              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedUrl
17445              *     $sce.getTrustedUrl(value)} to obtain the original value.  (privileged directives
17446              *     only accept expressions that are either literal constants or are the
17447              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17448              */
17449
17450             /**
17451              * @ngdoc method
17452              * @name $sce#trustAsResourceUrl
17453              *
17454              * @description
17455              * Shorthand method.  `$sce.trustAsResourceUrl(value)` →
17456              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.RESOURCE_URL, value)`}
17457              *
17458              * @param {*} value The value to trustAs.
17459              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedResourceUrl
17460              *     $sce.getTrustedResourceUrl(value)} to obtain the original value.  (privileged directives
17461              *     only accept expressions that are either literal constants or are the return
17462              *     value of {@link ng.$sce#trustAs $sce.trustAs}.)
17463              */
17464
17465             /**
17466              * @ngdoc method
17467              * @name $sce#trustAsJs
17468              *
17469              * @description
17470              * Shorthand method.  `$sce.trustAsJs(value)` →
17471              *     {@link ng.$sceDelegate#trustAs `$sceDelegate.trustAs($sce.JS, value)`}
17472              *
17473              * @param {*} value The value to trustAs.
17474              * @returns {*} An object that can be passed to {@link ng.$sce#getTrustedJs
17475              *     $sce.getTrustedJs(value)} to obtain the original value.  (privileged directives
17476              *     only accept expressions that are either literal constants or are the
17477              *     return value of {@link ng.$sce#trustAs $sce.trustAs}.)
17478              */
17479
17480             /**
17481              * @ngdoc method
17482              * @name $sce#getTrusted
17483              *
17484              * @description
17485              * Delegates to {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted`}.  As such,
17486              * takes the result of a {@link ng.$sce#trustAs `$sce.trustAs`}() call and returns the
17487              * originally supplied value if the queried context type is a supertype of the created type.
17488              * If this condition isn't satisfied, throws an exception.
17489              *
17490              * @param {string} type The kind of context in which this value is to be used.
17491              * @param {*} maybeTrusted The result of a prior {@link ng.$sce#trustAs `$sce.trustAs`}
17492              *                         call.
17493              * @returns {*} The value the was originally provided to
17494              *              {@link ng.$sce#trustAs `$sce.trustAs`} if valid in this context.
17495              *              Otherwise, throws an exception.
17496              */
17497
17498             /**
17499              * @ngdoc method
17500              * @name $sce#getTrustedHtml
17501              *
17502              * @description
17503              * Shorthand method.  `$sce.getTrustedHtml(value)` →
17504              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.HTML, value)`}
17505              *
17506              * @param {*} value The value to pass to `$sce.getTrusted`.
17507              * @returns {*} The return value of `$sce.getTrusted($sce.HTML, value)`
17508              */
17509
17510             /**
17511              * @ngdoc method
17512              * @name $sce#getTrustedCss
17513              *
17514              * @description
17515              * Shorthand method.  `$sce.getTrustedCss(value)` →
17516              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.CSS, value)`}
17517              *
17518              * @param {*} value The value to pass to `$sce.getTrusted`.
17519              * @returns {*} The return value of `$sce.getTrusted($sce.CSS, value)`
17520              */
17521
17522             /**
17523              * @ngdoc method
17524              * @name $sce#getTrustedUrl
17525              *
17526              * @description
17527              * Shorthand method.  `$sce.getTrustedUrl(value)` →
17528              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.URL, value)`}
17529              *
17530              * @param {*} value The value to pass to `$sce.getTrusted`.
17531              * @returns {*} The return value of `$sce.getTrusted($sce.URL, value)`
17532              */
17533
17534             /**
17535              * @ngdoc method
17536              * @name $sce#getTrustedResourceUrl
17537              *
17538              * @description
17539              * Shorthand method.  `$sce.getTrustedResourceUrl(value)` →
17540              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.RESOURCE_URL, value)`}
17541              *
17542              * @param {*} value The value to pass to `$sceDelegate.getTrusted`.
17543              * @returns {*} The return value of `$sce.getTrusted($sce.RESOURCE_URL, value)`
17544              */
17545
17546             /**
17547              * @ngdoc method
17548              * @name $sce#getTrustedJs
17549              *
17550              * @description
17551              * Shorthand method.  `$sce.getTrustedJs(value)` →
17552              *     {@link ng.$sceDelegate#getTrusted `$sceDelegate.getTrusted($sce.JS, value)`}
17553              *
17554              * @param {*} value The value to pass to `$sce.getTrusted`.
17555              * @returns {*} The return value of `$sce.getTrusted($sce.JS, value)`
17556              */
17557
17558             /**
17559              * @ngdoc method
17560              * @name $sce#parseAsHtml
17561              *
17562              * @description
17563              * Shorthand method.  `$sce.parseAsHtml(expression string)` →
17564              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.HTML, value)`}
17565              *
17566              * @param {string} expression String expression to compile.
17567              * @returns {function(context, locals)} a function which represents the compiled expression:
17568              *
17569              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17570              *      are evaluated against (typically a scope object).
17571              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17572              *      `context`.
17573              */
17574
17575             /**
17576              * @ngdoc method
17577              * @name $sce#parseAsCss
17578              *
17579              * @description
17580              * Shorthand method.  `$sce.parseAsCss(value)` →
17581              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.CSS, value)`}
17582              *
17583              * @param {string} expression String expression to compile.
17584              * @returns {function(context, locals)} a function which represents the compiled expression:
17585              *
17586              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17587              *      are evaluated against (typically a scope object).
17588              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17589              *      `context`.
17590              */
17591
17592             /**
17593              * @ngdoc method
17594              * @name $sce#parseAsUrl
17595              *
17596              * @description
17597              * Shorthand method.  `$sce.parseAsUrl(value)` →
17598              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.URL, value)`}
17599              *
17600              * @param {string} expression String expression to compile.
17601              * @returns {function(context, locals)} a function which represents the compiled expression:
17602              *
17603              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17604              *      are evaluated against (typically a scope object).
17605              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17606              *      `context`.
17607              */
17608
17609             /**
17610              * @ngdoc method
17611              * @name $sce#parseAsResourceUrl
17612              *
17613              * @description
17614              * Shorthand method.  `$sce.parseAsResourceUrl(value)` →
17615              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.RESOURCE_URL, value)`}
17616              *
17617              * @param {string} expression String expression to compile.
17618              * @returns {function(context, locals)} a function which represents the compiled expression:
17619              *
17620              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17621              *      are evaluated against (typically a scope object).
17622              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17623              *      `context`.
17624              */
17625
17626             /**
17627              * @ngdoc method
17628              * @name $sce#parseAsJs
17629              *
17630              * @description
17631              * Shorthand method.  `$sce.parseAsJs(value)` →
17632              *     {@link ng.$sce#parseAs `$sce.parseAs($sce.JS, value)`}
17633              *
17634              * @param {string} expression String expression to compile.
17635              * @returns {function(context, locals)} a function which represents the compiled expression:
17636              *
17637              *    * `context` – `{object}` – an object against which any expressions embedded in the strings
17638              *      are evaluated against (typically a scope object).
17639              *    * `locals` – `{object=}` – local variables context object, useful for overriding values in
17640              *      `context`.
17641              */
17642
17643             // Shorthand delegations.
17644             var parse = sce.parseAs,
17645                 getTrusted = sce.getTrusted,
17646                 trustAs = sce.trustAs;
17647
17648             forEach(SCE_CONTEXTS, function(enumValue, name) {
17649               var lName = lowercase(name);
17650               sce[camelCase("parse_as_" + lName)] = function(expr) {
17651                 return parse(enumValue, expr);
17652               };
17653               sce[camelCase("get_trusted_" + lName)] = function(value) {
17654                 return getTrusted(enumValue, value);
17655               };
17656               sce[camelCase("trust_as_" + lName)] = function(value) {
17657                 return trustAs(enumValue, value);
17658               };
17659             });
17660
17661             return sce;
17662           }];
17663         }
17664
17665         /**
17666          * !!! This is an undocumented "private" service !!!
17667          *
17668          * @name $sniffer
17669          * @requires $window
17670          * @requires $document
17671          *
17672          * @property {boolean} history Does the browser support html5 history api ?
17673          * @property {boolean} transitions Does the browser support CSS transition events ?
17674          * @property {boolean} animations Does the browser support CSS animation events ?
17675          *
17676          * @description
17677          * This is very simple implementation of testing browser's features.
17678          */
17679         function $SnifferProvider() {
17680           this.$get = ['$window', '$document', function($window, $document) {
17681             var eventSupport = {},
17682                 android =
17683                   toInt((/android (\d+)/.exec(lowercase(($window.navigator || {}).userAgent)) || [])[1]),
17684                 boxee = /Boxee/i.test(($window.navigator || {}).userAgent),
17685                 document = $document[0] || {},
17686                 vendorPrefix,
17687                 vendorRegex = /^(Moz|webkit|ms)(?=[A-Z])/,
17688                 bodyStyle = document.body && document.body.style,
17689                 transitions = false,
17690                 animations = false,
17691                 match;
17692
17693             if (bodyStyle) {
17694               for (var prop in bodyStyle) {
17695                 if (match = vendorRegex.exec(prop)) {
17696                   vendorPrefix = match[0];
17697                   vendorPrefix = vendorPrefix.substr(0, 1).toUpperCase() + vendorPrefix.substr(1);
17698                   break;
17699                 }
17700               }
17701
17702               if (!vendorPrefix) {
17703                 vendorPrefix = ('WebkitOpacity' in bodyStyle) && 'webkit';
17704               }
17705
17706               transitions = !!(('transition' in bodyStyle) || (vendorPrefix + 'Transition' in bodyStyle));
17707               animations  = !!(('animation' in bodyStyle) || (vendorPrefix + 'Animation' in bodyStyle));
17708
17709               if (android && (!transitions ||  !animations)) {
17710                 transitions = isString(bodyStyle.webkitTransition);
17711                 animations = isString(bodyStyle.webkitAnimation);
17712               }
17713             }
17714
17715
17716             return {
17717               // Android has history.pushState, but it does not update location correctly
17718               // so let's not use the history API at all.
17719               // http://code.google.com/p/android/issues/detail?id=17471
17720               // https://github.com/angular/angular.js/issues/904
17721
17722               // older webkit browser (533.9) on Boxee box has exactly the same problem as Android has
17723               // so let's not use the history API also
17724               // We are purposefully using `!(android < 4)` to cover the case when `android` is undefined
17725               // jshint -W018
17726               history: !!($window.history && $window.history.pushState && !(android < 4) && !boxee),
17727               // jshint +W018
17728               hasEvent: function(event) {
17729                 // IE9 implements 'input' event it's so fubared that we rather pretend that it doesn't have
17730                 // it. In particular the event is not fired when backspace or delete key are pressed or
17731                 // when cut operation is performed.
17732                 // IE10+ implements 'input' event but it erroneously fires under various situations,
17733                 // e.g. when placeholder changes, or a form is focused.
17734                 if (event === 'input' && msie <= 11) return false;
17735
17736                 if (isUndefined(eventSupport[event])) {
17737                   var divElm = document.createElement('div');
17738                   eventSupport[event] = 'on' + event in divElm;
17739                 }
17740
17741                 return eventSupport[event];
17742               },
17743               csp: csp(),
17744               vendorPrefix: vendorPrefix,
17745               transitions: transitions,
17746               animations: animations,
17747               android: android
17748             };
17749           }];
17750         }
17751
17752         var $compileMinErr = minErr('$compile');
17753
17754         /**
17755          * @ngdoc service
17756          * @name $templateRequest
17757          *
17758          * @description
17759          * The `$templateRequest` service runs security checks then downloads the provided template using
17760          * `$http` and, upon success, stores the contents inside of `$templateCache`. If the HTTP request
17761          * fails or the response data of the HTTP request is empty, a `$compile` error will be thrown (the
17762          * exception can be thwarted by setting the 2nd parameter of the function to true). Note that the
17763          * contents of `$templateCache` are trusted, so the call to `$sce.getTrustedUrl(tpl)` is omitted
17764          * when `tpl` is of type string and `$templateCache` has the matching entry.
17765          *
17766          * @param {string|TrustedResourceUrl} tpl The HTTP request template URL
17767          * @param {boolean=} ignoreRequestError Whether or not to ignore the exception when the request fails or the template is empty
17768          *
17769          * @return {Promise} a promise for the HTTP response data of the given URL.
17770          *
17771          * @property {number} totalPendingRequests total amount of pending template requests being downloaded.
17772          */
17773         function $TemplateRequestProvider() {
17774           this.$get = ['$templateCache', '$http', '$q', '$sce', function($templateCache, $http, $q, $sce) {
17775             function handleRequestFn(tpl, ignoreRequestError) {
17776               handleRequestFn.totalPendingRequests++;
17777
17778               // We consider the template cache holds only trusted templates, so
17779               // there's no need to go through whitelisting again for keys that already
17780               // are included in there. This also makes Angular accept any script
17781               // directive, no matter its name. However, we still need to unwrap trusted
17782               // types.
17783               if (!isString(tpl) || !$templateCache.get(tpl)) {
17784                 tpl = $sce.getTrustedResourceUrl(tpl);
17785               }
17786
17787               var transformResponse = $http.defaults && $http.defaults.transformResponse;
17788
17789               if (isArray(transformResponse)) {
17790                 transformResponse = transformResponse.filter(function(transformer) {
17791                   return transformer !== defaultHttpResponseTransform;
17792                 });
17793               } else if (transformResponse === defaultHttpResponseTransform) {
17794                 transformResponse = null;
17795               }
17796
17797               var httpOptions = {
17798                 cache: $templateCache,
17799                 transformResponse: transformResponse
17800               };
17801
17802               return $http.get(tpl, httpOptions)
17803                 ['finally'](function() {
17804                   handleRequestFn.totalPendingRequests--;
17805                 })
17806                 .then(function(response) {
17807                   $templateCache.put(tpl, response.data);
17808                   return response.data;
17809                 }, handleError);
17810
17811               function handleError(resp) {
17812                 if (!ignoreRequestError) {
17813                   throw $compileMinErr('tpload', 'Failed to load template: {0} (HTTP status: {1} {2})',
17814                     tpl, resp.status, resp.statusText);
17815                 }
17816                 return $q.reject(resp);
17817               }
17818             }
17819
17820             handleRequestFn.totalPendingRequests = 0;
17821
17822             return handleRequestFn;
17823           }];
17824         }
17825
17826         function $$TestabilityProvider() {
17827           this.$get = ['$rootScope', '$browser', '$location',
17828                function($rootScope,   $browser,   $location) {
17829
17830             /**
17831              * @name $testability
17832              *
17833              * @description
17834              * The private $$testability service provides a collection of methods for use when debugging
17835              * or by automated test and debugging tools.
17836              */
17837             var testability = {};
17838
17839             /**
17840              * @name $$testability#findBindings
17841              *
17842              * @description
17843              * Returns an array of elements that are bound (via ng-bind or {{}})
17844              * to expressions matching the input.
17845              *
17846              * @param {Element} element The element root to search from.
17847              * @param {string} expression The binding expression to match.
17848              * @param {boolean} opt_exactMatch If true, only returns exact matches
17849              *     for the expression. Filters and whitespace are ignored.
17850              */
17851             testability.findBindings = function(element, expression, opt_exactMatch) {
17852               var bindings = element.getElementsByClassName('ng-binding');
17853               var matches = [];
17854               forEach(bindings, function(binding) {
17855                 var dataBinding = angular.element(binding).data('$binding');
17856                 if (dataBinding) {
17857                   forEach(dataBinding, function(bindingName) {
17858                     if (opt_exactMatch) {
17859                       var matcher = new RegExp('(^|\\s)' + escapeForRegexp(expression) + '(\\s|\\||$)');
17860                       if (matcher.test(bindingName)) {
17861                         matches.push(binding);
17862                       }
17863                     } else {
17864                       if (bindingName.indexOf(expression) != -1) {
17865                         matches.push(binding);
17866                       }
17867                     }
17868                   });
17869                 }
17870               });
17871               return matches;
17872             };
17873
17874             /**
17875              * @name $$testability#findModels
17876              *
17877              * @description
17878              * Returns an array of elements that are two-way found via ng-model to
17879              * expressions matching the input.
17880              *
17881              * @param {Element} element The element root to search from.
17882              * @param {string} expression The model expression to match.
17883              * @param {boolean} opt_exactMatch If true, only returns exact matches
17884              *     for the expression.
17885              */
17886             testability.findModels = function(element, expression, opt_exactMatch) {
17887               var prefixes = ['ng-', 'data-ng-', 'ng\\:'];
17888               for (var p = 0; p < prefixes.length; ++p) {
17889                 var attributeEquals = opt_exactMatch ? '=' : '*=';
17890                 var selector = '[' + prefixes[p] + 'model' + attributeEquals + '"' + expression + '"]';
17891                 var elements = element.querySelectorAll(selector);
17892                 if (elements.length) {
17893                   return elements;
17894                 }
17895               }
17896             };
17897
17898             /**
17899              * @name $$testability#getLocation
17900              *
17901              * @description
17902              * Shortcut for getting the location in a browser agnostic way. Returns
17903              *     the path, search, and hash. (e.g. /path?a=b#hash)
17904              */
17905             testability.getLocation = function() {
17906               return $location.url();
17907             };
17908
17909             /**
17910              * @name $$testability#setLocation
17911              *
17912              * @description
17913              * Shortcut for navigating to a location without doing a full page reload.
17914              *
17915              * @param {string} url The location url (path, search and hash,
17916              *     e.g. /path?a=b#hash) to go to.
17917              */
17918             testability.setLocation = function(url) {
17919               if (url !== $location.url()) {
17920                 $location.url(url);
17921                 $rootScope.$digest();
17922               }
17923             };
17924
17925             /**
17926              * @name $$testability#whenStable
17927              *
17928              * @description
17929              * Calls the callback when $timeout and $http requests are completed.
17930              *
17931              * @param {function} callback
17932              */
17933             testability.whenStable = function(callback) {
17934               $browser.notifyWhenNoOutstandingRequests(callback);
17935             };
17936
17937             return testability;
17938           }];
17939         }
17940
17941         function $TimeoutProvider() {
17942           this.$get = ['$rootScope', '$browser', '$q', '$$q', '$exceptionHandler',
17943                function($rootScope,   $browser,   $q,   $$q,   $exceptionHandler) {
17944
17945             var deferreds = {};
17946
17947
17948              /**
17949               * @ngdoc service
17950               * @name $timeout
17951               *
17952               * @description
17953               * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
17954               * block and delegates any exceptions to
17955               * {@link ng.$exceptionHandler $exceptionHandler} service.
17956               *
17957               * The return value of calling `$timeout` is a promise, which will be resolved when
17958               * the delay has passed and the timeout function, if provided, is executed.
17959               *
17960               * To cancel a timeout request, call `$timeout.cancel(promise)`.
17961               *
17962               * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
17963               * synchronously flush the queue of deferred functions.
17964               *
17965               * If you only want a promise that will be resolved after some specified delay
17966               * then you can call `$timeout` without the `fn` function.
17967               *
17968               * @param {function()=} fn A function, whose execution should be delayed.
17969               * @param {number=} [delay=0] Delay in milliseconds.
17970               * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
17971               *   will invoke `fn` within the {@link ng.$rootScope.Scope#$apply $apply} block.
17972               * @param {...*=} Pass additional parameters to the executed function.
17973               * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
17974               *   promise will be resolved with is the return value of the `fn` function.
17975               *
17976               */
17977             function timeout(fn, delay, invokeApply) {
17978               if (!isFunction(fn)) {
17979                 invokeApply = delay;
17980                 delay = fn;
17981                 fn = noop;
17982               }
17983
17984               var args = sliceArgs(arguments, 3),
17985                   skipApply = (isDefined(invokeApply) && !invokeApply),
17986                   deferred = (skipApply ? $$q : $q).defer(),
17987                   promise = deferred.promise,
17988                   timeoutId;
17989
17990               timeoutId = $browser.defer(function() {
17991                 try {
17992                   deferred.resolve(fn.apply(null, args));
17993                 } catch (e) {
17994                   deferred.reject(e);
17995                   $exceptionHandler(e);
17996                 }
17997                 finally {
17998                   delete deferreds[promise.$$timeoutId];
17999                 }
18000
18001                 if (!skipApply) $rootScope.$apply();
18002               }, delay);
18003
18004               promise.$$timeoutId = timeoutId;
18005               deferreds[timeoutId] = deferred;
18006
18007               return promise;
18008             }
18009
18010
18011              /**
18012               * @ngdoc method
18013               * @name $timeout#cancel
18014               *
18015               * @description
18016               * Cancels a task associated with the `promise`. As a result of this, the promise will be
18017               * resolved with a rejection.
18018               *
18019               * @param {Promise=} promise Promise returned by the `$timeout` function.
18020               * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
18021               *   canceled.
18022               */
18023             timeout.cancel = function(promise) {
18024               if (promise && promise.$$timeoutId in deferreds) {
18025                 deferreds[promise.$$timeoutId].reject('canceled');
18026                 delete deferreds[promise.$$timeoutId];
18027                 return $browser.defer.cancel(promise.$$timeoutId);
18028               }
18029               return false;
18030             };
18031
18032             return timeout;
18033           }];
18034         }
18035
18036         // NOTE:  The usage of window and document instead of $window and $document here is
18037         // deliberate.  This service depends on the specific behavior of anchor nodes created by the
18038         // browser (resolving and parsing URLs) that is unlikely to be provided by mock objects and
18039         // cause us to break tests.  In addition, when the browser resolves a URL for XHR, it
18040         // doesn't know about mocked locations and resolves URLs to the real document - which is
18041         // exactly the behavior needed here.  There is little value is mocking these out for this
18042         // service.
18043         var urlParsingNode = document.createElement("a");
18044         var originUrl = urlResolve(window.location.href);
18045
18046
18047         /**
18048          *
18049          * Implementation Notes for non-IE browsers
18050          * ----------------------------------------
18051          * Assigning a URL to the href property of an anchor DOM node, even one attached to the DOM,
18052          * results both in the normalizing and parsing of the URL.  Normalizing means that a relative
18053          * URL will be resolved into an absolute URL in the context of the application document.
18054          * Parsing means that the anchor node's host, hostname, protocol, port, pathname and related
18055          * properties are all populated to reflect the normalized URL.  This approach has wide
18056          * compatibility - Safari 1+, Mozilla 1+, Opera 7+,e etc.  See
18057          * http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18058          *
18059          * Implementation Notes for IE
18060          * ---------------------------
18061          * IE <= 10 normalizes the URL when assigned to the anchor node similar to the other
18062          * browsers.  However, the parsed components will not be set if the URL assigned did not specify
18063          * them.  (e.g. if you assign a.href = "foo", then a.protocol, a.host, etc. will be empty.)  We
18064          * work around that by performing the parsing in a 2nd step by taking a previously normalized
18065          * URL (e.g. by assigning to a.href) and assigning it a.href again.  This correctly populates the
18066          * properties such as protocol, hostname, port, etc.
18067          *
18068          * References:
18069          *   http://developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement
18070          *   http://www.aptana.com/reference/html/api/HTMLAnchorElement.html
18071          *   http://url.spec.whatwg.org/#urlutils
18072          *   https://github.com/angular/angular.js/pull/2902
18073          *   http://james.padolsey.com/javascript/parsing-urls-with-the-dom/
18074          *
18075          * @kind function
18076          * @param {string} url The URL to be parsed.
18077          * @description Normalizes and parses a URL.
18078          * @returns {object} Returns the normalized URL as a dictionary.
18079          *
18080          *   | member name   | Description    |
18081          *   |---------------|----------------|
18082          *   | href          | A normalized version of the provided URL if it was not an absolute URL |
18083          *   | protocol      | The protocol including the trailing colon                              |
18084          *   | host          | The host and port (if the port is non-default) of the normalizedUrl    |
18085          *   | search        | The search params, minus the question mark                             |
18086          *   | hash          | The hash string, minus the hash symbol
18087          *   | hostname      | The hostname
18088          *   | port          | The port, without ":"
18089          *   | pathname      | The pathname, beginning with "/"
18090          *
18091          */
18092         function urlResolve(url) {
18093           var href = url;
18094
18095           if (msie) {
18096             // Normalize before parse.  Refer Implementation Notes on why this is
18097             // done in two steps on IE.
18098             urlParsingNode.setAttribute("href", href);
18099             href = urlParsingNode.href;
18100           }
18101
18102           urlParsingNode.setAttribute('href', href);
18103
18104           // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils
18105           return {
18106             href: urlParsingNode.href,
18107             protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',
18108             host: urlParsingNode.host,
18109             search: urlParsingNode.search ? urlParsingNode.search.replace(/^\?/, '') : '',
18110             hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',
18111             hostname: urlParsingNode.hostname,
18112             port: urlParsingNode.port,
18113             pathname: (urlParsingNode.pathname.charAt(0) === '/')
18114               ? urlParsingNode.pathname
18115               : '/' + urlParsingNode.pathname
18116           };
18117         }
18118
18119         /**
18120          * Parse a request URL and determine whether this is a same-origin request as the application document.
18121          *
18122          * @param {string|object} requestUrl The url of the request as a string that will be resolved
18123          * or a parsed URL object.
18124          * @returns {boolean} Whether the request is for the same origin as the application document.
18125          */
18126         function urlIsSameOrigin(requestUrl) {
18127           var parsed = (isString(requestUrl)) ? urlResolve(requestUrl) : requestUrl;
18128           return (parsed.protocol === originUrl.protocol &&
18129                   parsed.host === originUrl.host);
18130         }
18131
18132         /**
18133          * @ngdoc service
18134          * @name $window
18135          *
18136          * @description
18137          * A reference to the browser's `window` object. While `window`
18138          * is globally available in JavaScript, it causes testability problems, because
18139          * it is a global variable. In angular we always refer to it through the
18140          * `$window` service, so it may be overridden, removed or mocked for testing.
18141          *
18142          * Expressions, like the one defined for the `ngClick` directive in the example
18143          * below, are evaluated with respect to the current scope.  Therefore, there is
18144          * no risk of inadvertently coding in a dependency on a global value in such an
18145          * expression.
18146          *
18147          * @example
18148            <example module="windowExample">
18149              <file name="index.html">
18150                <script>
18151                  angular.module('windowExample', [])
18152                    .controller('ExampleController', ['$scope', '$window', function($scope, $window) {
18153                      $scope.greeting = 'Hello, World!';
18154                      $scope.doGreeting = function(greeting) {
18155                        $window.alert(greeting);
18156                      };
18157                    }]);
18158                </script>
18159                <div ng-controller="ExampleController">
18160                  <input type="text" ng-model="greeting" aria-label="greeting" />
18161                  <button ng-click="doGreeting(greeting)">ALERT</button>
18162                </div>
18163              </file>
18164              <file name="protractor.js" type="protractor">
18165               it('should display the greeting in the input box', function() {
18166                element(by.model('greeting')).sendKeys('Hello, E2E Tests');
18167                // If we click the button it will block the test runner
18168                // element(':button').click();
18169               });
18170              </file>
18171            </example>
18172          */
18173         function $WindowProvider() {
18174           this.$get = valueFn(window);
18175         }
18176
18177         /**
18178          * @name $$cookieReader
18179          * @requires $document
18180          *
18181          * @description
18182          * This is a private service for reading cookies used by $http and ngCookies
18183          *
18184          * @return {Object} a key/value map of the current cookies
18185          */
18186         function $$CookieReader($document) {
18187           var rawDocument = $document[0] || {};
18188           var lastCookies = {};
18189           var lastCookieString = '';
18190
18191           function safeDecodeURIComponent(str) {
18192             try {
18193               return decodeURIComponent(str);
18194             } catch (e) {
18195               return str;
18196             }
18197           }
18198
18199           return function() {
18200             var cookieArray, cookie, i, index, name;
18201             var currentCookieString = rawDocument.cookie || '';
18202
18203             if (currentCookieString !== lastCookieString) {
18204               lastCookieString = currentCookieString;
18205               cookieArray = lastCookieString.split('; ');
18206               lastCookies = {};
18207
18208               for (i = 0; i < cookieArray.length; i++) {
18209                 cookie = cookieArray[i];
18210                 index = cookie.indexOf('=');
18211                 if (index > 0) { //ignore nameless cookies
18212                   name = safeDecodeURIComponent(cookie.substring(0, index));
18213                   // the first value that is seen for a cookie is the most
18214                   // specific one.  values for the same cookie name that
18215                   // follow are for less specific paths.
18216                   if (isUndefined(lastCookies[name])) {
18217                     lastCookies[name] = safeDecodeURIComponent(cookie.substring(index + 1));
18218                   }
18219                 }
18220               }
18221             }
18222             return lastCookies;
18223           };
18224         }
18225
18226         $$CookieReader.$inject = ['$document'];
18227
18228         function $$CookieReaderProvider() {
18229           this.$get = $$CookieReader;
18230         }
18231
18232         /* global currencyFilter: true,
18233          dateFilter: true,
18234          filterFilter: true,
18235          jsonFilter: true,
18236          limitToFilter: true,
18237          lowercaseFilter: true,
18238          numberFilter: true,
18239          orderByFilter: true,
18240          uppercaseFilter: true,
18241          */
18242
18243         /**
18244          * @ngdoc provider
18245          * @name $filterProvider
18246          * @description
18247          *
18248          * Filters are just functions which transform input to an output. However filters need to be
18249          * Dependency Injected. To achieve this a filter definition consists of a factory function which is
18250          * annotated with dependencies and is responsible for creating a filter function.
18251          *
18252          * <div class="alert alert-warning">
18253          * **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18254          * Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18255          * your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18256          * (`myapp_subsection_filterx`).
18257          * </div>
18258          *
18259          * ```js
18260          *   // Filter registration
18261          *   function MyModule($provide, $filterProvider) {
18262          *     // create a service to demonstrate injection (not always needed)
18263          *     $provide.value('greet', function(name){
18264          *       return 'Hello ' + name + '!';
18265          *     });
18266          *
18267          *     // register a filter factory which uses the
18268          *     // greet service to demonstrate DI.
18269          *     $filterProvider.register('greet', function(greet){
18270          *       // return the filter function which uses the greet service
18271          *       // to generate salutation
18272          *       return function(text) {
18273          *         // filters need to be forgiving so check input validity
18274          *         return text && greet(text) || text;
18275          *       };
18276          *     });
18277          *   }
18278          * ```
18279          *
18280          * The filter function is registered with the `$injector` under the filter name suffix with
18281          * `Filter`.
18282          *
18283          * ```js
18284          *   it('should be the same instance', inject(
18285          *     function($filterProvider) {
18286          *       $filterProvider.register('reverse', function(){
18287          *         return ...;
18288          *       });
18289          *     },
18290          *     function($filter, reverseFilter) {
18291          *       expect($filter('reverse')).toBe(reverseFilter);
18292          *     });
18293          * ```
18294          *
18295          *
18296          * For more information about how angular filters work, and how to create your own filters, see
18297          * {@link guide/filter Filters} in the Angular Developer Guide.
18298          */
18299
18300         /**
18301          * @ngdoc service
18302          * @name $filter
18303          * @kind function
18304          * @description
18305          * Filters are used for formatting data displayed to the user.
18306          *
18307          * The general syntax in templates is as follows:
18308          *
18309          *         {{ expression [| filter_name[:parameter_value] ... ] }}
18310          *
18311          * @param {String} name Name of the filter function to retrieve
18312          * @return {Function} the filter function
18313          * @example
18314            <example name="$filter" module="filterExample">
18315              <file name="index.html">
18316                <div ng-controller="MainCtrl">
18317                 <h3>{{ originalText }}</h3>
18318                 <h3>{{ filteredText }}</h3>
18319                </div>
18320              </file>
18321
18322              <file name="script.js">
18323               angular.module('filterExample', [])
18324               .controller('MainCtrl', function($scope, $filter) {
18325                 $scope.originalText = 'hello';
18326                 $scope.filteredText = $filter('uppercase')($scope.originalText);
18327               });
18328              </file>
18329            </example>
18330           */
18331         $FilterProvider.$inject = ['$provide'];
18332         function $FilterProvider($provide) {
18333           var suffix = 'Filter';
18334
18335           /**
18336            * @ngdoc method
18337            * @name $filterProvider#register
18338            * @param {string|Object} name Name of the filter function, or an object map of filters where
18339            *    the keys are the filter names and the values are the filter factories.
18340            *
18341            *    <div class="alert alert-warning">
18342            *    **Note:** Filter names must be valid angular {@link expression} identifiers, such as `uppercase` or `orderBy`.
18343            *    Names with special characters, such as hyphens and dots, are not allowed. If you wish to namespace
18344            *    your filters, then you can use capitalization (`myappSubsectionFilterx`) or underscores
18345            *    (`myapp_subsection_filterx`).
18346            *    </div>
18347             * @param {Function} factory If the first argument was a string, a factory function for the filter to be registered.
18348            * @returns {Object} Registered filter instance, or if a map of filters was provided then a map
18349            *    of the registered filter instances.
18350            */
18351           function register(name, factory) {
18352             if (isObject(name)) {
18353               var filters = {};
18354               forEach(name, function(filter, key) {
18355                 filters[key] = register(key, filter);
18356               });
18357               return filters;
18358             } else {
18359               return $provide.factory(name + suffix, factory);
18360             }
18361           }
18362           this.register = register;
18363
18364           this.$get = ['$injector', function($injector) {
18365             return function(name) {
18366               return $injector.get(name + suffix);
18367             };
18368           }];
18369
18370           ////////////////////////////////////////
18371
18372           /* global
18373             currencyFilter: false,
18374             dateFilter: false,
18375             filterFilter: false,
18376             jsonFilter: false,
18377             limitToFilter: false,
18378             lowercaseFilter: false,
18379             numberFilter: false,
18380             orderByFilter: false,
18381             uppercaseFilter: false,
18382           */
18383
18384           register('currency', currencyFilter);
18385           register('date', dateFilter);
18386           register('filter', filterFilter);
18387           register('json', jsonFilter);
18388           register('limitTo', limitToFilter);
18389           register('lowercase', lowercaseFilter);
18390           register('number', numberFilter);
18391           register('orderBy', orderByFilter);
18392           register('uppercase', uppercaseFilter);
18393         }
18394
18395         /**
18396          * @ngdoc filter
18397          * @name filter
18398          * @kind function
18399          *
18400          * @description
18401          * Selects a subset of items from `array` and returns it as a new array.
18402          *
18403          * @param {Array} array The source array.
18404          * @param {string|Object|function()} expression The predicate to be used for selecting items from
18405          *   `array`.
18406          *
18407          *   Can be one of:
18408          *
18409          *   - `string`: The string is used for matching against the contents of the `array`. All strings or
18410          *     objects with string properties in `array` that match this string will be returned. This also
18411          *     applies to nested object properties.
18412          *     The predicate can be negated by prefixing the string with `!`.
18413          *
18414          *   - `Object`: A pattern object can be used to filter specific properties on objects contained
18415          *     by `array`. For example `{name:"M", phone:"1"}` predicate will return an array of items
18416          *     which have property `name` containing "M" and property `phone` containing "1". A special
18417          *     property name `$` can be used (as in `{$:"text"}`) to accept a match against any
18418          *     property of the object or its nested object properties. That's equivalent to the simple
18419          *     substring match with a `string` as described above. The predicate can be negated by prefixing
18420          *     the string with `!`.
18421          *     For example `{name: "!M"}` predicate will return an array of items which have property `name`
18422          *     not containing "M".
18423          *
18424          *     Note that a named property will match properties on the same level only, while the special
18425          *     `$` property will match properties on the same level or deeper. E.g. an array item like
18426          *     `{name: {first: 'John', last: 'Doe'}}` will **not** be matched by `{name: 'John'}`, but
18427          *     **will** be matched by `{$: 'John'}`.
18428          *
18429          *   - `function(value, index, array)`: A predicate function can be used to write arbitrary filters.
18430          *     The function is called for each element of the array, with the element, its index, and
18431          *     the entire array itself as arguments.
18432          *
18433          *     The final result is an array of those elements that the predicate returned true for.
18434          *
18435          * @param {function(actual, expected)|true|undefined} comparator Comparator which is used in
18436          *     determining if the expected value (from the filter expression) and actual value (from
18437          *     the object in the array) should be considered a match.
18438          *
18439          *   Can be one of:
18440          *
18441          *   - `function(actual, expected)`:
18442          *     The function will be given the object value and the predicate value to compare and
18443          *     should return true if both values should be considered equal.
18444          *
18445          *   - `true`: A shorthand for `function(actual, expected) { return angular.equals(actual, expected)}`.
18446          *     This is essentially strict comparison of expected and actual.
18447          *
18448          *   - `false|undefined`: A short hand for a function which will look for a substring match in case
18449          *     insensitive way.
18450          *
18451          *     Primitive values are converted to strings. Objects are not compared against primitives,
18452          *     unless they have a custom `toString` method (e.g. `Date` objects).
18453          *
18454          * @example
18455            <example>
18456              <file name="index.html">
18457                <div ng-init="friends = [{name:'John', phone:'555-1276'},
18458                                         {name:'Mary', phone:'800-BIG-MARY'},
18459                                         {name:'Mike', phone:'555-4321'},
18460                                         {name:'Adam', phone:'555-5678'},
18461                                         {name:'Julie', phone:'555-8765'},
18462                                         {name:'Juliette', phone:'555-5678'}]"></div>
18463
18464                <label>Search: <input ng-model="searchText"></label>
18465                <table id="searchTextResults">
18466                  <tr><th>Name</th><th>Phone</th></tr>
18467                  <tr ng-repeat="friend in friends | filter:searchText">
18468                    <td>{{friend.name}}</td>
18469                    <td>{{friend.phone}}</td>
18470                  </tr>
18471                </table>
18472                <hr>
18473                <label>Any: <input ng-model="search.$"></label> <br>
18474                <label>Name only <input ng-model="search.name"></label><br>
18475                <label>Phone only <input ng-model="search.phone"></label><br>
18476                <label>Equality <input type="checkbox" ng-model="strict"></label><br>
18477                <table id="searchObjResults">
18478                  <tr><th>Name</th><th>Phone</th></tr>
18479                  <tr ng-repeat="friendObj in friends | filter:search:strict">
18480                    <td>{{friendObj.name}}</td>
18481                    <td>{{friendObj.phone}}</td>
18482                  </tr>
18483                </table>
18484              </file>
18485              <file name="protractor.js" type="protractor">
18486                var expectFriendNames = function(expectedNames, key) {
18487                  element.all(by.repeater(key + ' in friends').column(key + '.name')).then(function(arr) {
18488                    arr.forEach(function(wd, i) {
18489                      expect(wd.getText()).toMatch(expectedNames[i]);
18490                    });
18491                  });
18492                };
18493
18494                it('should search across all fields when filtering with a string', function() {
18495                  var searchText = element(by.model('searchText'));
18496                  searchText.clear();
18497                  searchText.sendKeys('m');
18498                  expectFriendNames(['Mary', 'Mike', 'Adam'], 'friend');
18499
18500                  searchText.clear();
18501                  searchText.sendKeys('76');
18502                  expectFriendNames(['John', 'Julie'], 'friend');
18503                });
18504
18505                it('should search in specific fields when filtering with a predicate object', function() {
18506                  var searchAny = element(by.model('search.$'));
18507                  searchAny.clear();
18508                  searchAny.sendKeys('i');
18509                  expectFriendNames(['Mary', 'Mike', 'Julie', 'Juliette'], 'friendObj');
18510                });
18511                it('should use a equal comparison when comparator is true', function() {
18512                  var searchName = element(by.model('search.name'));
18513                  var strict = element(by.model('strict'));
18514                  searchName.clear();
18515                  searchName.sendKeys('Julie');
18516                  strict.click();
18517                  expectFriendNames(['Julie'], 'friendObj');
18518                });
18519              </file>
18520            </example>
18521          */
18522         function filterFilter() {
18523           return function(array, expression, comparator) {
18524             if (!isArrayLike(array)) {
18525               if (array == null) {
18526                 return array;
18527               } else {
18528                 throw minErr('filter')('notarray', 'Expected array but received: {0}', array);
18529               }
18530             }
18531
18532             var expressionType = getTypeForFilter(expression);
18533             var predicateFn;
18534             var matchAgainstAnyProp;
18535
18536             switch (expressionType) {
18537               case 'function':
18538                 predicateFn = expression;
18539                 break;
18540               case 'boolean':
18541               case 'null':
18542               case 'number':
18543               case 'string':
18544                 matchAgainstAnyProp = true;
18545                 //jshint -W086
18546               case 'object':
18547                 //jshint +W086
18548                 predicateFn = createPredicateFn(expression, comparator, matchAgainstAnyProp);
18549                 break;
18550               default:
18551                 return array;
18552             }
18553
18554             return Array.prototype.filter.call(array, predicateFn);
18555           };
18556         }
18557
18558         // Helper functions for `filterFilter`
18559         function createPredicateFn(expression, comparator, matchAgainstAnyProp) {
18560           var shouldMatchPrimitives = isObject(expression) && ('$' in expression);
18561           var predicateFn;
18562
18563           if (comparator === true) {
18564             comparator = equals;
18565           } else if (!isFunction(comparator)) {
18566             comparator = function(actual, expected) {
18567               if (isUndefined(actual)) {
18568                 // No substring matching against `undefined`
18569                 return false;
18570               }
18571               if ((actual === null) || (expected === null)) {
18572                 // No substring matching against `null`; only match against `null`
18573                 return actual === expected;
18574               }
18575               if (isObject(expected) || (isObject(actual) && !hasCustomToString(actual))) {
18576                 // Should not compare primitives against objects, unless they have custom `toString` method
18577                 return false;
18578               }
18579
18580               actual = lowercase('' + actual);
18581               expected = lowercase('' + expected);
18582               return actual.indexOf(expected) !== -1;
18583             };
18584           }
18585
18586           predicateFn = function(item) {
18587             if (shouldMatchPrimitives && !isObject(item)) {
18588               return deepCompare(item, expression.$, comparator, false);
18589             }
18590             return deepCompare(item, expression, comparator, matchAgainstAnyProp);
18591           };
18592
18593           return predicateFn;
18594         }
18595
18596         function deepCompare(actual, expected, comparator, matchAgainstAnyProp, dontMatchWholeObject) {
18597           var actualType = getTypeForFilter(actual);
18598           var expectedType = getTypeForFilter(expected);
18599
18600           if ((expectedType === 'string') && (expected.charAt(0) === '!')) {
18601             return !deepCompare(actual, expected.substring(1), comparator, matchAgainstAnyProp);
18602           } else if (isArray(actual)) {
18603             // In case `actual` is an array, consider it a match
18604             // if ANY of it's items matches `expected`
18605             return actual.some(function(item) {
18606               return deepCompare(item, expected, comparator, matchAgainstAnyProp);
18607             });
18608           }
18609
18610           switch (actualType) {
18611             case 'object':
18612               var key;
18613               if (matchAgainstAnyProp) {
18614                 for (key in actual) {
18615                   if ((key.charAt(0) !== '$') && deepCompare(actual[key], expected, comparator, true)) {
18616                     return true;
18617                   }
18618                 }
18619                 return dontMatchWholeObject ? false : deepCompare(actual, expected, comparator, false);
18620               } else if (expectedType === 'object') {
18621                 for (key in expected) {
18622                   var expectedVal = expected[key];
18623                   if (isFunction(expectedVal) || isUndefined(expectedVal)) {
18624                     continue;
18625                   }
18626
18627                   var matchAnyProperty = key === '$';
18628                   var actualVal = matchAnyProperty ? actual : actual[key];
18629                   if (!deepCompare(actualVal, expectedVal, comparator, matchAnyProperty, matchAnyProperty)) {
18630                     return false;
18631                   }
18632                 }
18633                 return true;
18634               } else {
18635                 return comparator(actual, expected);
18636               }
18637               break;
18638             case 'function':
18639               return false;
18640             default:
18641               return comparator(actual, expected);
18642           }
18643         }
18644
18645         // Used for easily differentiating between `null` and actual `object`
18646         function getTypeForFilter(val) {
18647           return (val === null) ? 'null' : typeof val;
18648         }
18649
18650         /**
18651          * @ngdoc filter
18652          * @name currency
18653          * @kind function
18654          *
18655          * @description
18656          * Formats a number as a currency (ie $1,234.56). When no currency symbol is provided, default
18657          * symbol for current locale is used.
18658          *
18659          * @param {number} amount Input to filter.
18660          * @param {string=} symbol Currency symbol or identifier to be displayed.
18661          * @param {number=} fractionSize Number of decimal places to round the amount to, defaults to default max fraction size for current locale
18662          * @returns {string} Formatted number.
18663          *
18664          *
18665          * @example
18666            <example module="currencyExample">
18667              <file name="index.html">
18668                <script>
18669                  angular.module('currencyExample', [])
18670                    .controller('ExampleController', ['$scope', function($scope) {
18671                      $scope.amount = 1234.56;
18672                    }]);
18673                </script>
18674                <div ng-controller="ExampleController">
18675                  <input type="number" ng-model="amount" aria-label="amount"> <br>
18676                  default currency symbol ($): <span id="currency-default">{{amount | currency}}</span><br>
18677                  custom currency identifier (USD$): <span id="currency-custom">{{amount | currency:"USD$"}}</span>
18678                  no fractions (0): <span id="currency-no-fractions">{{amount | currency:"USD$":0}}</span>
18679                </div>
18680              </file>
18681              <file name="protractor.js" type="protractor">
18682                it('should init with 1234.56', function() {
18683                  expect(element(by.id('currency-default')).getText()).toBe('$1,234.56');
18684                  expect(element(by.id('currency-custom')).getText()).toBe('USD$1,234.56');
18685                  expect(element(by.id('currency-no-fractions')).getText()).toBe('USD$1,235');
18686                });
18687                it('should update', function() {
18688                  if (browser.params.browser == 'safari') {
18689                    // Safari does not understand the minus key. See
18690                    // https://github.com/angular/protractor/issues/481
18691                    return;
18692                  }
18693                  element(by.model('amount')).clear();
18694                  element(by.model('amount')).sendKeys('-1234');
18695                  expect(element(by.id('currency-default')).getText()).toBe('-$1,234.00');
18696                  expect(element(by.id('currency-custom')).getText()).toBe('-USD$1,234.00');
18697                  expect(element(by.id('currency-no-fractions')).getText()).toBe('-USD$1,234');
18698                });
18699              </file>
18700            </example>
18701          */
18702         currencyFilter.$inject = ['$locale'];
18703         function currencyFilter($locale) {
18704           var formats = $locale.NUMBER_FORMATS;
18705           return function(amount, currencySymbol, fractionSize) {
18706             if (isUndefined(currencySymbol)) {
18707               currencySymbol = formats.CURRENCY_SYM;
18708             }
18709
18710             if (isUndefined(fractionSize)) {
18711               fractionSize = formats.PATTERNS[1].maxFrac;
18712             }
18713
18714             // if null or undefined pass it through
18715             return (amount == null)
18716                 ? amount
18717                 : formatNumber(amount, formats.PATTERNS[1], formats.GROUP_SEP, formats.DECIMAL_SEP, fractionSize).
18718                     replace(/\u00A4/g, currencySymbol);
18719           };
18720         }
18721
18722         /**
18723          * @ngdoc filter
18724          * @name number
18725          * @kind function
18726          *
18727          * @description
18728          * Formats a number as text.
18729          *
18730          * If the input is null or undefined, it will just be returned.
18731          * If the input is infinite (Infinity/-Infinity) the Infinity symbol '∞' is returned.
18732          * If the input is not a number an empty string is returned.
18733          *
18734          *
18735          * @param {number|string} number Number to format.
18736          * @param {(number|string)=} fractionSize Number of decimal places to round the number to.
18737          * If this is not provided then the fraction size is computed from the current locale's number
18738          * formatting pattern. In the case of the default locale, it will be 3.
18739          * @returns {string} Number rounded to decimalPlaces and places a “,” after each third digit.
18740          *
18741          * @example
18742            <example module="numberFilterExample">
18743              <file name="index.html">
18744                <script>
18745                  angular.module('numberFilterExample', [])
18746                    .controller('ExampleController', ['$scope', function($scope) {
18747                      $scope.val = 1234.56789;
18748                    }]);
18749                </script>
18750                <div ng-controller="ExampleController">
18751                  <label>Enter number: <input ng-model='val'></label><br>
18752                  Default formatting: <span id='number-default'>{{val | number}}</span><br>
18753                  No fractions: <span>{{val | number:0}}</span><br>
18754                  Negative number: <span>{{-val | number:4}}</span>
18755                </div>
18756              </file>
18757              <file name="protractor.js" type="protractor">
18758                it('should format numbers', function() {
18759                  expect(element(by.id('number-default')).getText()).toBe('1,234.568');
18760                  expect(element(by.binding('val | number:0')).getText()).toBe('1,235');
18761                  expect(element(by.binding('-val | number:4')).getText()).toBe('-1,234.5679');
18762                });
18763
18764                it('should update', function() {
18765                  element(by.model('val')).clear();
18766                  element(by.model('val')).sendKeys('3374.333');
18767                  expect(element(by.id('number-default')).getText()).toBe('3,374.333');
18768                  expect(element(by.binding('val | number:0')).getText()).toBe('3,374');
18769                  expect(element(by.binding('-val | number:4')).getText()).toBe('-3,374.3330');
18770               });
18771              </file>
18772            </example>
18773          */
18774
18775
18776         numberFilter.$inject = ['$locale'];
18777         function numberFilter($locale) {
18778           var formats = $locale.NUMBER_FORMATS;
18779           return function(number, fractionSize) {
18780
18781             // if null or undefined pass it through
18782             return (number == null)
18783                 ? number
18784                 : formatNumber(number, formats.PATTERNS[0], formats.GROUP_SEP, formats.DECIMAL_SEP,
18785                                fractionSize);
18786           };
18787         }
18788
18789         var DECIMAL_SEP = '.';
18790         function formatNumber(number, pattern, groupSep, decimalSep, fractionSize) {
18791           if (isObject(number)) return '';
18792
18793           var isNegative = number < 0;
18794           number = Math.abs(number);
18795
18796           var isInfinity = number === Infinity;
18797           if (!isInfinity && !isFinite(number)) return '';
18798
18799           var numStr = number + '',
18800               formatedText = '',
18801               hasExponent = false,
18802               parts = [];
18803
18804           if (isInfinity) formatedText = '\u221e';
18805
18806           if (!isInfinity && numStr.indexOf('e') !== -1) {
18807             var match = numStr.match(/([\d\.]+)e(-?)(\d+)/);
18808             if (match && match[2] == '-' && match[3] > fractionSize + 1) {
18809               number = 0;
18810             } else {
18811               formatedText = numStr;
18812               hasExponent = true;
18813             }
18814           }
18815
18816           if (!isInfinity && !hasExponent) {
18817             var fractionLen = (numStr.split(DECIMAL_SEP)[1] || '').length;
18818
18819             // determine fractionSize if it is not specified
18820             if (isUndefined(fractionSize)) {
18821               fractionSize = Math.min(Math.max(pattern.minFrac, fractionLen), pattern.maxFrac);
18822             }
18823
18824             // safely round numbers in JS without hitting imprecisions of floating-point arithmetics
18825             // inspired by:
18826             // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
18827             number = +(Math.round(+(number.toString() + 'e' + fractionSize)).toString() + 'e' + -fractionSize);
18828
18829             var fraction = ('' + number).split(DECIMAL_SEP);
18830             var whole = fraction[0];
18831             fraction = fraction[1] || '';
18832
18833             var i, pos = 0,
18834                 lgroup = pattern.lgSize,
18835                 group = pattern.gSize;
18836
18837             if (whole.length >= (lgroup + group)) {
18838               pos = whole.length - lgroup;
18839               for (i = 0; i < pos; i++) {
18840                 if ((pos - i) % group === 0 && i !== 0) {
18841                   formatedText += groupSep;
18842                 }
18843                 formatedText += whole.charAt(i);
18844               }
18845             }
18846
18847             for (i = pos; i < whole.length; i++) {
18848               if ((whole.length - i) % lgroup === 0 && i !== 0) {
18849                 formatedText += groupSep;
18850               }
18851               formatedText += whole.charAt(i);
18852             }
18853
18854             // format fraction part.
18855             while (fraction.length < fractionSize) {
18856               fraction += '0';
18857             }
18858
18859             if (fractionSize && fractionSize !== "0") formatedText += decimalSep + fraction.substr(0, fractionSize);
18860           } else {
18861             if (fractionSize > 0 && number < 1) {
18862               formatedText = number.toFixed(fractionSize);
18863               number = parseFloat(formatedText);
18864               formatedText = formatedText.replace(DECIMAL_SEP, decimalSep);
18865             }
18866           }
18867
18868           if (number === 0) {
18869             isNegative = false;
18870           }
18871
18872           parts.push(isNegative ? pattern.negPre : pattern.posPre,
18873                      formatedText,
18874                      isNegative ? pattern.negSuf : pattern.posSuf);
18875           return parts.join('');
18876         }
18877
18878         function padNumber(num, digits, trim) {
18879           var neg = '';
18880           if (num < 0) {
18881             neg =  '-';
18882             num = -num;
18883           }
18884           num = '' + num;
18885           while (num.length < digits) num = '0' + num;
18886           if (trim) {
18887             num = num.substr(num.length - digits);
18888           }
18889           return neg + num;
18890         }
18891
18892
18893         function dateGetter(name, size, offset, trim) {
18894           offset = offset || 0;
18895           return function(date) {
18896             var value = date['get' + name]();
18897             if (offset > 0 || value > -offset) {
18898               value += offset;
18899             }
18900             if (value === 0 && offset == -12) value = 12;
18901             return padNumber(value, size, trim);
18902           };
18903         }
18904
18905         function dateStrGetter(name, shortForm) {
18906           return function(date, formats) {
18907             var value = date['get' + name]();
18908             var get = uppercase(shortForm ? ('SHORT' + name) : name);
18909
18910             return formats[get][value];
18911           };
18912         }
18913
18914         function timeZoneGetter(date, formats, offset) {
18915           var zone = -1 * offset;
18916           var paddedZone = (zone >= 0) ? "+" : "";
18917
18918           paddedZone += padNumber(Math[zone > 0 ? 'floor' : 'ceil'](zone / 60), 2) +
18919                         padNumber(Math.abs(zone % 60), 2);
18920
18921           return paddedZone;
18922         }
18923
18924         function getFirstThursdayOfYear(year) {
18925             // 0 = index of January
18926             var dayOfWeekOnFirst = (new Date(year, 0, 1)).getDay();
18927             // 4 = index of Thursday (+1 to account for 1st = 5)
18928             // 11 = index of *next* Thursday (+1 account for 1st = 12)
18929             return new Date(year, 0, ((dayOfWeekOnFirst <= 4) ? 5 : 12) - dayOfWeekOnFirst);
18930         }
18931
18932         function getThursdayThisWeek(datetime) {
18933             return new Date(datetime.getFullYear(), datetime.getMonth(),
18934               // 4 = index of Thursday
18935               datetime.getDate() + (4 - datetime.getDay()));
18936         }
18937
18938         function weekGetter(size) {
18939            return function(date) {
18940               var firstThurs = getFirstThursdayOfYear(date.getFullYear()),
18941                  thisThurs = getThursdayThisWeek(date);
18942
18943               var diff = +thisThurs - +firstThurs,
18944                  result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
18945
18946               return padNumber(result, size);
18947            };
18948         }
18949
18950         function ampmGetter(date, formats) {
18951           return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1];
18952         }
18953
18954         function eraGetter(date, formats) {
18955           return date.getFullYear() <= 0 ? formats.ERAS[0] : formats.ERAS[1];
18956         }
18957
18958         function longEraGetter(date, formats) {
18959           return date.getFullYear() <= 0 ? formats.ERANAMES[0] : formats.ERANAMES[1];
18960         }
18961
18962         var DATE_FORMATS = {
18963           yyyy: dateGetter('FullYear', 4),
18964             yy: dateGetter('FullYear', 2, 0, true),
18965              y: dateGetter('FullYear', 1),
18966           MMMM: dateStrGetter('Month'),
18967            MMM: dateStrGetter('Month', true),
18968             MM: dateGetter('Month', 2, 1),
18969              M: dateGetter('Month', 1, 1),
18970             dd: dateGetter('Date', 2),
18971              d: dateGetter('Date', 1),
18972             HH: dateGetter('Hours', 2),
18973              H: dateGetter('Hours', 1),
18974             hh: dateGetter('Hours', 2, -12),
18975              h: dateGetter('Hours', 1, -12),
18976             mm: dateGetter('Minutes', 2),
18977              m: dateGetter('Minutes', 1),
18978             ss: dateGetter('Seconds', 2),
18979              s: dateGetter('Seconds', 1),
18980              // while ISO 8601 requires fractions to be prefixed with `.` or `,`
18981              // we can be just safely rely on using `sss` since we currently don't support single or two digit fractions
18982            sss: dateGetter('Milliseconds', 3),
18983           EEEE: dateStrGetter('Day'),
18984            EEE: dateStrGetter('Day', true),
18985              a: ampmGetter,
18986              Z: timeZoneGetter,
18987             ww: weekGetter(2),
18988              w: weekGetter(1),
18989              G: eraGetter,
18990              GG: eraGetter,
18991              GGG: eraGetter,
18992              GGGG: longEraGetter
18993         };
18994
18995         var DATE_FORMATS_SPLIT = /((?:[^yMdHhmsaZEwG']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z|G+|w+))(.*)/,
18996             NUMBER_STRING = /^\-?\d+$/;
18997
18998         /**
18999          * @ngdoc filter
19000          * @name date
19001          * @kind function
19002          *
19003          * @description
19004          *   Formats `date` to a string based on the requested `format`.
19005          *
19006          *   `format` string can be composed of the following elements:
19007          *
19008          *   * `'yyyy'`: 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
19009          *   * `'yy'`: 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
19010          *   * `'y'`: 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
19011          *   * `'MMMM'`: Month in year (January-December)
19012          *   * `'MMM'`: Month in year (Jan-Dec)
19013          *   * `'MM'`: Month in year, padded (01-12)
19014          *   * `'M'`: Month in year (1-12)
19015          *   * `'dd'`: Day in month, padded (01-31)
19016          *   * `'d'`: Day in month (1-31)
19017          *   * `'EEEE'`: Day in Week,(Sunday-Saturday)
19018          *   * `'EEE'`: Day in Week, (Sun-Sat)
19019          *   * `'HH'`: Hour in day, padded (00-23)
19020          *   * `'H'`: Hour in day (0-23)
19021          *   * `'hh'`: Hour in AM/PM, padded (01-12)
19022          *   * `'h'`: Hour in AM/PM, (1-12)
19023          *   * `'mm'`: Minute in hour, padded (00-59)
19024          *   * `'m'`: Minute in hour (0-59)
19025          *   * `'ss'`: Second in minute, padded (00-59)
19026          *   * `'s'`: Second in minute (0-59)
19027          *   * `'sss'`: Millisecond in second, padded (000-999)
19028          *   * `'a'`: AM/PM marker
19029          *   * `'Z'`: 4 digit (+sign) representation of the timezone offset (-1200-+1200)
19030          *   * `'ww'`: Week of year, padded (00-53). Week 01 is the week with the first Thursday of the year
19031          *   * `'w'`: Week of year (0-53). Week 1 is the week with the first Thursday of the year
19032          *   * `'G'`, `'GG'`, `'GGG'`: The abbreviated form of the era string (e.g. 'AD')
19033          *   * `'GGGG'`: The long form of the era string (e.g. 'Anno Domini')
19034          *
19035          *   `format` string can also be one of the following predefined
19036          *   {@link guide/i18n localizable formats}:
19037          *
19038          *   * `'medium'`: equivalent to `'MMM d, y h:mm:ss a'` for en_US locale
19039          *     (e.g. Sep 3, 2010 12:05:08 PM)
19040          *   * `'short'`: equivalent to `'M/d/yy h:mm a'` for en_US  locale (e.g. 9/3/10 12:05 PM)
19041          *   * `'fullDate'`: equivalent to `'EEEE, MMMM d, y'` for en_US  locale
19042          *     (e.g. Friday, September 3, 2010)
19043          *   * `'longDate'`: equivalent to `'MMMM d, y'` for en_US  locale (e.g. September 3, 2010)
19044          *   * `'mediumDate'`: equivalent to `'MMM d, y'` for en_US  locale (e.g. Sep 3, 2010)
19045          *   * `'shortDate'`: equivalent to `'M/d/yy'` for en_US locale (e.g. 9/3/10)
19046          *   * `'mediumTime'`: equivalent to `'h:mm:ss a'` for en_US locale (e.g. 12:05:08 PM)
19047          *   * `'shortTime'`: equivalent to `'h:mm a'` for en_US locale (e.g. 12:05 PM)
19048          *
19049          *   `format` string can contain literal values. These need to be escaped by surrounding with single quotes (e.g.
19050          *   `"h 'in the morning'"`). In order to output a single quote, escape it - i.e., two single quotes in a sequence
19051          *   (e.g. `"h 'o''clock'"`).
19052          *
19053          * @param {(Date|number|string)} date Date to format either as Date object, milliseconds (string or
19054          *    number) or various ISO 8601 datetime string formats (e.g. yyyy-MM-ddTHH:mm:ss.sssZ and its
19055          *    shorter versions like yyyy-MM-ddTHH:mmZ, yyyy-MM-dd or yyyyMMddTHHmmssZ). If no timezone is
19056          *    specified in the string input, the time is considered to be in the local timezone.
19057          * @param {string=} format Formatting rules (see Description). If not specified,
19058          *    `mediumDate` is used.
19059          * @param {string=} timezone Timezone to be used for formatting. It understands UTC/GMT and the
19060          *    continental US time zone abbreviations, but for general use, use a time zone offset, for
19061          *    example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
19062          *    If not specified, the timezone of the browser will be used.
19063          * @returns {string} Formatted string or the input if input is not recognized as date/millis.
19064          *
19065          * @example
19066            <example>
19067              <file name="index.html">
19068                <span ng-non-bindable>{{1288323623006 | date:'medium'}}</span>:
19069                    <span>{{1288323623006 | date:'medium'}}</span><br>
19070                <span ng-non-bindable>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span>:
19071                   <span>{{1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'}}</span><br>
19072                <span ng-non-bindable>{{1288323623006 | date:'MM/dd/yyyy @ h:mma'}}</span>:
19073                   <span>{{'1288323623006' | date:'MM/dd/yyyy @ h:mma'}}</span><br>
19074                <span ng-non-bindable>{{1288323623006 | date:"MM/dd/yyyy 'at' h:mma"}}</span>:
19075                   <span>{{'1288323623006' | date:"MM/dd/yyyy 'at' h:mma"}}</span><br>
19076              </file>
19077              <file name="protractor.js" type="protractor">
19078                it('should format date', function() {
19079                  expect(element(by.binding("1288323623006 | date:'medium'")).getText()).
19080                     toMatch(/Oct 2\d, 2010 \d{1,2}:\d{2}:\d{2} (AM|PM)/);
19081                  expect(element(by.binding("1288323623006 | date:'yyyy-MM-dd HH:mm:ss Z'")).getText()).
19082                     toMatch(/2010\-10\-2\d \d{2}:\d{2}:\d{2} (\-|\+)?\d{4}/);
19083                  expect(element(by.binding("'1288323623006' | date:'MM/dd/yyyy @ h:mma'")).getText()).
19084                     toMatch(/10\/2\d\/2010 @ \d{1,2}:\d{2}(AM|PM)/);
19085                  expect(element(by.binding("'1288323623006' | date:\"MM/dd/yyyy 'at' h:mma\"")).getText()).
19086                     toMatch(/10\/2\d\/2010 at \d{1,2}:\d{2}(AM|PM)/);
19087                });
19088              </file>
19089            </example>
19090          */
19091         dateFilter.$inject = ['$locale'];
19092         function dateFilter($locale) {
19093
19094
19095           var R_ISO8601_STR = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
19096                              // 1        2       3         4          5          6          7          8  9     10      11
19097           function jsonStringToDate(string) {
19098             var match;
19099             if (match = string.match(R_ISO8601_STR)) {
19100               var date = new Date(0),
19101                   tzHour = 0,
19102                   tzMin  = 0,
19103                   dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear,
19104                   timeSetter = match[8] ? date.setUTCHours : date.setHours;
19105
19106               if (match[9]) {
19107                 tzHour = toInt(match[9] + match[10]);
19108                 tzMin = toInt(match[9] + match[11]);
19109               }
19110               dateSetter.call(date, toInt(match[1]), toInt(match[2]) - 1, toInt(match[3]));
19111               var h = toInt(match[4] || 0) - tzHour;
19112               var m = toInt(match[5] || 0) - tzMin;
19113               var s = toInt(match[6] || 0);
19114               var ms = Math.round(parseFloat('0.' + (match[7] || 0)) * 1000);
19115               timeSetter.call(date, h, m, s, ms);
19116               return date;
19117             }
19118             return string;
19119           }
19120
19121
19122           return function(date, format, timezone) {
19123             var text = '',
19124                 parts = [],
19125                 fn, match;
19126
19127             format = format || 'mediumDate';
19128             format = $locale.DATETIME_FORMATS[format] || format;
19129             if (isString(date)) {
19130               date = NUMBER_STRING.test(date) ? toInt(date) : jsonStringToDate(date);
19131             }
19132
19133             if (isNumber(date)) {
19134               date = new Date(date);
19135             }
19136
19137             if (!isDate(date) || !isFinite(date.getTime())) {
19138               return date;
19139             }
19140
19141             while (format) {
19142               match = DATE_FORMATS_SPLIT.exec(format);
19143               if (match) {
19144                 parts = concat(parts, match, 1);
19145                 format = parts.pop();
19146               } else {
19147                 parts.push(format);
19148                 format = null;
19149               }
19150             }
19151
19152             var dateTimezoneOffset = date.getTimezoneOffset();
19153             if (timezone) {
19154               dateTimezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
19155               date = convertTimezoneToLocal(date, timezone, true);
19156             }
19157             forEach(parts, function(value) {
19158               fn = DATE_FORMATS[value];
19159               text += fn ? fn(date, $locale.DATETIME_FORMATS, dateTimezoneOffset)
19160                          : value.replace(/(^'|'$)/g, '').replace(/''/g, "'");
19161             });
19162
19163             return text;
19164           };
19165         }
19166
19167
19168         /**
19169          * @ngdoc filter
19170          * @name json
19171          * @kind function
19172          *
19173          * @description
19174          *   Allows you to convert a JavaScript object into JSON string.
19175          *
19176          *   This filter is mostly useful for debugging. When using the double curly {{value}} notation
19177          *   the binding is automatically converted to JSON.
19178          *
19179          * @param {*} object Any JavaScript object (including arrays and primitive types) to filter.
19180          * @param {number=} spacing The number of spaces to use per indentation, defaults to 2.
19181          * @returns {string} JSON string.
19182          *
19183          *
19184          * @example
19185            <example>
19186              <file name="index.html">
19187                <pre id="default-spacing">{{ {'name':'value'} | json }}</pre>
19188                <pre id="custom-spacing">{{ {'name':'value'} | json:4 }}</pre>
19189              </file>
19190              <file name="protractor.js" type="protractor">
19191                it('should jsonify filtered objects', function() {
19192                  expect(element(by.id('default-spacing')).getText()).toMatch(/\{\n  "name": ?"value"\n}/);
19193                  expect(element(by.id('custom-spacing')).getText()).toMatch(/\{\n    "name": ?"value"\n}/);
19194                });
19195              </file>
19196            </example>
19197          *
19198          */
19199         function jsonFilter() {
19200           return function(object, spacing) {
19201             if (isUndefined(spacing)) {
19202                 spacing = 2;
19203             }
19204             return toJson(object, spacing);
19205           };
19206         }
19207
19208
19209         /**
19210          * @ngdoc filter
19211          * @name lowercase
19212          * @kind function
19213          * @description
19214          * Converts string to lowercase.
19215          * @see angular.lowercase
19216          */
19217         var lowercaseFilter = valueFn(lowercase);
19218
19219
19220         /**
19221          * @ngdoc filter
19222          * @name uppercase
19223          * @kind function
19224          * @description
19225          * Converts string to uppercase.
19226          * @see angular.uppercase
19227          */
19228         var uppercaseFilter = valueFn(uppercase);
19229
19230         /**
19231          * @ngdoc filter
19232          * @name limitTo
19233          * @kind function
19234          *
19235          * @description
19236          * Creates a new array or string containing only a specified number of elements. The elements
19237          * are taken from either the beginning or the end of the source array, string or number, as specified by
19238          * the value and sign (positive or negative) of `limit`. If a number is used as input, it is
19239          * converted to a string.
19240          *
19241          * @param {Array|string|number} input Source array, string or number to be limited.
19242          * @param {string|number} limit The length of the returned array or string. If the `limit` number
19243          *     is positive, `limit` number of items from the beginning of the source array/string are copied.
19244          *     If the number is negative, `limit` number  of items from the end of the source array/string
19245          *     are copied. The `limit` will be trimmed if it exceeds `array.length`. If `limit` is undefined,
19246          *     the input will be returned unchanged.
19247          * @param {(string|number)=} begin Index at which to begin limitation. As a negative index, `begin`
19248          *     indicates an offset from the end of `input`. Defaults to `0`.
19249          * @returns {Array|string} A new sub-array or substring of length `limit` or less if input array
19250          *     had less than `limit` elements.
19251          *
19252          * @example
19253            <example module="limitToExample">
19254              <file name="index.html">
19255                <script>
19256                  angular.module('limitToExample', [])
19257                    .controller('ExampleController', ['$scope', function($scope) {
19258                      $scope.numbers = [1,2,3,4,5,6,7,8,9];
19259                      $scope.letters = "abcdefghi";
19260                      $scope.longNumber = 2345432342;
19261                      $scope.numLimit = 3;
19262                      $scope.letterLimit = 3;
19263                      $scope.longNumberLimit = 3;
19264                    }]);
19265                </script>
19266                <div ng-controller="ExampleController">
19267                  <label>
19268                     Limit {{numbers}} to:
19269                     <input type="number" step="1" ng-model="numLimit">
19270                  </label>
19271                  <p>Output numbers: {{ numbers | limitTo:numLimit }}</p>
19272                  <label>
19273                     Limit {{letters}} to:
19274                     <input type="number" step="1" ng-model="letterLimit">
19275                  </label>
19276                  <p>Output letters: {{ letters | limitTo:letterLimit }}</p>
19277                  <label>
19278                     Limit {{longNumber}} to:
19279                     <input type="number" step="1" ng-model="longNumberLimit">
19280                  </label>
19281                  <p>Output long number: {{ longNumber | limitTo:longNumberLimit }}</p>
19282                </div>
19283              </file>
19284              <file name="protractor.js" type="protractor">
19285                var numLimitInput = element(by.model('numLimit'));
19286                var letterLimitInput = element(by.model('letterLimit'));
19287                var longNumberLimitInput = element(by.model('longNumberLimit'));
19288                var limitedNumbers = element(by.binding('numbers | limitTo:numLimit'));
19289                var limitedLetters = element(by.binding('letters | limitTo:letterLimit'));
19290                var limitedLongNumber = element(by.binding('longNumber | limitTo:longNumberLimit'));
19291
19292                it('should limit the number array to first three items', function() {
19293                  expect(numLimitInput.getAttribute('value')).toBe('3');
19294                  expect(letterLimitInput.getAttribute('value')).toBe('3');
19295                  expect(longNumberLimitInput.getAttribute('value')).toBe('3');
19296                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3]');
19297                  expect(limitedLetters.getText()).toEqual('Output letters: abc');
19298                  expect(limitedLongNumber.getText()).toEqual('Output long number: 234');
19299                });
19300
19301                // There is a bug in safari and protractor that doesn't like the minus key
19302                // it('should update the output when -3 is entered', function() {
19303                //   numLimitInput.clear();
19304                //   numLimitInput.sendKeys('-3');
19305                //   letterLimitInput.clear();
19306                //   letterLimitInput.sendKeys('-3');
19307                //   longNumberLimitInput.clear();
19308                //   longNumberLimitInput.sendKeys('-3');
19309                //   expect(limitedNumbers.getText()).toEqual('Output numbers: [7,8,9]');
19310                //   expect(limitedLetters.getText()).toEqual('Output letters: ghi');
19311                //   expect(limitedLongNumber.getText()).toEqual('Output long number: 342');
19312                // });
19313
19314                it('should not exceed the maximum size of input array', function() {
19315                  numLimitInput.clear();
19316                  numLimitInput.sendKeys('100');
19317                  letterLimitInput.clear();
19318                  letterLimitInput.sendKeys('100');
19319                  longNumberLimitInput.clear();
19320                  longNumberLimitInput.sendKeys('100');
19321                  expect(limitedNumbers.getText()).toEqual('Output numbers: [1,2,3,4,5,6,7,8,9]');
19322                  expect(limitedLetters.getText()).toEqual('Output letters: abcdefghi');
19323                  expect(limitedLongNumber.getText()).toEqual('Output long number: 2345432342');
19324                });
19325              </file>
19326            </example>
19327         */
19328         function limitToFilter() {
19329           return function(input, limit, begin) {
19330             if (Math.abs(Number(limit)) === Infinity) {
19331               limit = Number(limit);
19332             } else {
19333               limit = toInt(limit);
19334             }
19335             if (isNaN(limit)) return input;
19336
19337             if (isNumber(input)) input = input.toString();
19338             if (!isArray(input) && !isString(input)) return input;
19339
19340             begin = (!begin || isNaN(begin)) ? 0 : toInt(begin);
19341             begin = (begin < 0) ? Math.max(0, input.length + begin) : begin;
19342
19343             if (limit >= 0) {
19344               return input.slice(begin, begin + limit);
19345             } else {
19346               if (begin === 0) {
19347                 return input.slice(limit, input.length);
19348               } else {
19349                 return input.slice(Math.max(0, begin + limit), begin);
19350               }
19351             }
19352           };
19353         }
19354
19355         /**
19356          * @ngdoc filter
19357          * @name orderBy
19358          * @kind function
19359          *
19360          * @description
19361          * Orders a specified `array` by the `expression` predicate. It is ordered alphabetically
19362          * for strings and numerically for numbers. Note: if you notice numbers are not being sorted
19363          * as expected, make sure they are actually being saved as numbers and not strings.
19364          *
19365          * @param {Array} array The array to sort.
19366          * @param {function(*)|string|Array.<(function(*)|string)>=} expression A predicate to be
19367          *    used by the comparator to determine the order of elements.
19368          *
19369          *    Can be one of:
19370          *
19371          *    - `function`: Getter function. The result of this function will be sorted using the
19372          *      `<`, `===`, `>` operator.
19373          *    - `string`: An Angular expression. The result of this expression is used to compare elements
19374          *      (for example `name` to sort by a property called `name` or `name.substr(0, 3)` to sort by
19375          *      3 first characters of a property called `name`). The result of a constant expression
19376          *      is interpreted as a property name to be used in comparisons (for example `"special name"`
19377          *      to sort object by the value of their `special name` property). An expression can be
19378          *      optionally prefixed with `+` or `-` to control ascending or descending sort order
19379          *      (for example, `+name` or `-name`). If no property is provided, (e.g. `'+'`) then the array
19380          *      element itself is used to compare where sorting.
19381          *    - `Array`: An array of function or string predicates. The first predicate in the array
19382          *      is used for sorting, but when two items are equivalent, the next predicate is used.
19383          *
19384          *    If the predicate is missing or empty then it defaults to `'+'`.
19385          *
19386          * @param {boolean=} reverse Reverse the order of the array.
19387          * @returns {Array} Sorted copy of the source array.
19388          *
19389          *
19390          * @example
19391          * The example below demonstrates a simple ngRepeat, where the data is sorted
19392          * by age in descending order (predicate is set to `'-age'`).
19393          * `reverse` is not set, which means it defaults to `false`.
19394            <example module="orderByExample">
19395              <file name="index.html">
19396                <script>
19397                  angular.module('orderByExample', [])
19398                    .controller('ExampleController', ['$scope', function($scope) {
19399                      $scope.friends =
19400                          [{name:'John', phone:'555-1212', age:10},
19401                           {name:'Mary', phone:'555-9876', age:19},
19402                           {name:'Mike', phone:'555-4321', age:21},
19403                           {name:'Adam', phone:'555-5678', age:35},
19404                           {name:'Julie', phone:'555-8765', age:29}];
19405                    }]);
19406                </script>
19407                <div ng-controller="ExampleController">
19408                  <table class="friend">
19409                    <tr>
19410                      <th>Name</th>
19411                      <th>Phone Number</th>
19412                      <th>Age</th>
19413                    </tr>
19414                    <tr ng-repeat="friend in friends | orderBy:'-age'">
19415                      <td>{{friend.name}}</td>
19416                      <td>{{friend.phone}}</td>
19417                      <td>{{friend.age}}</td>
19418                    </tr>
19419                  </table>
19420                </div>
19421              </file>
19422            </example>
19423          *
19424          * The predicate and reverse parameters can be controlled dynamically through scope properties,
19425          * as shown in the next example.
19426          * @example
19427            <example module="orderByExample">
19428              <file name="index.html">
19429                <script>
19430                  angular.module('orderByExample', [])
19431                    .controller('ExampleController', ['$scope', function($scope) {
19432                      $scope.friends =
19433                          [{name:'John', phone:'555-1212', age:10},
19434                           {name:'Mary', phone:'555-9876', age:19},
19435                           {name:'Mike', phone:'555-4321', age:21},
19436                           {name:'Adam', phone:'555-5678', age:35},
19437                           {name:'Julie', phone:'555-8765', age:29}];
19438                      $scope.predicate = 'age';
19439                      $scope.reverse = true;
19440                      $scope.order = function(predicate) {
19441                        $scope.reverse = ($scope.predicate === predicate) ? !$scope.reverse : false;
19442                        $scope.predicate = predicate;
19443                      };
19444                    }]);
19445                </script>
19446                <style type="text/css">
19447                  .sortorder:after {
19448                    content: '\25b2';
19449                  }
19450                  .sortorder.reverse:after {
19451                    content: '\25bc';
19452                  }
19453                </style>
19454                <div ng-controller="ExampleController">
19455                  <pre>Sorting predicate = {{predicate}}; reverse = {{reverse}}</pre>
19456                  <hr/>
19457                  [ <a href="" ng-click="predicate=''">unsorted</a> ]
19458                  <table class="friend">
19459                    <tr>
19460                      <th>
19461                        <a href="" ng-click="order('name')">Name</a>
19462                        <span class="sortorder" ng-show="predicate === 'name'" ng-class="{reverse:reverse}"></span>
19463                      </th>
19464                      <th>
19465                        <a href="" ng-click="order('phone')">Phone Number</a>
19466                        <span class="sortorder" ng-show="predicate === 'phone'" ng-class="{reverse:reverse}"></span>
19467                      </th>
19468                      <th>
19469                        <a href="" ng-click="order('age')">Age</a>
19470                        <span class="sortorder" ng-show="predicate === 'age'" ng-class="{reverse:reverse}"></span>
19471                      </th>
19472                    </tr>
19473                    <tr ng-repeat="friend in friends | orderBy:predicate:reverse">
19474                      <td>{{friend.name}}</td>
19475                      <td>{{friend.phone}}</td>
19476                      <td>{{friend.age}}</td>
19477                    </tr>
19478                  </table>
19479                </div>
19480              </file>
19481            </example>
19482          *
19483          * It's also possible to call the orderBy filter manually, by injecting `$filter`, retrieving the
19484          * filter routine with `$filter('orderBy')`, and calling the returned filter routine with the
19485          * desired parameters.
19486          *
19487          * Example:
19488          *
19489          * @example
19490           <example module="orderByExample">
19491             <file name="index.html">
19492               <div ng-controller="ExampleController">
19493                 <table class="friend">
19494                   <tr>
19495                     <th><a href="" ng-click="reverse=false;order('name', false)">Name</a>
19496                       (<a href="" ng-click="order('-name',false)">^</a>)</th>
19497                     <th><a href="" ng-click="reverse=!reverse;order('phone', reverse)">Phone Number</a></th>
19498                     <th><a href="" ng-click="reverse=!reverse;order('age',reverse)">Age</a></th>
19499                   </tr>
19500                   <tr ng-repeat="friend in friends">
19501                     <td>{{friend.name}}</td>
19502                     <td>{{friend.phone}}</td>
19503                     <td>{{friend.age}}</td>
19504                   </tr>
19505                 </table>
19506               </div>
19507             </file>
19508
19509             <file name="script.js">
19510               angular.module('orderByExample', [])
19511                 .controller('ExampleController', ['$scope', '$filter', function($scope, $filter) {
19512                   var orderBy = $filter('orderBy');
19513                   $scope.friends = [
19514                     { name: 'John',    phone: '555-1212',    age: 10 },
19515                     { name: 'Mary',    phone: '555-9876',    age: 19 },
19516                     { name: 'Mike',    phone: '555-4321',    age: 21 },
19517                     { name: 'Adam',    phone: '555-5678',    age: 35 },
19518                     { name: 'Julie',   phone: '555-8765',    age: 29 }
19519                   ];
19520                   $scope.order = function(predicate, reverse) {
19521                     $scope.friends = orderBy($scope.friends, predicate, reverse);
19522                   };
19523                   $scope.order('-age',false);
19524                 }]);
19525             </file>
19526         </example>
19527          */
19528         orderByFilter.$inject = ['$parse'];
19529         function orderByFilter($parse) {
19530           return function(array, sortPredicate, reverseOrder) {
19531
19532             if (!(isArrayLike(array))) return array;
19533
19534             if (!isArray(sortPredicate)) { sortPredicate = [sortPredicate]; }
19535             if (sortPredicate.length === 0) { sortPredicate = ['+']; }
19536
19537             var predicates = processPredicates(sortPredicate, reverseOrder);
19538             // Add a predicate at the end that evaluates to the element index. This makes the
19539             // sort stable as it works as a tie-breaker when all the input predicates cannot
19540             // distinguish between two elements.
19541             predicates.push({ get: function() { return {}; }, descending: reverseOrder ? -1 : 1});
19542
19543             // The next three lines are a version of a Swartzian Transform idiom from Perl
19544             // (sometimes called the Decorate-Sort-Undecorate idiom)
19545             // See https://en.wikipedia.org/wiki/Schwartzian_transform
19546             var compareValues = Array.prototype.map.call(array, getComparisonObject);
19547             compareValues.sort(doComparison);
19548             array = compareValues.map(function(item) { return item.value; });
19549
19550             return array;
19551
19552             function getComparisonObject(value, index) {
19553               return {
19554                 value: value,
19555                 predicateValues: predicates.map(function(predicate) {
19556                   return getPredicateValue(predicate.get(value), index);
19557                 })
19558               };
19559             }
19560
19561             function doComparison(v1, v2) {
19562               var result = 0;
19563               for (var index=0, length = predicates.length; index < length; ++index) {
19564                 result = compare(v1.predicateValues[index], v2.predicateValues[index]) * predicates[index].descending;
19565                 if (result) break;
19566               }
19567               return result;
19568             }
19569           };
19570
19571           function processPredicates(sortPredicate, reverseOrder) {
19572             reverseOrder = reverseOrder ? -1 : 1;
19573             return sortPredicate.map(function(predicate) {
19574               var descending = 1, get = identity;
19575
19576               if (isFunction(predicate)) {
19577                 get = predicate;
19578               } else if (isString(predicate)) {
19579                 if ((predicate.charAt(0) == '+' || predicate.charAt(0) == '-')) {
19580                   descending = predicate.charAt(0) == '-' ? -1 : 1;
19581                   predicate = predicate.substring(1);
19582                 }
19583                 if (predicate !== '') {
19584                   get = $parse(predicate);
19585                   if (get.constant) {
19586                     var key = get();
19587                     get = function(value) { return value[key]; };
19588                   }
19589                 }
19590               }
19591               return { get: get, descending: descending * reverseOrder };
19592             });
19593           }
19594
19595           function isPrimitive(value) {
19596             switch (typeof value) {
19597               case 'number': /* falls through */
19598               case 'boolean': /* falls through */
19599               case 'string':
19600                 return true;
19601               default:
19602                 return false;
19603             }
19604           }
19605
19606           function objectValue(value, index) {
19607             // If `valueOf` is a valid function use that
19608             if (typeof value.valueOf === 'function') {
19609               value = value.valueOf();
19610               if (isPrimitive(value)) return value;
19611             }
19612             // If `toString` is a valid function and not the one from `Object.prototype` use that
19613             if (hasCustomToString(value)) {
19614               value = value.toString();
19615               if (isPrimitive(value)) return value;
19616             }
19617             // We have a basic object so we use the position of the object in the collection
19618             return index;
19619           }
19620
19621           function getPredicateValue(value, index) {
19622             var type = typeof value;
19623             if (value === null) {
19624               type = 'string';
19625               value = 'null';
19626             } else if (type === 'string') {
19627               value = value.toLowerCase();
19628             } else if (type === 'object') {
19629               value = objectValue(value, index);
19630             }
19631             return { value: value, type: type };
19632           }
19633
19634           function compare(v1, v2) {
19635             var result = 0;
19636             if (v1.type === v2.type) {
19637               if (v1.value !== v2.value) {
19638                 result = v1.value < v2.value ? -1 : 1;
19639               }
19640             } else {
19641               result = v1.type < v2.type ? -1 : 1;
19642             }
19643             return result;
19644           }
19645         }
19646
19647         function ngDirective(directive) {
19648           if (isFunction(directive)) {
19649             directive = {
19650               link: directive
19651             };
19652           }
19653           directive.restrict = directive.restrict || 'AC';
19654           return valueFn(directive);
19655         }
19656
19657         /**
19658          * @ngdoc directive
19659          * @name a
19660          * @restrict E
19661          *
19662          * @description
19663          * Modifies the default behavior of the html A tag so that the default action is prevented when
19664          * the href attribute is empty.
19665          *
19666          * This change permits the easy creation of action links with the `ngClick` directive
19667          * without changing the location or causing page reloads, e.g.:
19668          * `<a href="" ng-click="list.addItem()">Add Item</a>`
19669          */
19670         var htmlAnchorDirective = valueFn({
19671           restrict: 'E',
19672           compile: function(element, attr) {
19673             if (!attr.href && !attr.xlinkHref) {
19674               return function(scope, element) {
19675                 // If the linked element is not an anchor tag anymore, do nothing
19676                 if (element[0].nodeName.toLowerCase() !== 'a') return;
19677
19678                 // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
19679                 var href = toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
19680                            'xlink:href' : 'href';
19681                 element.on('click', function(event) {
19682                   // if we have no href url, then don't navigate anywhere.
19683                   if (!element.attr(href)) {
19684                     event.preventDefault();
19685                   }
19686                 });
19687               };
19688             }
19689           }
19690         });
19691
19692         /**
19693          * @ngdoc directive
19694          * @name ngHref
19695          * @restrict A
19696          * @priority 99
19697          *
19698          * @description
19699          * Using Angular markup like `{{hash}}` in an href attribute will
19700          * make the link go to the wrong URL if the user clicks it before
19701          * Angular has a chance to replace the `{{hash}}` markup with its
19702          * value. Until Angular replaces the markup the link will be broken
19703          * and will most likely return a 404 error. The `ngHref` directive
19704          * solves this problem.
19705          *
19706          * The wrong way to write it:
19707          * ```html
19708          * <a href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19709          * ```
19710          *
19711          * The correct way to write it:
19712          * ```html
19713          * <a ng-href="http://www.gravatar.com/avatar/{{hash}}">link1</a>
19714          * ```
19715          *
19716          * @element A
19717          * @param {template} ngHref any string which can contain `{{}}` markup.
19718          *
19719          * @example
19720          * This example shows various combinations of `href`, `ng-href` and `ng-click` attributes
19721          * in links and their different behaviors:
19722             <example>
19723               <file name="index.html">
19724                 <input ng-model="value" /><br />
19725                 <a id="link-1" href ng-click="value = 1">link 1</a> (link, don't reload)<br />
19726                 <a id="link-2" href="" ng-click="value = 2">link 2</a> (link, don't reload)<br />
19727                 <a id="link-3" ng-href="/{{'123'}}">link 3</a> (link, reload!)<br />
19728                 <a id="link-4" href="" name="xx" ng-click="value = 4">anchor</a> (link, don't reload)<br />
19729                 <a id="link-5" name="xxx" ng-click="value = 5">anchor</a> (no link)<br />
19730                 <a id="link-6" ng-href="{{value}}">link</a> (link, change location)
19731               </file>
19732               <file name="protractor.js" type="protractor">
19733                 it('should execute ng-click but not reload when href without value', function() {
19734                   element(by.id('link-1')).click();
19735                   expect(element(by.model('value')).getAttribute('value')).toEqual('1');
19736                   expect(element(by.id('link-1')).getAttribute('href')).toBe('');
19737                 });
19738
19739                 it('should execute ng-click but not reload when href empty string', function() {
19740                   element(by.id('link-2')).click();
19741                   expect(element(by.model('value')).getAttribute('value')).toEqual('2');
19742                   expect(element(by.id('link-2')).getAttribute('href')).toBe('');
19743                 });
19744
19745                 it('should execute ng-click and change url when ng-href specified', function() {
19746                   expect(element(by.id('link-3')).getAttribute('href')).toMatch(/\/123$/);
19747
19748                   element(by.id('link-3')).click();
19749
19750                   // At this point, we navigate away from an Angular page, so we need
19751                   // to use browser.driver to get the base webdriver.
19752
19753                   browser.wait(function() {
19754                     return browser.driver.getCurrentUrl().then(function(url) {
19755                       return url.match(/\/123$/);
19756                     });
19757                   }, 5000, 'page should navigate to /123');
19758                 });
19759
19760                 it('should execute ng-click but not reload when href empty string and name specified', function() {
19761                   element(by.id('link-4')).click();
19762                   expect(element(by.model('value')).getAttribute('value')).toEqual('4');
19763                   expect(element(by.id('link-4')).getAttribute('href')).toBe('');
19764                 });
19765
19766                 it('should execute ng-click but not reload when no href but name specified', function() {
19767                   element(by.id('link-5')).click();
19768                   expect(element(by.model('value')).getAttribute('value')).toEqual('5');
19769                   expect(element(by.id('link-5')).getAttribute('href')).toBe(null);
19770                 });
19771
19772                 it('should only change url when only ng-href', function() {
19773                   element(by.model('value')).clear();
19774                   element(by.model('value')).sendKeys('6');
19775                   expect(element(by.id('link-6')).getAttribute('href')).toMatch(/\/6$/);
19776
19777                   element(by.id('link-6')).click();
19778
19779                   // At this point, we navigate away from an Angular page, so we need
19780                   // to use browser.driver to get the base webdriver.
19781                   browser.wait(function() {
19782                     return browser.driver.getCurrentUrl().then(function(url) {
19783                       return url.match(/\/6$/);
19784                     });
19785                   }, 5000, 'page should navigate to /6');
19786                 });
19787               </file>
19788             </example>
19789          */
19790
19791         /**
19792          * @ngdoc directive
19793          * @name ngSrc
19794          * @restrict A
19795          * @priority 99
19796          *
19797          * @description
19798          * Using Angular markup like `{{hash}}` in a `src` attribute doesn't
19799          * work right: The browser will fetch from the URL with the literal
19800          * text `{{hash}}` until Angular replaces the expression inside
19801          * `{{hash}}`. The `ngSrc` directive solves this problem.
19802          *
19803          * The buggy way to write it:
19804          * ```html
19805          * <img src="http://www.gravatar.com/avatar/{{hash}}" alt="Description"/>
19806          * ```
19807          *
19808          * The correct way to write it:
19809          * ```html
19810          * <img ng-src="http://www.gravatar.com/avatar/{{hash}}" alt="Description" />
19811          * ```
19812          *
19813          * @element IMG
19814          * @param {template} ngSrc any string which can contain `{{}}` markup.
19815          */
19816
19817         /**
19818          * @ngdoc directive
19819          * @name ngSrcset
19820          * @restrict A
19821          * @priority 99
19822          *
19823          * @description
19824          * Using Angular markup like `{{hash}}` in a `srcset` attribute doesn't
19825          * work right: The browser will fetch from the URL with the literal
19826          * text `{{hash}}` until Angular replaces the expression inside
19827          * `{{hash}}`. The `ngSrcset` directive solves this problem.
19828          *
19829          * The buggy way to write it:
19830          * ```html
19831          * <img srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description"/>
19832          * ```
19833          *
19834          * The correct way to write it:
19835          * ```html
19836          * <img ng-srcset="http://www.gravatar.com/avatar/{{hash}} 2x" alt="Description" />
19837          * ```
19838          *
19839          * @element IMG
19840          * @param {template} ngSrcset any string which can contain `{{}}` markup.
19841          */
19842
19843         /**
19844          * @ngdoc directive
19845          * @name ngDisabled
19846          * @restrict A
19847          * @priority 100
19848          *
19849          * @description
19850          *
19851          * This directive sets the `disabled` attribute on the element if the
19852          * {@link guide/expression expression} inside `ngDisabled` evaluates to truthy.
19853          *
19854          * A special directive is necessary because we cannot use interpolation inside the `disabled`
19855          * attribute.  The following example would make the button enabled on Chrome/Firefox
19856          * but not on older IEs:
19857          *
19858          * ```html
19859          * <!-- See below for an example of ng-disabled being used correctly -->
19860          * <div ng-init="isDisabled = false">
19861          *  <button disabled="{{isDisabled}}">Disabled</button>
19862          * </div>
19863          * ```
19864          *
19865          * This is because the HTML specification does not require browsers to preserve the values of
19866          * boolean attributes such as `disabled` (Their presence means true and their absence means false.)
19867          * If we put an Angular interpolation expression into such an attribute then the
19868          * binding information would be lost when the browser removes the attribute.
19869          *
19870          * @example
19871             <example>
19872               <file name="index.html">
19873                 <label>Click me to toggle: <input type="checkbox" ng-model="checked"></label><br/>
19874                 <button ng-model="button" ng-disabled="checked">Button</button>
19875               </file>
19876               <file name="protractor.js" type="protractor">
19877                 it('should toggle button', function() {
19878                   expect(element(by.css('button')).getAttribute('disabled')).toBeFalsy();
19879                   element(by.model('checked')).click();
19880                   expect(element(by.css('button')).getAttribute('disabled')).toBeTruthy();
19881                 });
19882               </file>
19883             </example>
19884          *
19885          * @element INPUT
19886          * @param {expression} ngDisabled If the {@link guide/expression expression} is truthy,
19887          *     then the `disabled` attribute will be set on the element
19888          */
19889
19890
19891         /**
19892          * @ngdoc directive
19893          * @name ngChecked
19894          * @restrict A
19895          * @priority 100
19896          *
19897          * @description
19898          * Sets the `checked` attribute on the element, if the expression inside `ngChecked` is truthy.
19899          *
19900          * Note that this directive should not be used together with {@link ngModel `ngModel`},
19901          * as this can lead to unexpected behavior.
19902          *
19903          * ### Why do we need `ngChecked`?
19904          *
19905          * The HTML specification does not require browsers to preserve the values of boolean attributes
19906          * such as checked. (Their presence means true and their absence means false.)
19907          * If we put an Angular interpolation expression into such an attribute then the
19908          * binding information would be lost when the browser removes the attribute.
19909          * The `ngChecked` directive solves this problem for the `checked` attribute.
19910          * This complementary directive is not removed by the browser and so provides
19911          * a permanent reliable place to store the binding information.
19912          * @example
19913             <example>
19914               <file name="index.html">
19915                 <label>Check me to check both: <input type="checkbox" ng-model="master"></label><br/>
19916                 <input id="checkSlave" type="checkbox" ng-checked="master" aria-label="Slave input">
19917               </file>
19918               <file name="protractor.js" type="protractor">
19919                 it('should check both checkBoxes', function() {
19920                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeFalsy();
19921                   element(by.model('master')).click();
19922                   expect(element(by.id('checkSlave')).getAttribute('checked')).toBeTruthy();
19923                 });
19924               </file>
19925             </example>
19926          *
19927          * @element INPUT
19928          * @param {expression} ngChecked If the {@link guide/expression expression} is truthy,
19929          *     then the `checked` attribute will be set on the element
19930          */
19931
19932
19933         /**
19934          * @ngdoc directive
19935          * @name ngReadonly
19936          * @restrict A
19937          * @priority 100
19938          *
19939          * @description
19940          * The HTML specification does not require browsers to preserve the values of boolean attributes
19941          * such as readonly. (Their presence means true and their absence means false.)
19942          * If we put an Angular interpolation expression into such an attribute then the
19943          * binding information would be lost when the browser removes the attribute.
19944          * The `ngReadonly` directive solves this problem for the `readonly` attribute.
19945          * This complementary directive is not removed by the browser and so provides
19946          * a permanent reliable place to store the binding information.
19947          * @example
19948             <example>
19949               <file name="index.html">
19950                 <label>Check me to make text readonly: <input type="checkbox" ng-model="checked"></label><br/>
19951                 <input type="text" ng-readonly="checked" value="I'm Angular" aria-label="Readonly field" />
19952               </file>
19953               <file name="protractor.js" type="protractor">
19954                 it('should toggle readonly attr', function() {
19955                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeFalsy();
19956                   element(by.model('checked')).click();
19957                   expect(element(by.css('[type="text"]')).getAttribute('readonly')).toBeTruthy();
19958                 });
19959               </file>
19960             </example>
19961          *
19962          * @element INPUT
19963          * @param {expression} ngReadonly If the {@link guide/expression expression} is truthy,
19964          *     then special attribute "readonly" will be set on the element
19965          */
19966
19967
19968         /**
19969          * @ngdoc directive
19970          * @name ngSelected
19971          * @restrict A
19972          * @priority 100
19973          *
19974          * @description
19975          * The HTML specification does not require browsers to preserve the values of boolean attributes
19976          * such as selected. (Their presence means true and their absence means false.)
19977          * If we put an Angular interpolation expression into such an attribute then the
19978          * binding information would be lost when the browser removes the attribute.
19979          * The `ngSelected` directive solves this problem for the `selected` attribute.
19980          * This complementary directive is not removed by the browser and so provides
19981          * a permanent reliable place to store the binding information.
19982          *
19983          * @example
19984             <example>
19985               <file name="index.html">
19986                 <label>Check me to select: <input type="checkbox" ng-model="selected"></label><br/>
19987                 <select aria-label="ngSelected demo">
19988                   <option>Hello!</option>
19989                   <option id="greet" ng-selected="selected">Greetings!</option>
19990                 </select>
19991               </file>
19992               <file name="protractor.js" type="protractor">
19993                 it('should select Greetings!', function() {
19994                   expect(element(by.id('greet')).getAttribute('selected')).toBeFalsy();
19995                   element(by.model('selected')).click();
19996                   expect(element(by.id('greet')).getAttribute('selected')).toBeTruthy();
19997                 });
19998               </file>
19999             </example>
20000          *
20001          * @element OPTION
20002          * @param {expression} ngSelected If the {@link guide/expression expression} is truthy,
20003          *     then special attribute "selected" will be set on the element
20004          */
20005
20006         /**
20007          * @ngdoc directive
20008          * @name ngOpen
20009          * @restrict A
20010          * @priority 100
20011          *
20012          * @description
20013          * The HTML specification does not require browsers to preserve the values of boolean attributes
20014          * such as open. (Their presence means true and their absence means false.)
20015          * If we put an Angular interpolation expression into such an attribute then the
20016          * binding information would be lost when the browser removes the attribute.
20017          * The `ngOpen` directive solves this problem for the `open` attribute.
20018          * This complementary directive is not removed by the browser and so provides
20019          * a permanent reliable place to store the binding information.
20020          * @example
20021              <example>
20022                <file name="index.html">
20023                  <label>Check me check multiple: <input type="checkbox" ng-model="open"></label><br/>
20024                  <details id="details" ng-open="open">
20025                     <summary>Show/Hide me</summary>
20026                  </details>
20027                </file>
20028                <file name="protractor.js" type="protractor">
20029                  it('should toggle open', function() {
20030                    expect(element(by.id('details')).getAttribute('open')).toBeFalsy();
20031                    element(by.model('open')).click();
20032                    expect(element(by.id('details')).getAttribute('open')).toBeTruthy();
20033                  });
20034                </file>
20035              </example>
20036          *
20037          * @element DETAILS
20038          * @param {expression} ngOpen If the {@link guide/expression expression} is truthy,
20039          *     then special attribute "open" will be set on the element
20040          */
20041
20042         var ngAttributeAliasDirectives = {};
20043
20044         // boolean attrs are evaluated
20045         forEach(BOOLEAN_ATTR, function(propName, attrName) {
20046           // binding to multiple is not supported
20047           if (propName == "multiple") return;
20048
20049           function defaultLinkFn(scope, element, attr) {
20050             scope.$watch(attr[normalized], function ngBooleanAttrWatchAction(value) {
20051               attr.$set(attrName, !!value);
20052             });
20053           }
20054
20055           var normalized = directiveNormalize('ng-' + attrName);
20056           var linkFn = defaultLinkFn;
20057
20058           if (propName === 'checked') {
20059             linkFn = function(scope, element, attr) {
20060               // ensuring ngChecked doesn't interfere with ngModel when both are set on the same input
20061               if (attr.ngModel !== attr[normalized]) {
20062                 defaultLinkFn(scope, element, attr);
20063               }
20064             };
20065           }
20066
20067           ngAttributeAliasDirectives[normalized] = function() {
20068             return {
20069               restrict: 'A',
20070               priority: 100,
20071               link: linkFn
20072             };
20073           };
20074         });
20075
20076         // aliased input attrs are evaluated
20077         forEach(ALIASED_ATTR, function(htmlAttr, ngAttr) {
20078           ngAttributeAliasDirectives[ngAttr] = function() {
20079             return {
20080               priority: 100,
20081               link: function(scope, element, attr) {
20082                 //special case ngPattern when a literal regular expression value
20083                 //is used as the expression (this way we don't have to watch anything).
20084                 if (ngAttr === "ngPattern" && attr.ngPattern.charAt(0) == "/") {
20085                   var match = attr.ngPattern.match(REGEX_STRING_REGEXP);
20086                   if (match) {
20087                     attr.$set("ngPattern", new RegExp(match[1], match[2]));
20088                     return;
20089                   }
20090                 }
20091
20092                 scope.$watch(attr[ngAttr], function ngAttrAliasWatchAction(value) {
20093                   attr.$set(ngAttr, value);
20094                 });
20095               }
20096             };
20097           };
20098         });
20099
20100         // ng-src, ng-srcset, ng-href are interpolated
20101         forEach(['src', 'srcset', 'href'], function(attrName) {
20102           var normalized = directiveNormalize('ng-' + attrName);
20103           ngAttributeAliasDirectives[normalized] = function() {
20104             return {
20105               priority: 99, // it needs to run after the attributes are interpolated
20106               link: function(scope, element, attr) {
20107                 var propName = attrName,
20108                     name = attrName;
20109
20110                 if (attrName === 'href' &&
20111                     toString.call(element.prop('href')) === '[object SVGAnimatedString]') {
20112                   name = 'xlinkHref';
20113                   attr.$attr[name] = 'xlink:href';
20114                   propName = null;
20115                 }
20116
20117                 attr.$observe(normalized, function(value) {
20118                   if (!value) {
20119                     if (attrName === 'href') {
20120                       attr.$set(name, null);
20121                     }
20122                     return;
20123                   }
20124
20125                   attr.$set(name, value);
20126
20127                   // on IE, if "ng:src" directive declaration is used and "src" attribute doesn't exist
20128                   // then calling element.setAttribute('src', 'foo') doesn't do anything, so we need
20129                   // to set the property as well to achieve the desired effect.
20130                   // we use attr[attrName] value since $set can sanitize the url.
20131                   if (msie && propName) element.prop(propName, attr[name]);
20132                 });
20133               }
20134             };
20135           };
20136         });
20137
20138         /* global -nullFormCtrl, -SUBMITTED_CLASS, addSetValidityMethod: true
20139          */
20140         var nullFormCtrl = {
20141           $addControl: noop,
20142           $$renameControl: nullFormRenameControl,
20143           $removeControl: noop,
20144           $setValidity: noop,
20145           $setDirty: noop,
20146           $setPristine: noop,
20147           $setSubmitted: noop
20148         },
20149         SUBMITTED_CLASS = 'ng-submitted';
20150
20151         function nullFormRenameControl(control, name) {
20152           control.$name = name;
20153         }
20154
20155         /**
20156          * @ngdoc type
20157          * @name form.FormController
20158          *
20159          * @property {boolean} $pristine True if user has not interacted with the form yet.
20160          * @property {boolean} $dirty True if user has already interacted with the form.
20161          * @property {boolean} $valid True if all of the containing forms and controls are valid.
20162          * @property {boolean} $invalid True if at least one containing control or form is invalid.
20163          * @property {boolean} $pending True if at least one containing control or form is pending.
20164          * @property {boolean} $submitted True if user has submitted the form even if its invalid.
20165          *
20166          * @property {Object} $error Is an object hash, containing references to controls or
20167          *  forms with failing validators, where:
20168          *
20169          *  - keys are validation tokens (error names),
20170          *  - values are arrays of controls or forms that have a failing validator for given error name.
20171          *
20172          *  Built-in validation tokens:
20173          *
20174          *  - `email`
20175          *  - `max`
20176          *  - `maxlength`
20177          *  - `min`
20178          *  - `minlength`
20179          *  - `number`
20180          *  - `pattern`
20181          *  - `required`
20182          *  - `url`
20183          *  - `date`
20184          *  - `datetimelocal`
20185          *  - `time`
20186          *  - `week`
20187          *  - `month`
20188          *
20189          * @description
20190          * `FormController` keeps track of all its controls and nested forms as well as the state of them,
20191          * such as being valid/invalid or dirty/pristine.
20192          *
20193          * Each {@link ng.directive:form form} directive creates an instance
20194          * of `FormController`.
20195          *
20196          */
20197         //asks for $scope to fool the BC controller module
20198         FormController.$inject = ['$element', '$attrs', '$scope', '$animate', '$interpolate'];
20199         function FormController(element, attrs, $scope, $animate, $interpolate) {
20200           var form = this,
20201               controls = [];
20202
20203           // init state
20204           form.$error = {};
20205           form.$$success = {};
20206           form.$pending = undefined;
20207           form.$name = $interpolate(attrs.name || attrs.ngForm || '')($scope);
20208           form.$dirty = false;
20209           form.$pristine = true;
20210           form.$valid = true;
20211           form.$invalid = false;
20212           form.$submitted = false;
20213           form.$$parentForm = nullFormCtrl;
20214
20215           /**
20216            * @ngdoc method
20217            * @name form.FormController#$rollbackViewValue
20218            *
20219            * @description
20220            * Rollback all form controls pending updates to the `$modelValue`.
20221            *
20222            * Updates may be pending by a debounced event or because the input is waiting for a some future
20223            * event defined in `ng-model-options`. This method is typically needed by the reset button of
20224            * a form that uses `ng-model-options` to pend updates.
20225            */
20226           form.$rollbackViewValue = function() {
20227             forEach(controls, function(control) {
20228               control.$rollbackViewValue();
20229             });
20230           };
20231
20232           /**
20233            * @ngdoc method
20234            * @name form.FormController#$commitViewValue
20235            *
20236            * @description
20237            * Commit all form controls pending updates to the `$modelValue`.
20238            *
20239            * Updates may be pending by a debounced event or because the input is waiting for a some future
20240            * event defined in `ng-model-options`. This method is rarely needed as `NgModelController`
20241            * usually handles calling this in response to input events.
20242            */
20243           form.$commitViewValue = function() {
20244             forEach(controls, function(control) {
20245               control.$commitViewValue();
20246             });
20247           };
20248
20249           /**
20250            * @ngdoc method
20251            * @name form.FormController#$addControl
20252            * @param {object} control control object, either a {@link form.FormController} or an
20253            * {@link ngModel.NgModelController}
20254            *
20255            * @description
20256            * Register a control with the form. Input elements using ngModelController do this automatically
20257            * when they are linked.
20258            *
20259            * Note that the current state of the control will not be reflected on the new parent form. This
20260            * is not an issue with normal use, as freshly compiled and linked controls are in a `$pristine`
20261            * state.
20262            *
20263            * However, if the method is used programmatically, for example by adding dynamically created controls,
20264            * or controls that have been previously removed without destroying their corresponding DOM element,
20265            * it's the developers responsiblity to make sure the current state propagates to the parent form.
20266            *
20267            * For example, if an input control is added that is already `$dirty` and has `$error` properties,
20268            * calling `$setDirty()` and `$validate()` afterwards will propagate the state to the parent form.
20269            */
20270           form.$addControl = function(control) {
20271             // Breaking change - before, inputs whose name was "hasOwnProperty" were quietly ignored
20272             // and not added to the scope.  Now we throw an error.
20273             assertNotHasOwnProperty(control.$name, 'input');
20274             controls.push(control);
20275
20276             if (control.$name) {
20277               form[control.$name] = control;
20278             }
20279
20280             control.$$parentForm = form;
20281           };
20282
20283           // Private API: rename a form control
20284           form.$$renameControl = function(control, newName) {
20285             var oldName = control.$name;
20286
20287             if (form[oldName] === control) {
20288               delete form[oldName];
20289             }
20290             form[newName] = control;
20291             control.$name = newName;
20292           };
20293
20294           /**
20295            * @ngdoc method
20296            * @name form.FormController#$removeControl
20297            * @param {object} control control object, either a {@link form.FormController} or an
20298            * {@link ngModel.NgModelController}
20299            *
20300            * @description
20301            * Deregister a control from the form.
20302            *
20303            * Input elements using ngModelController do this automatically when they are destroyed.
20304            *
20305            * Note that only the removed control's validation state (`$errors`etc.) will be removed from the
20306            * form. `$dirty`, `$submitted` states will not be changed, because the expected behavior can be
20307            * different from case to case. For example, removing the only `$dirty` control from a form may or
20308            * may not mean that the form is still `$dirty`.
20309            */
20310           form.$removeControl = function(control) {
20311             if (control.$name && form[control.$name] === control) {
20312               delete form[control.$name];
20313             }
20314             forEach(form.$pending, function(value, name) {
20315               form.$setValidity(name, null, control);
20316             });
20317             forEach(form.$error, function(value, name) {
20318               form.$setValidity(name, null, control);
20319             });
20320             forEach(form.$$success, function(value, name) {
20321               form.$setValidity(name, null, control);
20322             });
20323
20324             arrayRemove(controls, control);
20325             control.$$parentForm = nullFormCtrl;
20326           };
20327
20328
20329           /**
20330            * @ngdoc method
20331            * @name form.FormController#$setValidity
20332            *
20333            * @description
20334            * Sets the validity of a form control.
20335            *
20336            * This method will also propagate to parent forms.
20337            */
20338           addSetValidityMethod({
20339             ctrl: this,
20340             $element: element,
20341             set: function(object, property, controller) {
20342               var list = object[property];
20343               if (!list) {
20344                 object[property] = [controller];
20345               } else {
20346                 var index = list.indexOf(controller);
20347                 if (index === -1) {
20348                   list.push(controller);
20349                 }
20350               }
20351             },
20352             unset: function(object, property, controller) {
20353               var list = object[property];
20354               if (!list) {
20355                 return;
20356               }
20357               arrayRemove(list, controller);
20358               if (list.length === 0) {
20359                 delete object[property];
20360               }
20361             },
20362             $animate: $animate
20363           });
20364
20365           /**
20366            * @ngdoc method
20367            * @name form.FormController#$setDirty
20368            *
20369            * @description
20370            * Sets the form to a dirty state.
20371            *
20372            * This method can be called to add the 'ng-dirty' class and set the form to a dirty
20373            * state (ng-dirty class). This method will also propagate to parent forms.
20374            */
20375           form.$setDirty = function() {
20376             $animate.removeClass(element, PRISTINE_CLASS);
20377             $animate.addClass(element, DIRTY_CLASS);
20378             form.$dirty = true;
20379             form.$pristine = false;
20380             form.$$parentForm.$setDirty();
20381           };
20382
20383           /**
20384            * @ngdoc method
20385            * @name form.FormController#$setPristine
20386            *
20387            * @description
20388            * Sets the form to its pristine state.
20389            *
20390            * This method can be called to remove the 'ng-dirty' class and set the form to its pristine
20391            * state (ng-pristine class). This method will also propagate to all the controls contained
20392            * in this form.
20393            *
20394            * Setting a form back to a pristine state is often useful when we want to 'reuse' a form after
20395            * saving or resetting it.
20396            */
20397           form.$setPristine = function() {
20398             $animate.setClass(element, PRISTINE_CLASS, DIRTY_CLASS + ' ' + SUBMITTED_CLASS);
20399             form.$dirty = false;
20400             form.$pristine = true;
20401             form.$submitted = false;
20402             forEach(controls, function(control) {
20403               control.$setPristine();
20404             });
20405           };
20406
20407           /**
20408            * @ngdoc method
20409            * @name form.FormController#$setUntouched
20410            *
20411            * @description
20412            * Sets the form to its untouched state.
20413            *
20414            * This method can be called to remove the 'ng-touched' class and set the form controls to their
20415            * untouched state (ng-untouched class).
20416            *
20417            * Setting a form controls back to their untouched state is often useful when setting the form
20418            * back to its pristine state.
20419            */
20420           form.$setUntouched = function() {
20421             forEach(controls, function(control) {
20422               control.$setUntouched();
20423             });
20424           };
20425
20426           /**
20427            * @ngdoc method
20428            * @name form.FormController#$setSubmitted
20429            *
20430            * @description
20431            * Sets the form to its submitted state.
20432            */
20433           form.$setSubmitted = function() {
20434             $animate.addClass(element, SUBMITTED_CLASS);
20435             form.$submitted = true;
20436             form.$$parentForm.$setSubmitted();
20437           };
20438         }
20439
20440         /**
20441          * @ngdoc directive
20442          * @name ngForm
20443          * @restrict EAC
20444          *
20445          * @description
20446          * Nestable alias of {@link ng.directive:form `form`} directive. HTML
20447          * does not allow nesting of form elements. It is useful to nest forms, for example if the validity of a
20448          * sub-group of controls needs to be determined.
20449          *
20450          * Note: the purpose of `ngForm` is to group controls,
20451          * but not to be a replacement for the `<form>` tag with all of its capabilities
20452          * (e.g. posting to the server, ...).
20453          *
20454          * @param {string=} ngForm|name Name of the form. If specified, the form controller will be published into
20455          *                       related scope, under this name.
20456          *
20457          */
20458
20459          /**
20460          * @ngdoc directive
20461          * @name form
20462          * @restrict E
20463          *
20464          * @description
20465          * Directive that instantiates
20466          * {@link form.FormController FormController}.
20467          *
20468          * If the `name` attribute is specified, the form controller is published onto the current scope under
20469          * this name.
20470          *
20471          * # Alias: {@link ng.directive:ngForm `ngForm`}
20472          *
20473          * In Angular, forms can be nested. This means that the outer form is valid when all of the child
20474          * forms are valid as well. However, browsers do not allow nesting of `<form>` elements, so
20475          * Angular provides the {@link ng.directive:ngForm `ngForm`} directive which behaves identically to
20476          * `<form>` but can be nested.  This allows you to have nested forms, which is very useful when
20477          * using Angular validation directives in forms that are dynamically generated using the
20478          * {@link ng.directive:ngRepeat `ngRepeat`} directive. Since you cannot dynamically generate the `name`
20479          * attribute of input elements using interpolation, you have to wrap each set of repeated inputs in an
20480          * `ngForm` directive and nest these in an outer `form` element.
20481          *
20482          *
20483          * # CSS classes
20484          *  - `ng-valid` is set if the form is valid.
20485          *  - `ng-invalid` is set if the form is invalid.
20486          *  - `ng-pending` is set if the form is pending.
20487          *  - `ng-pristine` is set if the form is pristine.
20488          *  - `ng-dirty` is set if the form is dirty.
20489          *  - `ng-submitted` is set if the form was submitted.
20490          *
20491          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
20492          *
20493          *
20494          * # Submitting a form and preventing the default action
20495          *
20496          * Since the role of forms in client-side Angular applications is different than in classical
20497          * roundtrip apps, it is desirable for the browser not to translate the form submission into a full
20498          * page reload that sends the data to the server. Instead some javascript logic should be triggered
20499          * to handle the form submission in an application-specific way.
20500          *
20501          * For this reason, Angular prevents the default action (form submission to the server) unless the
20502          * `<form>` element has an `action` attribute specified.
20503          *
20504          * You can use one of the following two ways to specify what javascript method should be called when
20505          * a form is submitted:
20506          *
20507          * - {@link ng.directive:ngSubmit ngSubmit} directive on the form element
20508          * - {@link ng.directive:ngClick ngClick} directive on the first
20509           *  button or input field of type submit (input[type=submit])
20510          *
20511          * To prevent double execution of the handler, use only one of the {@link ng.directive:ngSubmit ngSubmit}
20512          * or {@link ng.directive:ngClick ngClick} directives.
20513          * This is because of the following form submission rules in the HTML specification:
20514          *
20515          * - If a form has only one input field then hitting enter in this field triggers form submit
20516          * (`ngSubmit`)
20517          * - if a form has 2+ input fields and no buttons or input[type=submit] then hitting enter
20518          * doesn't trigger submit
20519          * - if a form has one or more input fields and one or more buttons or input[type=submit] then
20520          * hitting enter in any of the input fields will trigger the click handler on the *first* button or
20521          * input[type=submit] (`ngClick`) *and* a submit handler on the enclosing form (`ngSubmit`)
20522          *
20523          * Any pending `ngModelOptions` changes will take place immediately when an enclosing form is
20524          * submitted. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
20525          * to have access to the updated model.
20526          *
20527          * ## Animation Hooks
20528          *
20529          * Animations in ngForm are triggered when any of the associated CSS classes are added and removed.
20530          * These classes are: `.ng-pristine`, `.ng-dirty`, `.ng-invalid` and `.ng-valid` as well as any
20531          * other validations that are performed within the form. Animations in ngForm are similar to how
20532          * they work in ngClass and animations can be hooked into using CSS transitions, keyframes as well
20533          * as JS animations.
20534          *
20535          * The following example shows a simple way to utilize CSS transitions to style a form element
20536          * that has been rendered as invalid after it has been validated:
20537          *
20538          * <pre>
20539          * //be sure to include ngAnimate as a module to hook into more
20540          * //advanced animations
20541          * .my-form {
20542          *   transition:0.5s linear all;
20543          *   background: white;
20544          * }
20545          * .my-form.ng-invalid {
20546          *   background: red;
20547          *   color:white;
20548          * }
20549          * </pre>
20550          *
20551          * @example
20552             <example deps="angular-animate.js" animations="true" fixBase="true" module="formExample">
20553               <file name="index.html">
20554                <script>
20555                  angular.module('formExample', [])
20556                    .controller('FormController', ['$scope', function($scope) {
20557                      $scope.userType = 'guest';
20558                    }]);
20559                </script>
20560                <style>
20561                 .my-form {
20562                   transition:all linear 0.5s;
20563                   background: transparent;
20564                 }
20565                 .my-form.ng-invalid {
20566                   background: red;
20567                 }
20568                </style>
20569                <form name="myForm" ng-controller="FormController" class="my-form">
20570                  userType: <input name="input" ng-model="userType" required>
20571                  <span class="error" ng-show="myForm.input.$error.required">Required!</span><br>
20572                  <code>userType = {{userType}}</code><br>
20573                  <code>myForm.input.$valid = {{myForm.input.$valid}}</code><br>
20574                  <code>myForm.input.$error = {{myForm.input.$error}}</code><br>
20575                  <code>myForm.$valid = {{myForm.$valid}}</code><br>
20576                  <code>myForm.$error.required = {{!!myForm.$error.required}}</code><br>
20577                 </form>
20578               </file>
20579               <file name="protractor.js" type="protractor">
20580                 it('should initialize to model', function() {
20581                   var userType = element(by.binding('userType'));
20582                   var valid = element(by.binding('myForm.input.$valid'));
20583
20584                   expect(userType.getText()).toContain('guest');
20585                   expect(valid.getText()).toContain('true');
20586                 });
20587
20588                 it('should be invalid if empty', function() {
20589                   var userType = element(by.binding('userType'));
20590                   var valid = element(by.binding('myForm.input.$valid'));
20591                   var userInput = element(by.model('userType'));
20592
20593                   userInput.clear();
20594                   userInput.sendKeys('');
20595
20596                   expect(userType.getText()).toEqual('userType =');
20597                   expect(valid.getText()).toContain('false');
20598                 });
20599               </file>
20600             </example>
20601          *
20602          * @param {string=} name Name of the form. If specified, the form controller will be published into
20603          *                       related scope, under this name.
20604          */
20605         var formDirectiveFactory = function(isNgForm) {
20606           return ['$timeout', '$parse', function($timeout, $parse) {
20607             var formDirective = {
20608               name: 'form',
20609               restrict: isNgForm ? 'EAC' : 'E',
20610               require: ['form', '^^?form'], //first is the form's own ctrl, second is an optional parent form
20611               controller: FormController,
20612               compile: function ngFormCompile(formElement, attr) {
20613                 // Setup initial state of the control
20614                 formElement.addClass(PRISTINE_CLASS).addClass(VALID_CLASS);
20615
20616                 var nameAttr = attr.name ? 'name' : (isNgForm && attr.ngForm ? 'ngForm' : false);
20617
20618                 return {
20619                   pre: function ngFormPreLink(scope, formElement, attr, ctrls) {
20620                     var controller = ctrls[0];
20621
20622                     // if `action` attr is not present on the form, prevent the default action (submission)
20623                     if (!('action' in attr)) {
20624                       // we can't use jq events because if a form is destroyed during submission the default
20625                       // action is not prevented. see #1238
20626                       //
20627                       // IE 9 is not affected because it doesn't fire a submit event and try to do a full
20628                       // page reload if the form was destroyed by submission of the form via a click handler
20629                       // on a button in the form. Looks like an IE9 specific bug.
20630                       var handleFormSubmission = function(event) {
20631                         scope.$apply(function() {
20632                           controller.$commitViewValue();
20633                           controller.$setSubmitted();
20634                         });
20635
20636                         event.preventDefault();
20637                       };
20638
20639                       addEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20640
20641                       // unregister the preventDefault listener so that we don't not leak memory but in a
20642                       // way that will achieve the prevention of the default action.
20643                       formElement.on('$destroy', function() {
20644                         $timeout(function() {
20645                           removeEventListenerFn(formElement[0], 'submit', handleFormSubmission);
20646                         }, 0, false);
20647                       });
20648                     }
20649
20650                     var parentFormCtrl = ctrls[1] || controller.$$parentForm;
20651                     parentFormCtrl.$addControl(controller);
20652
20653                     var setter = nameAttr ? getSetter(controller.$name) : noop;
20654
20655                     if (nameAttr) {
20656                       setter(scope, controller);
20657                       attr.$observe(nameAttr, function(newValue) {
20658                         if (controller.$name === newValue) return;
20659                         setter(scope, undefined);
20660                         controller.$$parentForm.$$renameControl(controller, newValue);
20661                         setter = getSetter(controller.$name);
20662                         setter(scope, controller);
20663                       });
20664                     }
20665                     formElement.on('$destroy', function() {
20666                       controller.$$parentForm.$removeControl(controller);
20667                       setter(scope, undefined);
20668                       extend(controller, nullFormCtrl); //stop propagating child destruction handlers upwards
20669                     });
20670                   }
20671                 };
20672               }
20673             };
20674
20675             return formDirective;
20676
20677             function getSetter(expression) {
20678               if (expression === '') {
20679                 //create an assignable expression, so forms with an empty name can be renamed later
20680                 return $parse('this[""]').assign;
20681               }
20682               return $parse(expression).assign || noop;
20683             }
20684           }];
20685         };
20686
20687         var formDirective = formDirectiveFactory();
20688         var ngFormDirective = formDirectiveFactory(true);
20689
20690         /* global VALID_CLASS: false,
20691           INVALID_CLASS: false,
20692           PRISTINE_CLASS: false,
20693           DIRTY_CLASS: false,
20694           UNTOUCHED_CLASS: false,
20695           TOUCHED_CLASS: false,
20696           ngModelMinErr: false,
20697         */
20698
20699         // Regex code is obtained from SO: https://stackoverflow.com/questions/3143070/javascript-regex-iso-datetime#answer-3143231
20700         var 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)/;
20701         // See valid URLs in RFC3987 (http://tools.ietf.org/html/rfc3987)
20702         var URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/;
20703         var EMAIL_REGEXP = /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i;
20704         var NUMBER_REGEXP = /^\s*(\-|\+)?(\d+|(\d*(\.\d*)))([eE][+-]?\d+)?\s*$/;
20705         var DATE_REGEXP = /^(\d{4})-(\d{2})-(\d{2})$/;
20706         var DATETIMELOCAL_REGEXP = /^(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20707         var WEEK_REGEXP = /^(\d{4})-W(\d\d)$/;
20708         var MONTH_REGEXP = /^(\d{4})-(\d\d)$/;
20709         var TIME_REGEXP = /^(\d\d):(\d\d)(?::(\d\d)(\.\d{1,3})?)?$/;
20710
20711         var inputType = {
20712
20713           /**
20714            * @ngdoc input
20715            * @name input[text]
20716            *
20717            * @description
20718            * Standard HTML text input with angular data binding, inherited by most of the `input` elements.
20719            *
20720            *
20721            * @param {string} ngModel Assignable angular expression to data-bind to.
20722            * @param {string=} name Property name of the form under which the control is published.
20723            * @param {string=} required Adds `required` validation error key if the value is not entered.
20724            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20725            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20726            *    `required` when you want to data-bind to the `required` attribute.
20727            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
20728            *    minlength.
20729            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
20730            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
20731            *    any length.
20732            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
20733            *    that contains the regular expression body that will be converted to a regular expression
20734            *    as in the ngPattern directive.
20735            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
20736            *    a RegExp found by evaluating the Angular expression given in the attribute value.
20737            *    If the expression evaluates to a RegExp object, then this is used directly.
20738            *    If the expression evaluates to a string, then it will be converted to a RegExp
20739            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
20740            *    `new RegExp('^abc$')`.<br />
20741            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
20742            *    start at the index of the last search's match, thus not taking the whole input value into
20743            *    account.
20744            * @param {string=} ngChange Angular expression to be executed when input changes due to user
20745            *    interaction with the input element.
20746            * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
20747            *    This parameter is ignored for input[type=password] controls, which will never trim the
20748            *    input.
20749            *
20750            * @example
20751               <example name="text-input-directive" module="textInputExample">
20752                 <file name="index.html">
20753                  <script>
20754                    angular.module('textInputExample', [])
20755                      .controller('ExampleController', ['$scope', function($scope) {
20756                        $scope.example = {
20757                          text: 'guest',
20758                          word: /^\s*\w*\s*$/
20759                        };
20760                      }]);
20761                  </script>
20762                  <form name="myForm" ng-controller="ExampleController">
20763                    <label>Single word:
20764                      <input type="text" name="input" ng-model="example.text"
20765                             ng-pattern="example.word" required ng-trim="false">
20766                    </label>
20767                    <div role="alert">
20768                      <span class="error" ng-show="myForm.input.$error.required">
20769                        Required!</span>
20770                      <span class="error" ng-show="myForm.input.$error.pattern">
20771                        Single word only!</span>
20772                    </div>
20773                    <tt>text = {{example.text}}</tt><br/>
20774                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20775                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20776                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20777                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20778                   </form>
20779                 </file>
20780                 <file name="protractor.js" type="protractor">
20781                   var text = element(by.binding('example.text'));
20782                   var valid = element(by.binding('myForm.input.$valid'));
20783                   var input = element(by.model('example.text'));
20784
20785                   it('should initialize to model', function() {
20786                     expect(text.getText()).toContain('guest');
20787                     expect(valid.getText()).toContain('true');
20788                   });
20789
20790                   it('should be invalid if empty', function() {
20791                     input.clear();
20792                     input.sendKeys('');
20793
20794                     expect(text.getText()).toEqual('text =');
20795                     expect(valid.getText()).toContain('false');
20796                   });
20797
20798                   it('should be invalid if multi word', function() {
20799                     input.clear();
20800                     input.sendKeys('hello world');
20801
20802                     expect(valid.getText()).toContain('false');
20803                   });
20804                 </file>
20805               </example>
20806            */
20807           'text': textInputType,
20808
20809             /**
20810              * @ngdoc input
20811              * @name input[date]
20812              *
20813              * @description
20814              * Input with date validation and transformation. In browsers that do not yet support
20815              * the HTML5 date input, a text element will be used. In that case, text must be entered in a valid ISO-8601
20816              * date format (yyyy-MM-dd), for example: `2009-01-06`. Since many
20817              * modern browsers do not yet support this input type, it is important to provide cues to users on the
20818              * expected input format via a placeholder or label.
20819              *
20820              * The model must always be a Date object, otherwise Angular will throw an error.
20821              * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20822              *
20823              * The timezone to be used to read/write the `Date` instance in the model can be defined using
20824              * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20825              *
20826              * @param {string} ngModel Assignable angular expression to data-bind to.
20827              * @param {string=} name Property name of the form under which the control is published.
20828              * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`. This must be a
20829              *   valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20830              *   (e.g. `min="{{minDate | date:'yyyy-MM-dd'}}"`). Note that `min` will also add native HTML5
20831              *   constraint validation.
20832              * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`. This must be
20833              *   a valid ISO date string (yyyy-MM-dd). You can also use interpolation inside this attribute
20834              *   (e.g. `max="{{maxDate | date:'yyyy-MM-dd'}}"`). Note that `max` will also add native HTML5
20835              *   constraint validation.
20836              * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO date string
20837              *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20838              * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO date string
20839              *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20840              * @param {string=} required Sets `required` validation error key if the value is not entered.
20841              * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20842              *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20843              *    `required` when you want to data-bind to the `required` attribute.
20844              * @param {string=} ngChange Angular expression to be executed when input changes due to user
20845              *    interaction with the input element.
20846              *
20847              * @example
20848              <example name="date-input-directive" module="dateInputExample">
20849              <file name="index.html">
20850                <script>
20851                   angular.module('dateInputExample', [])
20852                     .controller('DateController', ['$scope', function($scope) {
20853                       $scope.example = {
20854                         value: new Date(2013, 9, 22)
20855                       };
20856                     }]);
20857                </script>
20858                <form name="myForm" ng-controller="DateController as dateCtrl">
20859                   <label for="exampleInput">Pick a date in 2013:</label>
20860                   <input type="date" id="exampleInput" name="input" ng-model="example.value"
20861                       placeholder="yyyy-MM-dd" min="2013-01-01" max="2013-12-31" required />
20862                   <div role="alert">
20863                     <span class="error" ng-show="myForm.input.$error.required">
20864                         Required!</span>
20865                     <span class="error" ng-show="myForm.input.$error.date">
20866                         Not a valid date!</span>
20867                    </div>
20868                    <tt>value = {{example.value | date: "yyyy-MM-dd"}}</tt><br/>
20869                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20870                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20871                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20872                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20873                </form>
20874              </file>
20875              <file name="protractor.js" type="protractor">
20876                 var value = element(by.binding('example.value | date: "yyyy-MM-dd"'));
20877                 var valid = element(by.binding('myForm.input.$valid'));
20878                 var input = element(by.model('example.value'));
20879
20880                 // currently protractor/webdriver does not support
20881                 // sending keys to all known HTML5 input controls
20882                 // for various browsers (see https://github.com/angular/protractor/issues/562).
20883                 function setInput(val) {
20884                   // set the value of the element and force validation.
20885                   var scr = "var ipt = document.getElementById('exampleInput'); " +
20886                   "ipt.value = '" + val + "';" +
20887                   "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20888                   browser.executeScript(scr);
20889                 }
20890
20891                 it('should initialize to model', function() {
20892                   expect(value.getText()).toContain('2013-10-22');
20893                   expect(valid.getText()).toContain('myForm.input.$valid = true');
20894                 });
20895
20896                 it('should be invalid if empty', function() {
20897                   setInput('');
20898                   expect(value.getText()).toEqual('value =');
20899                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20900                 });
20901
20902                 it('should be invalid if over max', function() {
20903                   setInput('2015-01-01');
20904                   expect(value.getText()).toContain('');
20905                   expect(valid.getText()).toContain('myForm.input.$valid = false');
20906                 });
20907              </file>
20908              </example>
20909              */
20910           'date': createDateInputType('date', DATE_REGEXP,
20911                  createDateParser(DATE_REGEXP, ['yyyy', 'MM', 'dd']),
20912                  'yyyy-MM-dd'),
20913
20914            /**
20915             * @ngdoc input
20916             * @name input[datetime-local]
20917             *
20918             * @description
20919             * Input with datetime validation and transformation. In browsers that do not yet support
20920             * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
20921             * local datetime format (yyyy-MM-ddTHH:mm:ss), for example: `2010-12-28T14:57:00`.
20922             *
20923             * The model must always be a Date object, otherwise Angular will throw an error.
20924             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
20925             *
20926             * The timezone to be used to read/write the `Date` instance in the model can be defined using
20927             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
20928             *
20929             * @param {string} ngModel Assignable angular expression to data-bind to.
20930             * @param {string=} name Property name of the form under which the control is published.
20931             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
20932             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20933             *   inside this attribute (e.g. `min="{{minDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20934             *   Note that `min` will also add native HTML5 constraint validation.
20935             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
20936             *   This must be a valid ISO datetime format (yyyy-MM-ddTHH:mm:ss). You can also use interpolation
20937             *   inside this attribute (e.g. `max="{{maxDatetimeLocal | date:'yyyy-MM-ddTHH:mm:ss'}}"`).
20938             *   Note that `max` will also add native HTML5 constraint validation.
20939             * @param {(date|string)=} ngMin Sets the `min` validation error key to the Date / ISO datetime string
20940             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
20941             * @param {(date|string)=} ngMax Sets the `max` validation error key to the Date / ISO datetime string
20942             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
20943             * @param {string=} required Sets `required` validation error key if the value is not entered.
20944             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
20945             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
20946             *    `required` when you want to data-bind to the `required` attribute.
20947             * @param {string=} ngChange Angular expression to be executed when input changes due to user
20948             *    interaction with the input element.
20949             *
20950             * @example
20951             <example name="datetimelocal-input-directive" module="dateExample">
20952             <file name="index.html">
20953               <script>
20954                 angular.module('dateExample', [])
20955                   .controller('DateController', ['$scope', function($scope) {
20956                     $scope.example = {
20957                       value: new Date(2010, 11, 28, 14, 57)
20958                     };
20959                   }]);
20960               </script>
20961               <form name="myForm" ng-controller="DateController as dateCtrl">
20962                 <label for="exampleInput">Pick a date between in 2013:</label>
20963                 <input type="datetime-local" id="exampleInput" name="input" ng-model="example.value"
20964                     placeholder="yyyy-MM-ddTHH:mm:ss" min="2001-01-01T00:00:00" max="2013-12-31T00:00:00" required />
20965                 <div role="alert">
20966                   <span class="error" ng-show="myForm.input.$error.required">
20967                       Required!</span>
20968                   <span class="error" ng-show="myForm.input.$error.datetimelocal">
20969                       Not a valid date!</span>
20970                 </div>
20971                 <tt>value = {{example.value | date: "yyyy-MM-ddTHH:mm:ss"}}</tt><br/>
20972                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
20973                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
20974                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
20975                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
20976               </form>
20977             </file>
20978             <file name="protractor.js" type="protractor">
20979               var value = element(by.binding('example.value | date: "yyyy-MM-ddTHH:mm:ss"'));
20980               var valid = element(by.binding('myForm.input.$valid'));
20981               var input = element(by.model('example.value'));
20982
20983               // currently protractor/webdriver does not support
20984               // sending keys to all known HTML5 input controls
20985               // for various browsers (https://github.com/angular/protractor/issues/562).
20986               function setInput(val) {
20987                 // set the value of the element and force validation.
20988                 var scr = "var ipt = document.getElementById('exampleInput'); " +
20989                 "ipt.value = '" + val + "';" +
20990                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
20991                 browser.executeScript(scr);
20992               }
20993
20994               it('should initialize to model', function() {
20995                 expect(value.getText()).toContain('2010-12-28T14:57:00');
20996                 expect(valid.getText()).toContain('myForm.input.$valid = true');
20997               });
20998
20999               it('should be invalid if empty', function() {
21000                 setInput('');
21001                 expect(value.getText()).toEqual('value =');
21002                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21003               });
21004
21005               it('should be invalid if over max', function() {
21006                 setInput('2015-01-01T23:59:00');
21007                 expect(value.getText()).toContain('');
21008                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21009               });
21010             </file>
21011             </example>
21012             */
21013           'datetime-local': createDateInputType('datetimelocal', DATETIMELOCAL_REGEXP,
21014               createDateParser(DATETIMELOCAL_REGEXP, ['yyyy', 'MM', 'dd', 'HH', 'mm', 'ss', 'sss']),
21015               'yyyy-MM-ddTHH:mm:ss.sss'),
21016
21017           /**
21018            * @ngdoc input
21019            * @name input[time]
21020            *
21021            * @description
21022            * Input with time validation and transformation. In browsers that do not yet support
21023            * the HTML5 date input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21024            * local time format (HH:mm:ss), for example: `14:57:00`. Model must be a Date object. This binding will always output a
21025            * Date object to the model of January 1, 1970, or local date `new Date(1970, 0, 1, HH, mm, ss)`.
21026            *
21027            * The model must always be a Date object, otherwise Angular will throw an error.
21028            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21029            *
21030            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21031            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21032            *
21033            * @param {string} ngModel Assignable angular expression to data-bind to.
21034            * @param {string=} name Property name of the form under which the control is published.
21035            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21036            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21037            *   attribute (e.g. `min="{{minTime | date:'HH:mm:ss'}}"`). Note that `min` will also add
21038            *   native HTML5 constraint validation.
21039            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21040            *   This must be a valid ISO time format (HH:mm:ss). You can also use interpolation inside this
21041            *   attribute (e.g. `max="{{maxTime | date:'HH:mm:ss'}}"`). Note that `max` will also add
21042            *   native HTML5 constraint validation.
21043            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO time string the
21044            *   `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21045            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO time string the
21046            *   `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21047            * @param {string=} required Sets `required` validation error key if the value is not entered.
21048            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21049            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21050            *    `required` when you want to data-bind to the `required` attribute.
21051            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21052            *    interaction with the input element.
21053            *
21054            * @example
21055            <example name="time-input-directive" module="timeExample">
21056            <file name="index.html">
21057              <script>
21058               angular.module('timeExample', [])
21059                 .controller('DateController', ['$scope', function($scope) {
21060                   $scope.example = {
21061                     value: new Date(1970, 0, 1, 14, 57, 0)
21062                   };
21063                 }]);
21064              </script>
21065              <form name="myForm" ng-controller="DateController as dateCtrl">
21066                 <label for="exampleInput">Pick a between 8am and 5pm:</label>
21067                 <input type="time" id="exampleInput" name="input" ng-model="example.value"
21068                     placeholder="HH:mm:ss" min="08:00:00" max="17:00:00" required />
21069                 <div role="alert">
21070                   <span class="error" ng-show="myForm.input.$error.required">
21071                       Required!</span>
21072                   <span class="error" ng-show="myForm.input.$error.time">
21073                       Not a valid date!</span>
21074                 </div>
21075                 <tt>value = {{example.value | date: "HH:mm:ss"}}</tt><br/>
21076                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21077                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21078                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21079                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21080              </form>
21081            </file>
21082            <file name="protractor.js" type="protractor">
21083               var value = element(by.binding('example.value | date: "HH:mm:ss"'));
21084               var valid = element(by.binding('myForm.input.$valid'));
21085               var input = element(by.model('example.value'));
21086
21087               // currently protractor/webdriver does not support
21088               // sending keys to all known HTML5 input controls
21089               // for various browsers (https://github.com/angular/protractor/issues/562).
21090               function setInput(val) {
21091                 // set the value of the element and force validation.
21092                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21093                 "ipt.value = '" + val + "';" +
21094                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21095                 browser.executeScript(scr);
21096               }
21097
21098               it('should initialize to model', function() {
21099                 expect(value.getText()).toContain('14:57:00');
21100                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21101               });
21102
21103               it('should be invalid if empty', function() {
21104                 setInput('');
21105                 expect(value.getText()).toEqual('value =');
21106                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21107               });
21108
21109               it('should be invalid if over max', function() {
21110                 setInput('23:59:00');
21111                 expect(value.getText()).toContain('');
21112                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21113               });
21114            </file>
21115            </example>
21116            */
21117           'time': createDateInputType('time', TIME_REGEXP,
21118               createDateParser(TIME_REGEXP, ['HH', 'mm', 'ss', 'sss']),
21119              'HH:mm:ss.sss'),
21120
21121            /**
21122             * @ngdoc input
21123             * @name input[week]
21124             *
21125             * @description
21126             * Input with week-of-the-year validation and transformation to Date. In browsers that do not yet support
21127             * the HTML5 week input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21128             * week format (yyyy-W##), for example: `2013-W02`.
21129             *
21130             * The model must always be a Date object, otherwise Angular will throw an error.
21131             * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21132             *
21133             * The timezone to be used to read/write the `Date` instance in the model can be defined using
21134             * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21135             *
21136             * @param {string} ngModel Assignable angular expression to data-bind to.
21137             * @param {string=} name Property name of the form under which the control is published.
21138             * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21139             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21140             *   attribute (e.g. `min="{{minWeek | date:'yyyy-Www'}}"`). Note that `min` will also add
21141             *   native HTML5 constraint validation.
21142             * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21143             *   This must be a valid ISO week format (yyyy-W##). You can also use interpolation inside this
21144             *   attribute (e.g. `max="{{maxWeek | date:'yyyy-Www'}}"`). Note that `max` will also add
21145             *   native HTML5 constraint validation.
21146             * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21147             *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21148             * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21149             *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21150             * @param {string=} required Sets `required` validation error key if the value is not entered.
21151             * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21152             *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21153             *    `required` when you want to data-bind to the `required` attribute.
21154             * @param {string=} ngChange Angular expression to be executed when input changes due to user
21155             *    interaction with the input element.
21156             *
21157             * @example
21158             <example name="week-input-directive" module="weekExample">
21159             <file name="index.html">
21160               <script>
21161               angular.module('weekExample', [])
21162                 .controller('DateController', ['$scope', function($scope) {
21163                   $scope.example = {
21164                     value: new Date(2013, 0, 3)
21165                   };
21166                 }]);
21167               </script>
21168               <form name="myForm" ng-controller="DateController as dateCtrl">
21169                 <label>Pick a date between in 2013:
21170                   <input id="exampleInput" type="week" name="input" ng-model="example.value"
21171                          placeholder="YYYY-W##" min="2012-W32"
21172                          max="2013-W52" required />
21173                 </label>
21174                 <div role="alert">
21175                   <span class="error" ng-show="myForm.input.$error.required">
21176                       Required!</span>
21177                   <span class="error" ng-show="myForm.input.$error.week">
21178                       Not a valid date!</span>
21179                 </div>
21180                 <tt>value = {{example.value | date: "yyyy-Www"}}</tt><br/>
21181                 <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21182                 <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21183                 <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21184                 <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21185               </form>
21186             </file>
21187             <file name="protractor.js" type="protractor">
21188               var value = element(by.binding('example.value | date: "yyyy-Www"'));
21189               var valid = element(by.binding('myForm.input.$valid'));
21190               var input = element(by.model('example.value'));
21191
21192               // currently protractor/webdriver does not support
21193               // sending keys to all known HTML5 input controls
21194               // for various browsers (https://github.com/angular/protractor/issues/562).
21195               function setInput(val) {
21196                 // set the value of the element and force validation.
21197                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21198                 "ipt.value = '" + val + "';" +
21199                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21200                 browser.executeScript(scr);
21201               }
21202
21203               it('should initialize to model', function() {
21204                 expect(value.getText()).toContain('2013-W01');
21205                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21206               });
21207
21208               it('should be invalid if empty', function() {
21209                 setInput('');
21210                 expect(value.getText()).toEqual('value =');
21211                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21212               });
21213
21214               it('should be invalid if over max', function() {
21215                 setInput('2015-W01');
21216                 expect(value.getText()).toContain('');
21217                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21218               });
21219             </file>
21220             </example>
21221             */
21222           'week': createDateInputType('week', WEEK_REGEXP, weekParser, 'yyyy-Www'),
21223
21224           /**
21225            * @ngdoc input
21226            * @name input[month]
21227            *
21228            * @description
21229            * Input with month validation and transformation. In browsers that do not yet support
21230            * the HTML5 month input, a text element will be used. In that case, the text must be entered in a valid ISO-8601
21231            * month format (yyyy-MM), for example: `2009-01`.
21232            *
21233            * The model must always be a Date object, otherwise Angular will throw an error.
21234            * Invalid `Date` objects (dates whose `getTime()` is `NaN`) will be rendered as an empty string.
21235            * If the model is not set to the first of the month, the next view to model update will set it
21236            * to the first of the month.
21237            *
21238            * The timezone to be used to read/write the `Date` instance in the model can be defined using
21239            * {@link ng.directive:ngModelOptions ngModelOptions}. By default, this is the timezone of the browser.
21240            *
21241            * @param {string} ngModel Assignable angular expression to data-bind to.
21242            * @param {string=} name Property name of the form under which the control is published.
21243            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21244            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21245            *   attribute (e.g. `min="{{minMonth | date:'yyyy-MM'}}"`). Note that `min` will also add
21246            *   native HTML5 constraint validation.
21247            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21248            *   This must be a valid ISO month format (yyyy-MM). You can also use interpolation inside this
21249            *   attribute (e.g. `max="{{maxMonth | date:'yyyy-MM'}}"`). Note that `max` will also add
21250            *   native HTML5 constraint validation.
21251            * @param {(date|string)=} ngMin Sets the `min` validation constraint to the Date / ISO week string
21252            *   the `ngMin` expression evaluates to. Note that it does not set the `min` attribute.
21253            * @param {(date|string)=} ngMax Sets the `max` validation constraint to the Date / ISO week string
21254            *   the `ngMax` expression evaluates to. Note that it does not set the `max` attribute.
21255
21256            * @param {string=} required Sets `required` validation error key if the value is not entered.
21257            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21258            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21259            *    `required` when you want to data-bind to the `required` attribute.
21260            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21261            *    interaction with the input element.
21262            *
21263            * @example
21264            <example name="month-input-directive" module="monthExample">
21265            <file name="index.html">
21266              <script>
21267               angular.module('monthExample', [])
21268                 .controller('DateController', ['$scope', function($scope) {
21269                   $scope.example = {
21270                     value: new Date(2013, 9, 1)
21271                   };
21272                 }]);
21273              </script>
21274              <form name="myForm" ng-controller="DateController as dateCtrl">
21275                <label for="exampleInput">Pick a month in 2013:</label>
21276                <input id="exampleInput" type="month" name="input" ng-model="example.value"
21277                   placeholder="yyyy-MM" min="2013-01" max="2013-12" required />
21278                <div role="alert">
21279                  <span class="error" ng-show="myForm.input.$error.required">
21280                     Required!</span>
21281                  <span class="error" ng-show="myForm.input.$error.month">
21282                     Not a valid month!</span>
21283                </div>
21284                <tt>value = {{example.value | date: "yyyy-MM"}}</tt><br/>
21285                <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21286                <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21287                <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21288                <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21289              </form>
21290            </file>
21291            <file name="protractor.js" type="protractor">
21292               var value = element(by.binding('example.value | date: "yyyy-MM"'));
21293               var valid = element(by.binding('myForm.input.$valid'));
21294               var input = element(by.model('example.value'));
21295
21296               // currently protractor/webdriver does not support
21297               // sending keys to all known HTML5 input controls
21298               // for various browsers (https://github.com/angular/protractor/issues/562).
21299               function setInput(val) {
21300                 // set the value of the element and force validation.
21301                 var scr = "var ipt = document.getElementById('exampleInput'); " +
21302                 "ipt.value = '" + val + "';" +
21303                 "angular.element(ipt).scope().$apply(function(s) { s.myForm[ipt.name].$setViewValue('" + val + "'); });";
21304                 browser.executeScript(scr);
21305               }
21306
21307               it('should initialize to model', function() {
21308                 expect(value.getText()).toContain('2013-10');
21309                 expect(valid.getText()).toContain('myForm.input.$valid = true');
21310               });
21311
21312               it('should be invalid if empty', function() {
21313                 setInput('');
21314                 expect(value.getText()).toEqual('value =');
21315                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21316               });
21317
21318               it('should be invalid if over max', function() {
21319                 setInput('2015-01');
21320                 expect(value.getText()).toContain('');
21321                 expect(valid.getText()).toContain('myForm.input.$valid = false');
21322               });
21323            </file>
21324            </example>
21325            */
21326           'month': createDateInputType('month', MONTH_REGEXP,
21327              createDateParser(MONTH_REGEXP, ['yyyy', 'MM']),
21328              'yyyy-MM'),
21329
21330           /**
21331            * @ngdoc input
21332            * @name input[number]
21333            *
21334            * @description
21335            * Text input with number validation and transformation. Sets the `number` validation
21336            * error if not a valid number.
21337            *
21338            * <div class="alert alert-warning">
21339            * The model must always be of type `number` otherwise Angular will throw an error.
21340            * Be aware that a string containing a number is not enough. See the {@link ngModel:numfmt}
21341            * error docs for more information and an example of how to convert your model if necessary.
21342            * </div>
21343            *
21344            * ## Issues with HTML5 constraint validation
21345            *
21346            * In browsers that follow the
21347            * [HTML5 specification](https://html.spec.whatwg.org/multipage/forms.html#number-state-%28type=number%29),
21348            * `input[number]` does not work as expected with {@link ngModelOptions `ngModelOptions.allowInvalid`}.
21349            * If a non-number is entered in the input, the browser will report the value as an empty string,
21350            * which means the view / model values in `ngModel` and subsequently the scope value
21351            * will also be an empty string.
21352            *
21353            *
21354            * @param {string} ngModel Assignable angular expression to data-bind to.
21355            * @param {string=} name Property name of the form under which the control is published.
21356            * @param {string=} min Sets the `min` validation error key if the value entered is less than `min`.
21357            * @param {string=} max Sets the `max` validation error key if the value entered is greater than `max`.
21358            * @param {string=} required Sets `required` validation error key if the value is not entered.
21359            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21360            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21361            *    `required` when you want to data-bind to the `required` attribute.
21362            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21363            *    minlength.
21364            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21365            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21366            *    any length.
21367            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21368            *    that contains the regular expression body that will be converted to a regular expression
21369            *    as in the ngPattern directive.
21370            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21371            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21372            *    If the expression evaluates to a RegExp object, then this is used directly.
21373            *    If the expression evaluates to a string, then it will be converted to a RegExp
21374            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21375            *    `new RegExp('^abc$')`.<br />
21376            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21377            *    start at the index of the last search's match, thus not taking the whole input value into
21378            *    account.
21379            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21380            *    interaction with the input element.
21381            *
21382            * @example
21383               <example name="number-input-directive" module="numberExample">
21384                 <file name="index.html">
21385                  <script>
21386                    angular.module('numberExample', [])
21387                      .controller('ExampleController', ['$scope', function($scope) {
21388                        $scope.example = {
21389                          value: 12
21390                        };
21391                      }]);
21392                  </script>
21393                  <form name="myForm" ng-controller="ExampleController">
21394                    <label>Number:
21395                      <input type="number" name="input" ng-model="example.value"
21396                             min="0" max="99" required>
21397                   </label>
21398                    <div role="alert">
21399                      <span class="error" ng-show="myForm.input.$error.required">
21400                        Required!</span>
21401                      <span class="error" ng-show="myForm.input.$error.number">
21402                        Not valid number!</span>
21403                    </div>
21404                    <tt>value = {{example.value}}</tt><br/>
21405                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21406                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21407                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21408                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21409                   </form>
21410                 </file>
21411                 <file name="protractor.js" type="protractor">
21412                   var value = element(by.binding('example.value'));
21413                   var valid = element(by.binding('myForm.input.$valid'));
21414                   var input = element(by.model('example.value'));
21415
21416                   it('should initialize to model', function() {
21417                     expect(value.getText()).toContain('12');
21418                     expect(valid.getText()).toContain('true');
21419                   });
21420
21421                   it('should be invalid if empty', function() {
21422                     input.clear();
21423                     input.sendKeys('');
21424                     expect(value.getText()).toEqual('value =');
21425                     expect(valid.getText()).toContain('false');
21426                   });
21427
21428                   it('should be invalid if over max', function() {
21429                     input.clear();
21430                     input.sendKeys('123');
21431                     expect(value.getText()).toEqual('value =');
21432                     expect(valid.getText()).toContain('false');
21433                   });
21434                 </file>
21435               </example>
21436            */
21437           'number': numberInputType,
21438
21439
21440           /**
21441            * @ngdoc input
21442            * @name input[url]
21443            *
21444            * @description
21445            * Text input with URL validation. Sets the `url` validation error key if the content is not a
21446            * valid URL.
21447            *
21448            * <div class="alert alert-warning">
21449            * **Note:** `input[url]` uses a regex to validate urls that is derived from the regex
21450            * used in Chromium. If you need stricter validation, you can use `ng-pattern` or modify
21451            * the built-in validators (see the {@link guide/forms Forms guide})
21452            * </div>
21453            *
21454            * @param {string} ngModel Assignable angular expression to data-bind to.
21455            * @param {string=} name Property name of the form under which the control is published.
21456            * @param {string=} required Sets `required` validation error key if the value is not entered.
21457            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21458            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21459            *    `required` when you want to data-bind to the `required` attribute.
21460            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21461            *    minlength.
21462            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21463            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21464            *    any length.
21465            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21466            *    that contains the regular expression body that will be converted to a regular expression
21467            *    as in the ngPattern directive.
21468            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21469            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21470            *    If the expression evaluates to a RegExp object, then this is used directly.
21471            *    If the expression evaluates to a string, then it will be converted to a RegExp
21472            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21473            *    `new RegExp('^abc$')`.<br />
21474            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21475            *    start at the index of the last search's match, thus not taking the whole input value into
21476            *    account.
21477            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21478            *    interaction with the input element.
21479            *
21480            * @example
21481               <example name="url-input-directive" module="urlExample">
21482                 <file name="index.html">
21483                  <script>
21484                    angular.module('urlExample', [])
21485                      .controller('ExampleController', ['$scope', function($scope) {
21486                        $scope.url = {
21487                          text: 'http://google.com'
21488                        };
21489                      }]);
21490                  </script>
21491                  <form name="myForm" ng-controller="ExampleController">
21492                    <label>URL:
21493                      <input type="url" name="input" ng-model="url.text" required>
21494                    <label>
21495                    <div role="alert">
21496                      <span class="error" ng-show="myForm.input.$error.required">
21497                        Required!</span>
21498                      <span class="error" ng-show="myForm.input.$error.url">
21499                        Not valid url!</span>
21500                    </div>
21501                    <tt>text = {{url.text}}</tt><br/>
21502                    <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21503                    <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21504                    <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21505                    <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21506                    <tt>myForm.$error.url = {{!!myForm.$error.url}}</tt><br/>
21507                   </form>
21508                 </file>
21509                 <file name="protractor.js" type="protractor">
21510                   var text = element(by.binding('url.text'));
21511                   var valid = element(by.binding('myForm.input.$valid'));
21512                   var input = element(by.model('url.text'));
21513
21514                   it('should initialize to model', function() {
21515                     expect(text.getText()).toContain('http://google.com');
21516                     expect(valid.getText()).toContain('true');
21517                   });
21518
21519                   it('should be invalid if empty', function() {
21520                     input.clear();
21521                     input.sendKeys('');
21522
21523                     expect(text.getText()).toEqual('text =');
21524                     expect(valid.getText()).toContain('false');
21525                   });
21526
21527                   it('should be invalid if not url', function() {
21528                     input.clear();
21529                     input.sendKeys('box');
21530
21531                     expect(valid.getText()).toContain('false');
21532                   });
21533                 </file>
21534               </example>
21535            */
21536           'url': urlInputType,
21537
21538
21539           /**
21540            * @ngdoc input
21541            * @name input[email]
21542            *
21543            * @description
21544            * Text input with email validation. Sets the `email` validation error key if not a valid email
21545            * address.
21546            *
21547            * <div class="alert alert-warning">
21548            * **Note:** `input[email]` uses a regex to validate email addresses that is derived from the regex
21549            * used in Chromium. If you need stricter validation (e.g. requiring a top-level domain), you can
21550            * use `ng-pattern` or modify the built-in validators (see the {@link guide/forms Forms guide})
21551            * </div>
21552            *
21553            * @param {string} ngModel Assignable angular expression to data-bind to.
21554            * @param {string=} name Property name of the form under which the control is published.
21555            * @param {string=} required Sets `required` validation error key if the value is not entered.
21556            * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
21557            *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
21558            *    `required` when you want to data-bind to the `required` attribute.
21559            * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
21560            *    minlength.
21561            * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
21562            *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of
21563            *    any length.
21564            * @param {string=} pattern Similar to `ngPattern` except that the attribute value is the actual string
21565            *    that contains the regular expression body that will be converted to a regular expression
21566            *    as in the ngPattern directive.
21567            * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
21568            *    a RegExp found by evaluating the Angular expression given in the attribute value.
21569            *    If the expression evaluates to a RegExp object, then this is used directly.
21570            *    If the expression evaluates to a string, then it will be converted to a RegExp
21571            *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
21572            *    `new RegExp('^abc$')`.<br />
21573            *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
21574            *    start at the index of the last search's match, thus not taking the whole input value into
21575            *    account.
21576            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21577            *    interaction with the input element.
21578            *
21579            * @example
21580               <example name="email-input-directive" module="emailExample">
21581                 <file name="index.html">
21582                  <script>
21583                    angular.module('emailExample', [])
21584                      .controller('ExampleController', ['$scope', function($scope) {
21585                        $scope.email = {
21586                          text: 'me@example.com'
21587                        };
21588                      }]);
21589                  </script>
21590                    <form name="myForm" ng-controller="ExampleController">
21591                      <label>Email:
21592                        <input type="email" name="input" ng-model="email.text" required>
21593                      </label>
21594                      <div role="alert">
21595                        <span class="error" ng-show="myForm.input.$error.required">
21596                          Required!</span>
21597                        <span class="error" ng-show="myForm.input.$error.email">
21598                          Not valid email!</span>
21599                      </div>
21600                      <tt>text = {{email.text}}</tt><br/>
21601                      <tt>myForm.input.$valid = {{myForm.input.$valid}}</tt><br/>
21602                      <tt>myForm.input.$error = {{myForm.input.$error}}</tt><br/>
21603                      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
21604                      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
21605                      <tt>myForm.$error.email = {{!!myForm.$error.email}}</tt><br/>
21606                    </form>
21607                  </file>
21608                 <file name="protractor.js" type="protractor">
21609                   var text = element(by.binding('email.text'));
21610                   var valid = element(by.binding('myForm.input.$valid'));
21611                   var input = element(by.model('email.text'));
21612
21613                   it('should initialize to model', function() {
21614                     expect(text.getText()).toContain('me@example.com');
21615                     expect(valid.getText()).toContain('true');
21616                   });
21617
21618                   it('should be invalid if empty', function() {
21619                     input.clear();
21620                     input.sendKeys('');
21621                     expect(text.getText()).toEqual('text =');
21622                     expect(valid.getText()).toContain('false');
21623                   });
21624
21625                   it('should be invalid if not email', function() {
21626                     input.clear();
21627                     input.sendKeys('xxx');
21628
21629                     expect(valid.getText()).toContain('false');
21630                   });
21631                 </file>
21632               </example>
21633            */
21634           'email': emailInputType,
21635
21636
21637           /**
21638            * @ngdoc input
21639            * @name input[radio]
21640            *
21641            * @description
21642            * HTML radio button.
21643            *
21644            * @param {string} ngModel Assignable angular expression to data-bind to.
21645            * @param {string} value The value to which the `ngModel` expression should be set when selected.
21646            *    Note that `value` only supports `string` values, i.e. the scope model needs to be a string,
21647            *    too. Use `ngValue` if you need complex models (`number`, `object`, ...).
21648            * @param {string=} name Property name of the form under which the control is published.
21649            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21650            *    interaction with the input element.
21651            * @param {string} ngValue Angular expression to which `ngModel` will be be set when the radio
21652            *    is selected. Should be used instead of the `value` attribute if you need
21653            *    a non-string `ngModel` (`boolean`, `array`, ...).
21654            *
21655            * @example
21656               <example name="radio-input-directive" module="radioExample">
21657                 <file name="index.html">
21658                  <script>
21659                    angular.module('radioExample', [])
21660                      .controller('ExampleController', ['$scope', function($scope) {
21661                        $scope.color = {
21662                          name: 'blue'
21663                        };
21664                        $scope.specialValue = {
21665                          "id": "12345",
21666                          "value": "green"
21667                        };
21668                      }]);
21669                  </script>
21670                  <form name="myForm" ng-controller="ExampleController">
21671                    <label>
21672                      <input type="radio" ng-model="color.name" value="red">
21673                      Red
21674                    </label><br/>
21675                    <label>
21676                      <input type="radio" ng-model="color.name" ng-value="specialValue">
21677                      Green
21678                    </label><br/>
21679                    <label>
21680                      <input type="radio" ng-model="color.name" value="blue">
21681                      Blue
21682                    </label><br/>
21683                    <tt>color = {{color.name | json}}</tt><br/>
21684                   </form>
21685                   Note that `ng-value="specialValue"` sets radio item's value to be the value of `$scope.specialValue`.
21686                 </file>
21687                 <file name="protractor.js" type="protractor">
21688                   it('should change state', function() {
21689                     var color = element(by.binding('color.name'));
21690
21691                     expect(color.getText()).toContain('blue');
21692
21693                     element.all(by.model('color.name')).get(0).click();
21694
21695                     expect(color.getText()).toContain('red');
21696                   });
21697                 </file>
21698               </example>
21699            */
21700           'radio': radioInputType,
21701
21702
21703           /**
21704            * @ngdoc input
21705            * @name input[checkbox]
21706            *
21707            * @description
21708            * HTML checkbox.
21709            *
21710            * @param {string} ngModel Assignable angular expression to data-bind to.
21711            * @param {string=} name Property name of the form under which the control is published.
21712            * @param {expression=} ngTrueValue The value to which the expression should be set when selected.
21713            * @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
21714            * @param {string=} ngChange Angular expression to be executed when input changes due to user
21715            *    interaction with the input element.
21716            *
21717            * @example
21718               <example name="checkbox-input-directive" module="checkboxExample">
21719                 <file name="index.html">
21720                  <script>
21721                    angular.module('checkboxExample', [])
21722                      .controller('ExampleController', ['$scope', function($scope) {
21723                        $scope.checkboxModel = {
21724                         value1 : true,
21725                         value2 : 'YES'
21726                       };
21727                      }]);
21728                  </script>
21729                  <form name="myForm" ng-controller="ExampleController">
21730                    <label>Value1:
21731                      <input type="checkbox" ng-model="checkboxModel.value1">
21732                    </label><br/>
21733                    <label>Value2:
21734                      <input type="checkbox" ng-model="checkboxModel.value2"
21735                             ng-true-value="'YES'" ng-false-value="'NO'">
21736                     </label><br/>
21737                    <tt>value1 = {{checkboxModel.value1}}</tt><br/>
21738                    <tt>value2 = {{checkboxModel.value2}}</tt><br/>
21739                   </form>
21740                 </file>
21741                 <file name="protractor.js" type="protractor">
21742                   it('should change state', function() {
21743                     var value1 = element(by.binding('checkboxModel.value1'));
21744                     var value2 = element(by.binding('checkboxModel.value2'));
21745
21746                     expect(value1.getText()).toContain('true');
21747                     expect(value2.getText()).toContain('YES');
21748
21749                     element(by.model('checkboxModel.value1')).click();
21750                     element(by.model('checkboxModel.value2')).click();
21751
21752                     expect(value1.getText()).toContain('false');
21753                     expect(value2.getText()).toContain('NO');
21754                   });
21755                 </file>
21756               </example>
21757            */
21758           'checkbox': checkboxInputType,
21759
21760           'hidden': noop,
21761           'button': noop,
21762           'submit': noop,
21763           'reset': noop,
21764           'file': noop
21765         };
21766
21767         function stringBasedInputType(ctrl) {
21768           ctrl.$formatters.push(function(value) {
21769             return ctrl.$isEmpty(value) ? value : value.toString();
21770           });
21771         }
21772
21773         function textInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21774           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21775           stringBasedInputType(ctrl);
21776         }
21777
21778         function baseInputType(scope, element, attr, ctrl, $sniffer, $browser) {
21779           var type = lowercase(element[0].type);
21780
21781           // In composition mode, users are still inputing intermediate text buffer,
21782           // hold the listener until composition is done.
21783           // More about composition events: https://developer.mozilla.org/en-US/docs/Web/API/CompositionEvent
21784           if (!$sniffer.android) {
21785             var composing = false;
21786
21787             element.on('compositionstart', function(data) {
21788               composing = true;
21789             });
21790
21791             element.on('compositionend', function() {
21792               composing = false;
21793               listener();
21794             });
21795           }
21796
21797           var listener = function(ev) {
21798             if (timeout) {
21799               $browser.defer.cancel(timeout);
21800               timeout = null;
21801             }
21802             if (composing) return;
21803             var value = element.val(),
21804                 event = ev && ev.type;
21805
21806             // By default we will trim the value
21807             // If the attribute ng-trim exists we will avoid trimming
21808             // If input type is 'password', the value is never trimmed
21809             if (type !== 'password' && (!attr.ngTrim || attr.ngTrim !== 'false')) {
21810               value = trim(value);
21811             }
21812
21813             // If a control is suffering from bad input (due to native validators), browsers discard its
21814             // value, so it may be necessary to revalidate (by calling $setViewValue again) even if the
21815             // control's value is the same empty value twice in a row.
21816             if (ctrl.$viewValue !== value || (value === '' && ctrl.$$hasNativeValidators)) {
21817               ctrl.$setViewValue(value, event);
21818             }
21819           };
21820
21821           // if the browser does support "input" event, we are fine - except on IE9 which doesn't fire the
21822           // input event on backspace, delete or cut
21823           if ($sniffer.hasEvent('input')) {
21824             element.on('input', listener);
21825           } else {
21826             var timeout;
21827
21828             var deferListener = function(ev, input, origValue) {
21829               if (!timeout) {
21830                 timeout = $browser.defer(function() {
21831                   timeout = null;
21832                   if (!input || input.value !== origValue) {
21833                     listener(ev);
21834                   }
21835                 });
21836               }
21837             };
21838
21839             element.on('keydown', function(event) {
21840               var key = event.keyCode;
21841
21842               // ignore
21843               //    command            modifiers                   arrows
21844               if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) return;
21845
21846               deferListener(event, this, this.value);
21847             });
21848
21849             // if user modifies input value using context menu in IE, we need "paste" and "cut" events to catch it
21850             if ($sniffer.hasEvent('paste')) {
21851               element.on('paste cut', deferListener);
21852             }
21853           }
21854
21855           // if user paste into input using mouse on older browser
21856           // or form autocomplete on newer browser, we need "change" event to catch it
21857           element.on('change', listener);
21858
21859           ctrl.$render = function() {
21860             // Workaround for Firefox validation #12102.
21861             var value = ctrl.$isEmpty(ctrl.$viewValue) ? '' : ctrl.$viewValue;
21862             if (element.val() !== value) {
21863               element.val(value);
21864             }
21865           };
21866         }
21867
21868         function weekParser(isoWeek, existingDate) {
21869           if (isDate(isoWeek)) {
21870             return isoWeek;
21871           }
21872
21873           if (isString(isoWeek)) {
21874             WEEK_REGEXP.lastIndex = 0;
21875             var parts = WEEK_REGEXP.exec(isoWeek);
21876             if (parts) {
21877               var year = +parts[1],
21878                   week = +parts[2],
21879                   hours = 0,
21880                   minutes = 0,
21881                   seconds = 0,
21882                   milliseconds = 0,
21883                   firstThurs = getFirstThursdayOfYear(year),
21884                   addDays = (week - 1) * 7;
21885
21886               if (existingDate) {
21887                 hours = existingDate.getHours();
21888                 minutes = existingDate.getMinutes();
21889                 seconds = existingDate.getSeconds();
21890                 milliseconds = existingDate.getMilliseconds();
21891               }
21892
21893               return new Date(year, 0, firstThurs.getDate() + addDays, hours, minutes, seconds, milliseconds);
21894             }
21895           }
21896
21897           return NaN;
21898         }
21899
21900         function createDateParser(regexp, mapping) {
21901           return function(iso, date) {
21902             var parts, map;
21903
21904             if (isDate(iso)) {
21905               return iso;
21906             }
21907
21908             if (isString(iso)) {
21909               // When a date is JSON'ified to wraps itself inside of an extra
21910               // set of double quotes. This makes the date parsing code unable
21911               // to match the date string and parse it as a date.
21912               if (iso.charAt(0) == '"' && iso.charAt(iso.length - 1) == '"') {
21913                 iso = iso.substring(1, iso.length - 1);
21914               }
21915               if (ISO_DATE_REGEXP.test(iso)) {
21916                 return new Date(iso);
21917               }
21918               regexp.lastIndex = 0;
21919               parts = regexp.exec(iso);
21920
21921               if (parts) {
21922                 parts.shift();
21923                 if (date) {
21924                   map = {
21925                     yyyy: date.getFullYear(),
21926                     MM: date.getMonth() + 1,
21927                     dd: date.getDate(),
21928                     HH: date.getHours(),
21929                     mm: date.getMinutes(),
21930                     ss: date.getSeconds(),
21931                     sss: date.getMilliseconds() / 1000
21932                   };
21933                 } else {
21934                   map = { yyyy: 1970, MM: 1, dd: 1, HH: 0, mm: 0, ss: 0, sss: 0 };
21935                 }
21936
21937                 forEach(parts, function(part, index) {
21938                   if (index < mapping.length) {
21939                     map[mapping[index]] = +part;
21940                   }
21941                 });
21942                 return new Date(map.yyyy, map.MM - 1, map.dd, map.HH, map.mm, map.ss || 0, map.sss * 1000 || 0);
21943               }
21944             }
21945
21946             return NaN;
21947           };
21948         }
21949
21950         function createDateInputType(type, regexp, parseDate, format) {
21951           return function dynamicDateInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter) {
21952             badInputChecker(scope, element, attr, ctrl);
21953             baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
21954             var timezone = ctrl && ctrl.$options && ctrl.$options.timezone;
21955             var previousDate;
21956
21957             ctrl.$$parserName = type;
21958             ctrl.$parsers.push(function(value) {
21959               if (ctrl.$isEmpty(value)) return null;
21960               if (regexp.test(value)) {
21961                 // Note: We cannot read ctrl.$modelValue, as there might be a different
21962                 // parser/formatter in the processing chain so that the model
21963                 // contains some different data format!
21964                 var parsedDate = parseDate(value, previousDate);
21965                 if (timezone) {
21966                   parsedDate = convertTimezoneToLocal(parsedDate, timezone);
21967                 }
21968                 return parsedDate;
21969               }
21970               return undefined;
21971             });
21972
21973             ctrl.$formatters.push(function(value) {
21974               if (value && !isDate(value)) {
21975                 throw ngModelMinErr('datefmt', 'Expected `{0}` to be a date', value);
21976               }
21977               if (isValidDate(value)) {
21978                 previousDate = value;
21979                 if (previousDate && timezone) {
21980                   previousDate = convertTimezoneToLocal(previousDate, timezone, true);
21981                 }
21982                 return $filter('date')(value, format, timezone);
21983               } else {
21984                 previousDate = null;
21985                 return '';
21986               }
21987             });
21988
21989             if (isDefined(attr.min) || attr.ngMin) {
21990               var minVal;
21991               ctrl.$validators.min = function(value) {
21992                 return !isValidDate(value) || isUndefined(minVal) || parseDate(value) >= minVal;
21993               };
21994               attr.$observe('min', function(val) {
21995                 minVal = parseObservedDateValue(val);
21996                 ctrl.$validate();
21997               });
21998             }
21999
22000             if (isDefined(attr.max) || attr.ngMax) {
22001               var maxVal;
22002               ctrl.$validators.max = function(value) {
22003                 return !isValidDate(value) || isUndefined(maxVal) || parseDate(value) <= maxVal;
22004               };
22005               attr.$observe('max', function(val) {
22006                 maxVal = parseObservedDateValue(val);
22007                 ctrl.$validate();
22008               });
22009             }
22010
22011             function isValidDate(value) {
22012               // Invalid Date: getTime() returns NaN
22013               return value && !(value.getTime && value.getTime() !== value.getTime());
22014             }
22015
22016             function parseObservedDateValue(val) {
22017               return isDefined(val) && !isDate(val) ? parseDate(val) || undefined : val;
22018             }
22019           };
22020         }
22021
22022         function badInputChecker(scope, element, attr, ctrl) {
22023           var node = element[0];
22024           var nativeValidation = ctrl.$$hasNativeValidators = isObject(node.validity);
22025           if (nativeValidation) {
22026             ctrl.$parsers.push(function(value) {
22027               var validity = element.prop(VALIDITY_STATE_PROPERTY) || {};
22028               // Detect bug in FF35 for input[email] (https://bugzilla.mozilla.org/show_bug.cgi?id=1064430):
22029               // - also sets validity.badInput (should only be validity.typeMismatch).
22030               // - see http://www.whatwg.org/specs/web-apps/current-work/multipage/forms.html#e-mail-state-(type=email)
22031               // - can ignore this case as we can still read out the erroneous email...
22032               return validity.badInput && !validity.typeMismatch ? undefined : value;
22033             });
22034           }
22035         }
22036
22037         function numberInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22038           badInputChecker(scope, element, attr, ctrl);
22039           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22040
22041           ctrl.$$parserName = 'number';
22042           ctrl.$parsers.push(function(value) {
22043             if (ctrl.$isEmpty(value))      return null;
22044             if (NUMBER_REGEXP.test(value)) return parseFloat(value);
22045             return undefined;
22046           });
22047
22048           ctrl.$formatters.push(function(value) {
22049             if (!ctrl.$isEmpty(value)) {
22050               if (!isNumber(value)) {
22051                 throw ngModelMinErr('numfmt', 'Expected `{0}` to be a number', value);
22052               }
22053               value = value.toString();
22054             }
22055             return value;
22056           });
22057
22058           if (isDefined(attr.min) || attr.ngMin) {
22059             var minVal;
22060             ctrl.$validators.min = function(value) {
22061               return ctrl.$isEmpty(value) || isUndefined(minVal) || value >= minVal;
22062             };
22063
22064             attr.$observe('min', function(val) {
22065               if (isDefined(val) && !isNumber(val)) {
22066                 val = parseFloat(val, 10);
22067               }
22068               minVal = isNumber(val) && !isNaN(val) ? val : undefined;
22069               // TODO(matsko): implement validateLater to reduce number of validations
22070               ctrl.$validate();
22071             });
22072           }
22073
22074           if (isDefined(attr.max) || attr.ngMax) {
22075             var maxVal;
22076             ctrl.$validators.max = function(value) {
22077               return ctrl.$isEmpty(value) || isUndefined(maxVal) || value <= maxVal;
22078             };
22079
22080             attr.$observe('max', function(val) {
22081               if (isDefined(val) && !isNumber(val)) {
22082                 val = parseFloat(val, 10);
22083               }
22084               maxVal = isNumber(val) && !isNaN(val) ? val : undefined;
22085               // TODO(matsko): implement validateLater to reduce number of validations
22086               ctrl.$validate();
22087             });
22088           }
22089         }
22090
22091         function urlInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22092           // Note: no badInputChecker here by purpose as `url` is only a validation
22093           // in browsers, i.e. we can always read out input.value even if it is not valid!
22094           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22095           stringBasedInputType(ctrl);
22096
22097           ctrl.$$parserName = 'url';
22098           ctrl.$validators.url = function(modelValue, viewValue) {
22099             var value = modelValue || viewValue;
22100             return ctrl.$isEmpty(value) || URL_REGEXP.test(value);
22101           };
22102         }
22103
22104         function emailInputType(scope, element, attr, ctrl, $sniffer, $browser) {
22105           // Note: no badInputChecker here by purpose as `url` is only a validation
22106           // in browsers, i.e. we can always read out input.value even if it is not valid!
22107           baseInputType(scope, element, attr, ctrl, $sniffer, $browser);
22108           stringBasedInputType(ctrl);
22109
22110           ctrl.$$parserName = 'email';
22111           ctrl.$validators.email = function(modelValue, viewValue) {
22112             var value = modelValue || viewValue;
22113             return ctrl.$isEmpty(value) || EMAIL_REGEXP.test(value);
22114           };
22115         }
22116
22117         function radioInputType(scope, element, attr, ctrl) {
22118           // make the name unique, if not defined
22119           if (isUndefined(attr.name)) {
22120             element.attr('name', nextUid());
22121           }
22122
22123           var listener = function(ev) {
22124             if (element[0].checked) {
22125               ctrl.$setViewValue(attr.value, ev && ev.type);
22126             }
22127           };
22128
22129           element.on('click', listener);
22130
22131           ctrl.$render = function() {
22132             var value = attr.value;
22133             element[0].checked = (value == ctrl.$viewValue);
22134           };
22135
22136           attr.$observe('value', ctrl.$render);
22137         }
22138
22139         function parseConstantExpr($parse, context, name, expression, fallback) {
22140           var parseFn;
22141           if (isDefined(expression)) {
22142             parseFn = $parse(expression);
22143             if (!parseFn.constant) {
22144               throw ngModelMinErr('constexpr', 'Expected constant expression for `{0}`, but saw ' +
22145                                            '`{1}`.', name, expression);
22146             }
22147             return parseFn(context);
22148           }
22149           return fallback;
22150         }
22151
22152         function checkboxInputType(scope, element, attr, ctrl, $sniffer, $browser, $filter, $parse) {
22153           var trueValue = parseConstantExpr($parse, scope, 'ngTrueValue', attr.ngTrueValue, true);
22154           var falseValue = parseConstantExpr($parse, scope, 'ngFalseValue', attr.ngFalseValue, false);
22155
22156           var listener = function(ev) {
22157             ctrl.$setViewValue(element[0].checked, ev && ev.type);
22158           };
22159
22160           element.on('click', listener);
22161
22162           ctrl.$render = function() {
22163             element[0].checked = ctrl.$viewValue;
22164           };
22165
22166           // Override the standard `$isEmpty` because the $viewValue of an empty checkbox is always set to `false`
22167           // This is because of the parser below, which compares the `$modelValue` with `trueValue` to convert
22168           // it to a boolean.
22169           ctrl.$isEmpty = function(value) {
22170             return value === false;
22171           };
22172
22173           ctrl.$formatters.push(function(value) {
22174             return equals(value, trueValue);
22175           });
22176
22177           ctrl.$parsers.push(function(value) {
22178             return value ? trueValue : falseValue;
22179           });
22180         }
22181
22182
22183         /**
22184          * @ngdoc directive
22185          * @name textarea
22186          * @restrict E
22187          *
22188          * @description
22189          * HTML textarea element control with angular data-binding. The data-binding and validation
22190          * properties of this element are exactly the same as those of the
22191          * {@link ng.directive:input input element}.
22192          *
22193          * @param {string} ngModel Assignable angular expression to data-bind to.
22194          * @param {string=} name Property name of the form under which the control is published.
22195          * @param {string=} required Sets `required` validation error key if the value is not entered.
22196          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
22197          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
22198          *    `required` when you want to data-bind to the `required` attribute.
22199          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22200          *    minlength.
22201          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22202          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22203          *    length.
22204          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22205          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22206          *    If the expression evaluates to a RegExp object, then this is used directly.
22207          *    If the expression evaluates to a string, then it will be converted to a RegExp
22208          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22209          *    `new RegExp('^abc$')`.<br />
22210          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22211          *    start at the index of the last search's match, thus not taking the whole input value into
22212          *    account.
22213          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22214          *    interaction with the input element.
22215          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22216          */
22217
22218
22219         /**
22220          * @ngdoc directive
22221          * @name input
22222          * @restrict E
22223          *
22224          * @description
22225          * HTML input element control. When used together with {@link ngModel `ngModel`}, it provides data-binding,
22226          * input state control, and validation.
22227          * Input control follows HTML5 input types and polyfills the HTML5 validation behavior for older browsers.
22228          *
22229          * <div class="alert alert-warning">
22230          * **Note:** Not every feature offered is available for all input types.
22231          * Specifically, data binding and event handling via `ng-model` is unsupported for `input[file]`.
22232          * </div>
22233          *
22234          * @param {string} ngModel Assignable angular expression to data-bind to.
22235          * @param {string=} name Property name of the form under which the control is published.
22236          * @param {string=} required Sets `required` validation error key if the value is not entered.
22237          * @param {boolean=} ngRequired Sets `required` attribute if set to true
22238          * @param {number=} ngMinlength Sets `minlength` validation error key if the value is shorter than
22239          *    minlength.
22240          * @param {number=} ngMaxlength Sets `maxlength` validation error key if the value is longer than
22241          *    maxlength. Setting the attribute to a negative or non-numeric value, allows view values of any
22242          *    length.
22243          * @param {string=} ngPattern Sets `pattern` validation error key if the ngModel value does not match
22244          *    a RegExp found by evaluating the Angular expression given in the attribute value.
22245          *    If the expression evaluates to a RegExp object, then this is used directly.
22246          *    If the expression evaluates to a string, then it will be converted to a RegExp
22247          *    after wrapping it in `^` and `$` characters. For instance, `"abc"` will be converted to
22248          *    `new RegExp('^abc$')`.<br />
22249          *    **Note:** Avoid using the `g` flag on the RegExp, as it will cause each successive search to
22250          *    start at the index of the last search's match, thus not taking the whole input value into
22251          *    account.
22252          * @param {string=} ngChange Angular expression to be executed when input changes due to user
22253          *    interaction with the input element.
22254          * @param {boolean=} [ngTrim=true] If set to false Angular will not automatically trim the input.
22255          *    This parameter is ignored for input[type=password] controls, which will never trim the
22256          *    input.
22257          *
22258          * @example
22259             <example name="input-directive" module="inputExample">
22260               <file name="index.html">
22261                <script>
22262                   angular.module('inputExample', [])
22263                     .controller('ExampleController', ['$scope', function($scope) {
22264                       $scope.user = {name: 'guest', last: 'visitor'};
22265                     }]);
22266                </script>
22267                <div ng-controller="ExampleController">
22268                  <form name="myForm">
22269                    <label>
22270                       User name:
22271                       <input type="text" name="userName" ng-model="user.name" required>
22272                    </label>
22273                    <div role="alert">
22274                      <span class="error" ng-show="myForm.userName.$error.required">
22275                       Required!</span>
22276                    </div>
22277                    <label>
22278                       Last name:
22279                       <input type="text" name="lastName" ng-model="user.last"
22280                       ng-minlength="3" ng-maxlength="10">
22281                    </label>
22282                    <div role="alert">
22283                      <span class="error" ng-show="myForm.lastName.$error.minlength">
22284                        Too short!</span>
22285                      <span class="error" ng-show="myForm.lastName.$error.maxlength">
22286                        Too long!</span>
22287                    </div>
22288                  </form>
22289                  <hr>
22290                  <tt>user = {{user}}</tt><br/>
22291                  <tt>myForm.userName.$valid = {{myForm.userName.$valid}}</tt><br/>
22292                  <tt>myForm.userName.$error = {{myForm.userName.$error}}</tt><br/>
22293                  <tt>myForm.lastName.$valid = {{myForm.lastName.$valid}}</tt><br/>
22294                  <tt>myForm.lastName.$error = {{myForm.lastName.$error}}</tt><br/>
22295                  <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
22296                  <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
22297                  <tt>myForm.$error.minlength = {{!!myForm.$error.minlength}}</tt><br/>
22298                  <tt>myForm.$error.maxlength = {{!!myForm.$error.maxlength}}</tt><br/>
22299                </div>
22300               </file>
22301               <file name="protractor.js" type="protractor">
22302                 var user = element(by.exactBinding('user'));
22303                 var userNameValid = element(by.binding('myForm.userName.$valid'));
22304                 var lastNameValid = element(by.binding('myForm.lastName.$valid'));
22305                 var lastNameError = element(by.binding('myForm.lastName.$error'));
22306                 var formValid = element(by.binding('myForm.$valid'));
22307                 var userNameInput = element(by.model('user.name'));
22308                 var userLastInput = element(by.model('user.last'));
22309
22310                 it('should initialize to model', function() {
22311                   expect(user.getText()).toContain('{"name":"guest","last":"visitor"}');
22312                   expect(userNameValid.getText()).toContain('true');
22313                   expect(formValid.getText()).toContain('true');
22314                 });
22315
22316                 it('should be invalid if empty when required', function() {
22317                   userNameInput.clear();
22318                   userNameInput.sendKeys('');
22319
22320                   expect(user.getText()).toContain('{"last":"visitor"}');
22321                   expect(userNameValid.getText()).toContain('false');
22322                   expect(formValid.getText()).toContain('false');
22323                 });
22324
22325                 it('should be valid if empty when min length is set', function() {
22326                   userLastInput.clear();
22327                   userLastInput.sendKeys('');
22328
22329                   expect(user.getText()).toContain('{"name":"guest","last":""}');
22330                   expect(lastNameValid.getText()).toContain('true');
22331                   expect(formValid.getText()).toContain('true');
22332                 });
22333
22334                 it('should be invalid if less than required min length', function() {
22335                   userLastInput.clear();
22336                   userLastInput.sendKeys('xx');
22337
22338                   expect(user.getText()).toContain('{"name":"guest"}');
22339                   expect(lastNameValid.getText()).toContain('false');
22340                   expect(lastNameError.getText()).toContain('minlength');
22341                   expect(formValid.getText()).toContain('false');
22342                 });
22343
22344                 it('should be invalid if longer than max length', function() {
22345                   userLastInput.clear();
22346                   userLastInput.sendKeys('some ridiculously long name');
22347
22348                   expect(user.getText()).toContain('{"name":"guest"}');
22349                   expect(lastNameValid.getText()).toContain('false');
22350                   expect(lastNameError.getText()).toContain('maxlength');
22351                   expect(formValid.getText()).toContain('false');
22352                 });
22353               </file>
22354             </example>
22355          */
22356         var inputDirective = ['$browser', '$sniffer', '$filter', '$parse',
22357             function($browser, $sniffer, $filter, $parse) {
22358           return {
22359             restrict: 'E',
22360             require: ['?ngModel'],
22361             link: {
22362               pre: function(scope, element, attr, ctrls) {
22363                 if (ctrls[0]) {
22364                   (inputType[lowercase(attr.type)] || inputType.text)(scope, element, attr, ctrls[0], $sniffer,
22365                                                                       $browser, $filter, $parse);
22366                 }
22367               }
22368             }
22369           };
22370         }];
22371
22372
22373
22374         var CONSTANT_VALUE_REGEXP = /^(true|false|\d+)$/;
22375         /**
22376          * @ngdoc directive
22377          * @name ngValue
22378          *
22379          * @description
22380          * Binds the given expression to the value of `<option>` or {@link input[radio] `input[radio]`},
22381          * so that when the element is selected, the {@link ngModel `ngModel`} of that element is set to
22382          * the bound value.
22383          *
22384          * `ngValue` is useful when dynamically generating lists of radio buttons using
22385          * {@link ngRepeat `ngRepeat`}, as shown below.
22386          *
22387          * Likewise, `ngValue` can be used to generate `<option>` elements for
22388          * the {@link select `select`} element. In that case however, only strings are supported
22389          * for the `value `attribute, so the resulting `ngModel` will always be a string.
22390          * Support for `select` models with non-string values is available via `ngOptions`.
22391          *
22392          * @element input
22393          * @param {string=} ngValue angular expression, whose value will be bound to the `value` attribute
22394          *   of the `input` element
22395          *
22396          * @example
22397             <example name="ngValue-directive" module="valueExample">
22398               <file name="index.html">
22399                <script>
22400                   angular.module('valueExample', [])
22401                     .controller('ExampleController', ['$scope', function($scope) {
22402                       $scope.names = ['pizza', 'unicorns', 'robots'];
22403                       $scope.my = { favorite: 'unicorns' };
22404                     }]);
22405                </script>
22406                 <form ng-controller="ExampleController">
22407                   <h2>Which is your favorite?</h2>
22408                     <label ng-repeat="name in names" for="{{name}}">
22409                       {{name}}
22410                       <input type="radio"
22411                              ng-model="my.favorite"
22412                              ng-value="name"
22413                              id="{{name}}"
22414                              name="favorite">
22415                     </label>
22416                   <div>You chose {{my.favorite}}</div>
22417                 </form>
22418               </file>
22419               <file name="protractor.js" type="protractor">
22420                 var favorite = element(by.binding('my.favorite'));
22421
22422                 it('should initialize to model', function() {
22423                   expect(favorite.getText()).toContain('unicorns');
22424                 });
22425                 it('should bind the values to the inputs', function() {
22426                   element.all(by.model('my.favorite')).get(0).click();
22427                   expect(favorite.getText()).toContain('pizza');
22428                 });
22429               </file>
22430             </example>
22431          */
22432         var ngValueDirective = function() {
22433           return {
22434             restrict: 'A',
22435             priority: 100,
22436             compile: function(tpl, tplAttr) {
22437               if (CONSTANT_VALUE_REGEXP.test(tplAttr.ngValue)) {
22438                 return function ngValueConstantLink(scope, elm, attr) {
22439                   attr.$set('value', scope.$eval(attr.ngValue));
22440                 };
22441               } else {
22442                 return function ngValueLink(scope, elm, attr) {
22443                   scope.$watch(attr.ngValue, function valueWatchAction(value) {
22444                     attr.$set('value', value);
22445                   });
22446                 };
22447               }
22448             }
22449           };
22450         };
22451
22452         /**
22453          * @ngdoc directive
22454          * @name ngBind
22455          * @restrict AC
22456          *
22457          * @description
22458          * The `ngBind` attribute tells Angular to replace the text content of the specified HTML element
22459          * with the value of a given expression, and to update the text content when the value of that
22460          * expression changes.
22461          *
22462          * Typically, you don't use `ngBind` directly, but instead you use the double curly markup like
22463          * `{{ expression }}` which is similar but less verbose.
22464          *
22465          * It is preferable to use `ngBind` instead of `{{ expression }}` if a template is momentarily
22466          * displayed by the browser in its raw state before Angular compiles it. Since `ngBind` is an
22467          * element attribute, it makes the bindings invisible to the user while the page is loading.
22468          *
22469          * An alternative solution to this problem would be using the
22470          * {@link ng.directive:ngCloak ngCloak} directive.
22471          *
22472          *
22473          * @element ANY
22474          * @param {expression} ngBind {@link guide/expression Expression} to evaluate.
22475          *
22476          * @example
22477          * Enter a name in the Live Preview text box; the greeting below the text box changes instantly.
22478            <example module="bindExample">
22479              <file name="index.html">
22480                <script>
22481                  angular.module('bindExample', [])
22482                    .controller('ExampleController', ['$scope', function($scope) {
22483                      $scope.name = 'Whirled';
22484                    }]);
22485                </script>
22486                <div ng-controller="ExampleController">
22487                  <label>Enter name: <input type="text" ng-model="name"></label><br>
22488                  Hello <span ng-bind="name"></span>!
22489                </div>
22490              </file>
22491              <file name="protractor.js" type="protractor">
22492                it('should check ng-bind', function() {
22493                  var nameInput = element(by.model('name'));
22494
22495                  expect(element(by.binding('name')).getText()).toBe('Whirled');
22496                  nameInput.clear();
22497                  nameInput.sendKeys('world');
22498                  expect(element(by.binding('name')).getText()).toBe('world');
22499                });
22500              </file>
22501            </example>
22502          */
22503         var ngBindDirective = ['$compile', function($compile) {
22504           return {
22505             restrict: 'AC',
22506             compile: function ngBindCompile(templateElement) {
22507               $compile.$$addBindingClass(templateElement);
22508               return function ngBindLink(scope, element, attr) {
22509                 $compile.$$addBindingInfo(element, attr.ngBind);
22510                 element = element[0];
22511                 scope.$watch(attr.ngBind, function ngBindWatchAction(value) {
22512                   element.textContent = isUndefined(value) ? '' : value;
22513                 });
22514               };
22515             }
22516           };
22517         }];
22518
22519
22520         /**
22521          * @ngdoc directive
22522          * @name ngBindTemplate
22523          *
22524          * @description
22525          * The `ngBindTemplate` directive specifies that the element
22526          * text content should be replaced with the interpolation of the template
22527          * in the `ngBindTemplate` attribute.
22528          * Unlike `ngBind`, the `ngBindTemplate` can contain multiple `{{` `}}`
22529          * expressions. This directive is needed since some HTML elements
22530          * (such as TITLE and OPTION) cannot contain SPAN elements.
22531          *
22532          * @element ANY
22533          * @param {string} ngBindTemplate template of form
22534          *   <tt>{{</tt> <tt>expression</tt> <tt>}}</tt> to eval.
22535          *
22536          * @example
22537          * Try it here: enter text in text box and watch the greeting change.
22538            <example module="bindExample">
22539              <file name="index.html">
22540                <script>
22541                  angular.module('bindExample', [])
22542                    .controller('ExampleController', ['$scope', function($scope) {
22543                      $scope.salutation = 'Hello';
22544                      $scope.name = 'World';
22545                    }]);
22546                </script>
22547                <div ng-controller="ExampleController">
22548                 <label>Salutation: <input type="text" ng-model="salutation"></label><br>
22549                 <label>Name: <input type="text" ng-model="name"></label><br>
22550                 <pre ng-bind-template="{{salutation}} {{name}}!"></pre>
22551                </div>
22552              </file>
22553              <file name="protractor.js" type="protractor">
22554                it('should check ng-bind', function() {
22555                  var salutationElem = element(by.binding('salutation'));
22556                  var salutationInput = element(by.model('salutation'));
22557                  var nameInput = element(by.model('name'));
22558
22559                  expect(salutationElem.getText()).toBe('Hello World!');
22560
22561                  salutationInput.clear();
22562                  salutationInput.sendKeys('Greetings');
22563                  nameInput.clear();
22564                  nameInput.sendKeys('user');
22565
22566                  expect(salutationElem.getText()).toBe('Greetings user!');
22567                });
22568              </file>
22569            </example>
22570          */
22571         var ngBindTemplateDirective = ['$interpolate', '$compile', function($interpolate, $compile) {
22572           return {
22573             compile: function ngBindTemplateCompile(templateElement) {
22574               $compile.$$addBindingClass(templateElement);
22575               return function ngBindTemplateLink(scope, element, attr) {
22576                 var interpolateFn = $interpolate(element.attr(attr.$attr.ngBindTemplate));
22577                 $compile.$$addBindingInfo(element, interpolateFn.expressions);
22578                 element = element[0];
22579                 attr.$observe('ngBindTemplate', function(value) {
22580                   element.textContent = isUndefined(value) ? '' : value;
22581                 });
22582               };
22583             }
22584           };
22585         }];
22586
22587
22588         /**
22589          * @ngdoc directive
22590          * @name ngBindHtml
22591          *
22592          * @description
22593          * Evaluates the expression and inserts the resulting HTML into the element in a secure way. By default,
22594          * the resulting HTML content will be sanitized using the {@link ngSanitize.$sanitize $sanitize} service.
22595          * To utilize this functionality, ensure that `$sanitize` is available, for example, by including {@link
22596          * ngSanitize} in your module's dependencies (not in core Angular). In order to use {@link ngSanitize}
22597          * in your module's dependencies, you need to include "angular-sanitize.js" in your application.
22598          *
22599          * You may also bypass sanitization for values you know are safe. To do so, bind to
22600          * an explicitly trusted value via {@link ng.$sce#trustAsHtml $sce.trustAsHtml}.  See the example
22601          * under {@link ng.$sce#show-me-an-example-using-sce- Strict Contextual Escaping (SCE)}.
22602          *
22603          * Note: If a `$sanitize` service is unavailable and the bound value isn't explicitly trusted, you
22604          * will have an exception (instead of an exploit.)
22605          *
22606          * @element ANY
22607          * @param {expression} ngBindHtml {@link guide/expression Expression} to evaluate.
22608          *
22609          * @example
22610
22611            <example module="bindHtmlExample" deps="angular-sanitize.js">
22612              <file name="index.html">
22613                <div ng-controller="ExampleController">
22614                 <p ng-bind-html="myHTML"></p>
22615                </div>
22616              </file>
22617
22618              <file name="script.js">
22619                angular.module('bindHtmlExample', ['ngSanitize'])
22620                  .controller('ExampleController', ['$scope', function($scope) {
22621                    $scope.myHTML =
22622                       'I am an <code>HTML</code>string with ' +
22623                       '<a href="#">links!</a> and other <em>stuff</em>';
22624                  }]);
22625              </file>
22626
22627              <file name="protractor.js" type="protractor">
22628                it('should check ng-bind-html', function() {
22629                  expect(element(by.binding('myHTML')).getText()).toBe(
22630                      'I am an HTMLstring with links! and other stuff');
22631                });
22632              </file>
22633            </example>
22634          */
22635         var ngBindHtmlDirective = ['$sce', '$parse', '$compile', function($sce, $parse, $compile) {
22636           return {
22637             restrict: 'A',
22638             compile: function ngBindHtmlCompile(tElement, tAttrs) {
22639               var ngBindHtmlGetter = $parse(tAttrs.ngBindHtml);
22640               var ngBindHtmlWatch = $parse(tAttrs.ngBindHtml, function getStringValue(value) {
22641                 return (value || '').toString();
22642               });
22643               $compile.$$addBindingClass(tElement);
22644
22645               return function ngBindHtmlLink(scope, element, attr) {
22646                 $compile.$$addBindingInfo(element, attr.ngBindHtml);
22647
22648                 scope.$watch(ngBindHtmlWatch, function ngBindHtmlWatchAction() {
22649                   // we re-evaluate the expr because we want a TrustedValueHolderType
22650                   // for $sce, not a string
22651                   element.html($sce.getTrustedHtml(ngBindHtmlGetter(scope)) || '');
22652                 });
22653               };
22654             }
22655           };
22656         }];
22657
22658         /**
22659          * @ngdoc directive
22660          * @name ngChange
22661          *
22662          * @description
22663          * Evaluate the given expression when the user changes the input.
22664          * The expression is evaluated immediately, unlike the JavaScript onchange event
22665          * which only triggers at the end of a change (usually, when the user leaves the
22666          * form element or presses the return key).
22667          *
22668          * The `ngChange` expression is only evaluated when a change in the input value causes
22669          * a new value to be committed to the model.
22670          *
22671          * It will not be evaluated:
22672          * * if the value returned from the `$parsers` transformation pipeline has not changed
22673          * * if the input has continued to be invalid since the model will stay `null`
22674          * * if the model is changed programmatically and not by a change to the input value
22675          *
22676          *
22677          * Note, this directive requires `ngModel` to be present.
22678          *
22679          * @element input
22680          * @param {expression} ngChange {@link guide/expression Expression} to evaluate upon change
22681          * in input value.
22682          *
22683          * @example
22684          * <example name="ngChange-directive" module="changeExample">
22685          *   <file name="index.html">
22686          *     <script>
22687          *       angular.module('changeExample', [])
22688          *         .controller('ExampleController', ['$scope', function($scope) {
22689          *           $scope.counter = 0;
22690          *           $scope.change = function() {
22691          *             $scope.counter++;
22692          *           };
22693          *         }]);
22694          *     </script>
22695          *     <div ng-controller="ExampleController">
22696          *       <input type="checkbox" ng-model="confirmed" ng-change="change()" id="ng-change-example1" />
22697          *       <input type="checkbox" ng-model="confirmed" id="ng-change-example2" />
22698          *       <label for="ng-change-example2">Confirmed</label><br />
22699          *       <tt>debug = {{confirmed}}</tt><br/>
22700          *       <tt>counter = {{counter}}</tt><br/>
22701          *     </div>
22702          *   </file>
22703          *   <file name="protractor.js" type="protractor">
22704          *     var counter = element(by.binding('counter'));
22705          *     var debug = element(by.binding('confirmed'));
22706          *
22707          *     it('should evaluate the expression if changing from view', function() {
22708          *       expect(counter.getText()).toContain('0');
22709          *
22710          *       element(by.id('ng-change-example1')).click();
22711          *
22712          *       expect(counter.getText()).toContain('1');
22713          *       expect(debug.getText()).toContain('true');
22714          *     });
22715          *
22716          *     it('should not evaluate the expression if changing from model', function() {
22717          *       element(by.id('ng-change-example2')).click();
22718
22719          *       expect(counter.getText()).toContain('0');
22720          *       expect(debug.getText()).toContain('true');
22721          *     });
22722          *   </file>
22723          * </example>
22724          */
22725         var ngChangeDirective = valueFn({
22726           restrict: 'A',
22727           require: 'ngModel',
22728           link: function(scope, element, attr, ctrl) {
22729             ctrl.$viewChangeListeners.push(function() {
22730               scope.$eval(attr.ngChange);
22731             });
22732           }
22733         });
22734
22735         function classDirective(name, selector) {
22736           name = 'ngClass' + name;
22737           return ['$animate', function($animate) {
22738             return {
22739               restrict: 'AC',
22740               link: function(scope, element, attr) {
22741                 var oldVal;
22742
22743                 scope.$watch(attr[name], ngClassWatchAction, true);
22744
22745                 attr.$observe('class', function(value) {
22746                   ngClassWatchAction(scope.$eval(attr[name]));
22747                 });
22748
22749
22750                 if (name !== 'ngClass') {
22751                   scope.$watch('$index', function($index, old$index) {
22752                     // jshint bitwise: false
22753                     var mod = $index & 1;
22754                     if (mod !== (old$index & 1)) {
22755                       var classes = arrayClasses(scope.$eval(attr[name]));
22756                       mod === selector ?
22757                         addClasses(classes) :
22758                         removeClasses(classes);
22759                     }
22760                   });
22761                 }
22762
22763                 function addClasses(classes) {
22764                   var newClasses = digestClassCounts(classes, 1);
22765                   attr.$addClass(newClasses);
22766                 }
22767
22768                 function removeClasses(classes) {
22769                   var newClasses = digestClassCounts(classes, -1);
22770                   attr.$removeClass(newClasses);
22771                 }
22772
22773                 function digestClassCounts(classes, count) {
22774                   // Use createMap() to prevent class assumptions involving property
22775                   // names in Object.prototype
22776                   var classCounts = element.data('$classCounts') || createMap();
22777                   var classesToUpdate = [];
22778                   forEach(classes, function(className) {
22779                     if (count > 0 || classCounts[className]) {
22780                       classCounts[className] = (classCounts[className] || 0) + count;
22781                       if (classCounts[className] === +(count > 0)) {
22782                         classesToUpdate.push(className);
22783                       }
22784                     }
22785                   });
22786                   element.data('$classCounts', classCounts);
22787                   return classesToUpdate.join(' ');
22788                 }
22789
22790                 function updateClasses(oldClasses, newClasses) {
22791                   var toAdd = arrayDifference(newClasses, oldClasses);
22792                   var toRemove = arrayDifference(oldClasses, newClasses);
22793                   toAdd = digestClassCounts(toAdd, 1);
22794                   toRemove = digestClassCounts(toRemove, -1);
22795                   if (toAdd && toAdd.length) {
22796                     $animate.addClass(element, toAdd);
22797                   }
22798                   if (toRemove && toRemove.length) {
22799                     $animate.removeClass(element, toRemove);
22800                   }
22801                 }
22802
22803                 function ngClassWatchAction(newVal) {
22804                   if (selector === true || scope.$index % 2 === selector) {
22805                     var newClasses = arrayClasses(newVal || []);
22806                     if (!oldVal) {
22807                       addClasses(newClasses);
22808                     } else if (!equals(newVal,oldVal)) {
22809                       var oldClasses = arrayClasses(oldVal);
22810                       updateClasses(oldClasses, newClasses);
22811                     }
22812                   }
22813                   oldVal = shallowCopy(newVal);
22814                 }
22815               }
22816             };
22817
22818             function arrayDifference(tokens1, tokens2) {
22819               var values = [];
22820
22821               outer:
22822               for (var i = 0; i < tokens1.length; i++) {
22823                 var token = tokens1[i];
22824                 for (var j = 0; j < tokens2.length; j++) {
22825                   if (token == tokens2[j]) continue outer;
22826                 }
22827                 values.push(token);
22828               }
22829               return values;
22830             }
22831
22832             function arrayClasses(classVal) {
22833               var classes = [];
22834               if (isArray(classVal)) {
22835                 forEach(classVal, function(v) {
22836                   classes = classes.concat(arrayClasses(v));
22837                 });
22838                 return classes;
22839               } else if (isString(classVal)) {
22840                 return classVal.split(' ');
22841               } else if (isObject(classVal)) {
22842                 forEach(classVal, function(v, k) {
22843                   if (v) {
22844                     classes = classes.concat(k.split(' '));
22845                   }
22846                 });
22847                 return classes;
22848               }
22849               return classVal;
22850             }
22851           }];
22852         }
22853
22854         /**
22855          * @ngdoc directive
22856          * @name ngClass
22857          * @restrict AC
22858          *
22859          * @description
22860          * The `ngClass` directive allows you to dynamically set CSS classes on an HTML element by databinding
22861          * an expression that represents all classes to be added.
22862          *
22863          * The directive operates in three different ways, depending on which of three types the expression
22864          * evaluates to:
22865          *
22866          * 1. If the expression evaluates to a string, the string should be one or more space-delimited class
22867          * names.
22868          *
22869          * 2. If the expression evaluates to an object, then for each key-value pair of the
22870          * object with a truthy value the corresponding key is used as a class name.
22871          *
22872          * 3. If the expression evaluates to an array, each element of the array should either be a string as in
22873          * type 1 or an object as in type 2. This means that you can mix strings and objects together in an array
22874          * to give you more control over what CSS classes appear. See the code below for an example of this.
22875          *
22876          *
22877          * The directive won't add duplicate classes if a particular class was already set.
22878          *
22879          * When the expression changes, the previously added classes are removed and only then are the
22880          * new classes added.
22881          *
22882          * @animations
22883          * **add** - happens just before the class is applied to the elements
22884          *
22885          * **remove** - happens just before the class is removed from the element
22886          *
22887          * @element ANY
22888          * @param {expression} ngClass {@link guide/expression Expression} to eval. The result
22889          *   of the evaluation can be a string representing space delimited class
22890          *   names, an array, or a map of class names to boolean values. In the case of a map, the
22891          *   names of the properties whose values are truthy will be added as css classes to the
22892          *   element.
22893          *
22894          * @example Example that demonstrates basic bindings via ngClass directive.
22895            <example>
22896              <file name="index.html">
22897                <p ng-class="{strike: deleted, bold: important, 'has-error': error}">Map Syntax Example</p>
22898                <label>
22899                   <input type="checkbox" ng-model="deleted">
22900                   deleted (apply "strike" class)
22901                </label><br>
22902                <label>
22903                   <input type="checkbox" ng-model="important">
22904                   important (apply "bold" class)
22905                </label><br>
22906                <label>
22907                   <input type="checkbox" ng-model="error">
22908                   error (apply "has-error" class)
22909                </label>
22910                <hr>
22911                <p ng-class="style">Using String Syntax</p>
22912                <input type="text" ng-model="style"
22913                       placeholder="Type: bold strike red" aria-label="Type: bold strike red">
22914                <hr>
22915                <p ng-class="[style1, style2, style3]">Using Array Syntax</p>
22916                <input ng-model="style1"
22917                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red"><br>
22918                <input ng-model="style2"
22919                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 2"><br>
22920                <input ng-model="style3"
22921                       placeholder="Type: bold, strike or red" aria-label="Type: bold, strike or red 3"><br>
22922                <hr>
22923                <p ng-class="[style4, {orange: warning}]">Using Array and Map Syntax</p>
22924                <input ng-model="style4" placeholder="Type: bold, strike" aria-label="Type: bold, strike"><br>
22925                <label><input type="checkbox" ng-model="warning"> warning (apply "orange" class)</label>
22926              </file>
22927              <file name="style.css">
22928                .strike {
22929                    text-decoration: line-through;
22930                }
22931                .bold {
22932                    font-weight: bold;
22933                }
22934                .red {
22935                    color: red;
22936                }
22937                .has-error {
22938                    color: red;
22939                    background-color: yellow;
22940                }
22941                .orange {
22942                    color: orange;
22943                }
22944              </file>
22945              <file name="protractor.js" type="protractor">
22946                var ps = element.all(by.css('p'));
22947
22948                it('should let you toggle the class', function() {
22949
22950                  expect(ps.first().getAttribute('class')).not.toMatch(/bold/);
22951                  expect(ps.first().getAttribute('class')).not.toMatch(/has-error/);
22952
22953                  element(by.model('important')).click();
22954                  expect(ps.first().getAttribute('class')).toMatch(/bold/);
22955
22956                  element(by.model('error')).click();
22957                  expect(ps.first().getAttribute('class')).toMatch(/has-error/);
22958                });
22959
22960                it('should let you toggle string example', function() {
22961                  expect(ps.get(1).getAttribute('class')).toBe('');
22962                  element(by.model('style')).clear();
22963                  element(by.model('style')).sendKeys('red');
22964                  expect(ps.get(1).getAttribute('class')).toBe('red');
22965                });
22966
22967                it('array example should have 3 classes', function() {
22968                  expect(ps.get(2).getAttribute('class')).toBe('');
22969                  element(by.model('style1')).sendKeys('bold');
22970                  element(by.model('style2')).sendKeys('strike');
22971                  element(by.model('style3')).sendKeys('red');
22972                  expect(ps.get(2).getAttribute('class')).toBe('bold strike red');
22973                });
22974
22975                it('array with map example should have 2 classes', function() {
22976                  expect(ps.last().getAttribute('class')).toBe('');
22977                  element(by.model('style4')).sendKeys('bold');
22978                  element(by.model('warning')).click();
22979                  expect(ps.last().getAttribute('class')).toBe('bold orange');
22980                });
22981              </file>
22982            </example>
22983
22984            ## Animations
22985
22986            The example below demonstrates how to perform animations using ngClass.
22987
22988            <example module="ngAnimate" deps="angular-animate.js" animations="true">
22989              <file name="index.html">
22990               <input id="setbtn" type="button" value="set" ng-click="myVar='my-class'">
22991               <input id="clearbtn" type="button" value="clear" ng-click="myVar=''">
22992               <br>
22993               <span class="base-class" ng-class="myVar">Sample Text</span>
22994              </file>
22995              <file name="style.css">
22996                .base-class {
22997                  transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
22998                }
22999
23000                .base-class.my-class {
23001                  color: red;
23002                  font-size:3em;
23003                }
23004              </file>
23005              <file name="protractor.js" type="protractor">
23006                it('should check ng-class', function() {
23007                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23008                    toMatch(/my-class/);
23009
23010                  element(by.id('setbtn')).click();
23011
23012                  expect(element(by.css('.base-class')).getAttribute('class')).
23013                    toMatch(/my-class/);
23014
23015                  element(by.id('clearbtn')).click();
23016
23017                  expect(element(by.css('.base-class')).getAttribute('class')).not.
23018                    toMatch(/my-class/);
23019                });
23020              </file>
23021            </example>
23022
23023
23024            ## ngClass and pre-existing CSS3 Transitions/Animations
23025            The ngClass directive still supports CSS3 Transitions/Animations even if they do not follow the ngAnimate CSS naming structure.
23026            Upon animation ngAnimate will apply supplementary CSS classes to track the start and end of an animation, but this will not hinder
23027            any pre-existing CSS transitions already on the element. To get an idea of what happens during a class-based animation, be sure
23028            to view the step by step details of {@link $animate#addClass $animate.addClass} and
23029            {@link $animate#removeClass $animate.removeClass}.
23030          */
23031         var ngClassDirective = classDirective('', true);
23032
23033         /**
23034          * @ngdoc directive
23035          * @name ngClassOdd
23036          * @restrict AC
23037          *
23038          * @description
23039          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23040          * {@link ng.directive:ngClass ngClass}, except they work in
23041          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23042          *
23043          * This directive can be applied only within the scope of an
23044          * {@link ng.directive:ngRepeat ngRepeat}.
23045          *
23046          * @element ANY
23047          * @param {expression} ngClassOdd {@link guide/expression Expression} to eval. The result
23048          *   of the evaluation can be a string representing space delimited class names or an array.
23049          *
23050          * @example
23051            <example>
23052              <file name="index.html">
23053                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23054                   <li ng-repeat="name in names">
23055                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23056                      {{name}}
23057                    </span>
23058                   </li>
23059                 </ol>
23060              </file>
23061              <file name="style.css">
23062                .odd {
23063                  color: red;
23064                }
23065                .even {
23066                  color: blue;
23067                }
23068              </file>
23069              <file name="protractor.js" type="protractor">
23070                it('should check ng-class-odd and ng-class-even', function() {
23071                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23072                    toMatch(/odd/);
23073                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23074                    toMatch(/even/);
23075                });
23076              </file>
23077            </example>
23078          */
23079         var ngClassOddDirective = classDirective('Odd', 0);
23080
23081         /**
23082          * @ngdoc directive
23083          * @name ngClassEven
23084          * @restrict AC
23085          *
23086          * @description
23087          * The `ngClassOdd` and `ngClassEven` directives work exactly as
23088          * {@link ng.directive:ngClass ngClass}, except they work in
23089          * conjunction with `ngRepeat` and take effect only on odd (even) rows.
23090          *
23091          * This directive can be applied only within the scope of an
23092          * {@link ng.directive:ngRepeat ngRepeat}.
23093          *
23094          * @element ANY
23095          * @param {expression} ngClassEven {@link guide/expression Expression} to eval. The
23096          *   result of the evaluation can be a string representing space delimited class names or an array.
23097          *
23098          * @example
23099            <example>
23100              <file name="index.html">
23101                 <ol ng-init="names=['John', 'Mary', 'Cate', 'Suz']">
23102                   <li ng-repeat="name in names">
23103                    <span ng-class-odd="'odd'" ng-class-even="'even'">
23104                      {{name}} &nbsp; &nbsp; &nbsp;
23105                    </span>
23106                   </li>
23107                 </ol>
23108              </file>
23109              <file name="style.css">
23110                .odd {
23111                  color: red;
23112                }
23113                .even {
23114                  color: blue;
23115                }
23116              </file>
23117              <file name="protractor.js" type="protractor">
23118                it('should check ng-class-odd and ng-class-even', function() {
23119                  expect(element(by.repeater('name in names').row(0).column('name')).getAttribute('class')).
23120                    toMatch(/odd/);
23121                  expect(element(by.repeater('name in names').row(1).column('name')).getAttribute('class')).
23122                    toMatch(/even/);
23123                });
23124              </file>
23125            </example>
23126          */
23127         var ngClassEvenDirective = classDirective('Even', 1);
23128
23129         /**
23130          * @ngdoc directive
23131          * @name ngCloak
23132          * @restrict AC
23133          *
23134          * @description
23135          * The `ngCloak` directive is used to prevent the Angular html template from being briefly
23136          * displayed by the browser in its raw (uncompiled) form while your application is loading. Use this
23137          * directive to avoid the undesirable flicker effect caused by the html template display.
23138          *
23139          * The directive can be applied to the `<body>` element, but the preferred usage is to apply
23140          * multiple `ngCloak` directives to small portions of the page to permit progressive rendering
23141          * of the browser view.
23142          *
23143          * `ngCloak` works in cooperation with the following css rule embedded within `angular.js` and
23144          * `angular.min.js`.
23145          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
23146          *
23147          * ```css
23148          * [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
23149          *   display: none !important;
23150          * }
23151          * ```
23152          *
23153          * When this css rule is loaded by the browser, all html elements (including their children) that
23154          * are tagged with the `ngCloak` directive are hidden. When Angular encounters this directive
23155          * during the compilation of the template it deletes the `ngCloak` element attribute, making
23156          * the compiled element visible.
23157          *
23158          * For the best result, the `angular.js` script must be loaded in the head section of the html
23159          * document; alternatively, the css rule above must be included in the external stylesheet of the
23160          * application.
23161          *
23162          * @element ANY
23163          *
23164          * @example
23165            <example>
23166              <file name="index.html">
23167                 <div id="template1" ng-cloak>{{ 'hello' }}</div>
23168                 <div id="template2" class="ng-cloak">{{ 'world' }}</div>
23169              </file>
23170              <file name="protractor.js" type="protractor">
23171                it('should remove the template directive and css class', function() {
23172                  expect($('#template1').getAttribute('ng-cloak')).
23173                    toBeNull();
23174                  expect($('#template2').getAttribute('ng-cloak')).
23175                    toBeNull();
23176                });
23177              </file>
23178            </example>
23179          *
23180          */
23181         var ngCloakDirective = ngDirective({
23182           compile: function(element, attr) {
23183             attr.$set('ngCloak', undefined);
23184             element.removeClass('ng-cloak');
23185           }
23186         });
23187
23188         /**
23189          * @ngdoc directive
23190          * @name ngController
23191          *
23192          * @description
23193          * The `ngController` directive attaches a controller class to the view. This is a key aspect of how angular
23194          * supports the principles behind the Model-View-Controller design pattern.
23195          *
23196          * MVC components in angular:
23197          *
23198          * * Model — Models are the properties of a scope; scopes are attached to the DOM where scope properties
23199          *   are accessed through bindings.
23200          * * View — The template (HTML with data bindings) that is rendered into the View.
23201          * * Controller — The `ngController` directive specifies a Controller class; the class contains business
23202          *   logic behind the application to decorate the scope with functions and values
23203          *
23204          * Note that you can also attach controllers to the DOM by declaring it in a route definition
23205          * via the {@link ngRoute.$route $route} service. A common mistake is to declare the controller
23206          * again using `ng-controller` in the template itself.  This will cause the controller to be attached
23207          * and executed twice.
23208          *
23209          * @element ANY
23210          * @scope
23211          * @priority 500
23212          * @param {expression} ngController Name of a constructor function registered with the current
23213          * {@link ng.$controllerProvider $controllerProvider} or an {@link guide/expression expression}
23214          * that on the current scope evaluates to a constructor function.
23215          *
23216          * The controller instance can be published into a scope property by specifying
23217          * `ng-controller="as propertyName"`.
23218          *
23219          * If the current `$controllerProvider` is configured to use globals (via
23220          * {@link ng.$controllerProvider#allowGlobals `$controllerProvider.allowGlobals()` }), this may
23221          * also be the name of a globally accessible constructor function (not recommended).
23222          *
23223          * @example
23224          * Here is a simple form for editing user contact information. Adding, removing, clearing, and
23225          * greeting are methods declared on the controller (see source tab). These methods can
23226          * easily be called from the angular markup. Any changes to the data are automatically reflected
23227          * in the View without the need for a manual update.
23228          *
23229          * Two different declaration styles are included below:
23230          *
23231          * * one binds methods and properties directly onto the controller using `this`:
23232          * `ng-controller="SettingsController1 as settings"`
23233          * * one injects `$scope` into the controller:
23234          * `ng-controller="SettingsController2"`
23235          *
23236          * The second option is more common in the Angular community, and is generally used in boilerplates
23237          * and in this guide. However, there are advantages to binding properties directly to the controller
23238          * and avoiding scope.
23239          *
23240          * * Using `controller as` makes it obvious which controller you are accessing in the template when
23241          * multiple controllers apply to an element.
23242          * * If you are writing your controllers as classes you have easier access to the properties and
23243          * methods, which will appear on the scope, from inside the controller code.
23244          * * Since there is always a `.` in the bindings, you don't have to worry about prototypal
23245          * inheritance masking primitives.
23246          *
23247          * This example demonstrates the `controller as` syntax.
23248          *
23249          * <example name="ngControllerAs" module="controllerAsExample">
23250          *   <file name="index.html">
23251          *    <div id="ctrl-as-exmpl" ng-controller="SettingsController1 as settings">
23252          *      <label>Name: <input type="text" ng-model="settings.name"/></label>
23253          *      <button ng-click="settings.greet()">greet</button><br/>
23254          *      Contact:
23255          *      <ul>
23256          *        <li ng-repeat="contact in settings.contacts">
23257          *          <select ng-model="contact.type" aria-label="Contact method" id="select_{{$index}}">
23258          *             <option>phone</option>
23259          *             <option>email</option>
23260          *          </select>
23261          *          <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23262          *          <button ng-click="settings.clearContact(contact)">clear</button>
23263          *          <button ng-click="settings.removeContact(contact)" aria-label="Remove">X</button>
23264          *        </li>
23265          *        <li><button ng-click="settings.addContact()">add</button></li>
23266          *     </ul>
23267          *    </div>
23268          *   </file>
23269          *   <file name="app.js">
23270          *    angular.module('controllerAsExample', [])
23271          *      .controller('SettingsController1', SettingsController1);
23272          *
23273          *    function SettingsController1() {
23274          *      this.name = "John Smith";
23275          *      this.contacts = [
23276          *        {type: 'phone', value: '408 555 1212'},
23277          *        {type: 'email', value: 'john.smith@example.org'} ];
23278          *    }
23279          *
23280          *    SettingsController1.prototype.greet = function() {
23281          *      alert(this.name);
23282          *    };
23283          *
23284          *    SettingsController1.prototype.addContact = function() {
23285          *      this.contacts.push({type: 'email', value: 'yourname@example.org'});
23286          *    };
23287          *
23288          *    SettingsController1.prototype.removeContact = function(contactToRemove) {
23289          *     var index = this.contacts.indexOf(contactToRemove);
23290          *      this.contacts.splice(index, 1);
23291          *    };
23292          *
23293          *    SettingsController1.prototype.clearContact = function(contact) {
23294          *      contact.type = 'phone';
23295          *      contact.value = '';
23296          *    };
23297          *   </file>
23298          *   <file name="protractor.js" type="protractor">
23299          *     it('should check controller as', function() {
23300          *       var container = element(by.id('ctrl-as-exmpl'));
23301          *         expect(container.element(by.model('settings.name'))
23302          *           .getAttribute('value')).toBe('John Smith');
23303          *
23304          *       var firstRepeat =
23305          *           container.element(by.repeater('contact in settings.contacts').row(0));
23306          *       var secondRepeat =
23307          *           container.element(by.repeater('contact in settings.contacts').row(1));
23308          *
23309          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23310          *           .toBe('408 555 1212');
23311          *
23312          *       expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23313          *           .toBe('john.smith@example.org');
23314          *
23315          *       firstRepeat.element(by.buttonText('clear')).click();
23316          *
23317          *       expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23318          *           .toBe('');
23319          *
23320          *       container.element(by.buttonText('add')).click();
23321          *
23322          *       expect(container.element(by.repeater('contact in settings.contacts').row(2))
23323          *           .element(by.model('contact.value'))
23324          *           .getAttribute('value'))
23325          *           .toBe('yourname@example.org');
23326          *     });
23327          *   </file>
23328          * </example>
23329          *
23330          * This example demonstrates the "attach to `$scope`" style of controller.
23331          *
23332          * <example name="ngController" module="controllerExample">
23333          *  <file name="index.html">
23334          *   <div id="ctrl-exmpl" ng-controller="SettingsController2">
23335          *     <label>Name: <input type="text" ng-model="name"/></label>
23336          *     <button ng-click="greet()">greet</button><br/>
23337          *     Contact:
23338          *     <ul>
23339          *       <li ng-repeat="contact in contacts">
23340          *         <select ng-model="contact.type" id="select_{{$index}}">
23341          *            <option>phone</option>
23342          *            <option>email</option>
23343          *         </select>
23344          *         <input type="text" ng-model="contact.value" aria-labelledby="select_{{$index}}" />
23345          *         <button ng-click="clearContact(contact)">clear</button>
23346          *         <button ng-click="removeContact(contact)">X</button>
23347          *       </li>
23348          *       <li>[ <button ng-click="addContact()">add</button> ]</li>
23349          *    </ul>
23350          *   </div>
23351          *  </file>
23352          *  <file name="app.js">
23353          *   angular.module('controllerExample', [])
23354          *     .controller('SettingsController2', ['$scope', SettingsController2]);
23355          *
23356          *   function SettingsController2($scope) {
23357          *     $scope.name = "John Smith";
23358          *     $scope.contacts = [
23359          *       {type:'phone', value:'408 555 1212'},
23360          *       {type:'email', value:'john.smith@example.org'} ];
23361          *
23362          *     $scope.greet = function() {
23363          *       alert($scope.name);
23364          *     };
23365          *
23366          *     $scope.addContact = function() {
23367          *       $scope.contacts.push({type:'email', value:'yourname@example.org'});
23368          *     };
23369          *
23370          *     $scope.removeContact = function(contactToRemove) {
23371          *       var index = $scope.contacts.indexOf(contactToRemove);
23372          *       $scope.contacts.splice(index, 1);
23373          *     };
23374          *
23375          *     $scope.clearContact = function(contact) {
23376          *       contact.type = 'phone';
23377          *       contact.value = '';
23378          *     };
23379          *   }
23380          *  </file>
23381          *  <file name="protractor.js" type="protractor">
23382          *    it('should check controller', function() {
23383          *      var container = element(by.id('ctrl-exmpl'));
23384          *
23385          *      expect(container.element(by.model('name'))
23386          *          .getAttribute('value')).toBe('John Smith');
23387          *
23388          *      var firstRepeat =
23389          *          container.element(by.repeater('contact in contacts').row(0));
23390          *      var secondRepeat =
23391          *          container.element(by.repeater('contact in contacts').row(1));
23392          *
23393          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23394          *          .toBe('408 555 1212');
23395          *      expect(secondRepeat.element(by.model('contact.value')).getAttribute('value'))
23396          *          .toBe('john.smith@example.org');
23397          *
23398          *      firstRepeat.element(by.buttonText('clear')).click();
23399          *
23400          *      expect(firstRepeat.element(by.model('contact.value')).getAttribute('value'))
23401          *          .toBe('');
23402          *
23403          *      container.element(by.buttonText('add')).click();
23404          *
23405          *      expect(container.element(by.repeater('contact in contacts').row(2))
23406          *          .element(by.model('contact.value'))
23407          *          .getAttribute('value'))
23408          *          .toBe('yourname@example.org');
23409          *    });
23410          *  </file>
23411          *</example>
23412
23413          */
23414         var ngControllerDirective = [function() {
23415           return {
23416             restrict: 'A',
23417             scope: true,
23418             controller: '@',
23419             priority: 500
23420           };
23421         }];
23422
23423         /**
23424          * @ngdoc directive
23425          * @name ngCsp
23426          *
23427          * @element html
23428          * @description
23429          *
23430          * Angular has some features that can break certain
23431          * [CSP (Content Security Policy)](https://developer.mozilla.org/en/Security/CSP) rules.
23432          *
23433          * If you intend to implement these rules then you must tell Angular not to use these features.
23434          *
23435          * This is necessary when developing things like Google Chrome Extensions or Universal Windows Apps.
23436          *
23437          *
23438          * The following rules affect Angular:
23439          *
23440          * * `unsafe-eval`: this rule forbids apps to use `eval` or `Function(string)` generated functions
23441          * (among other things). Angular makes use of this in the {@link $parse} service to provide a 30%
23442          * increase in the speed of evaluating Angular expressions.
23443          *
23444          * * `unsafe-inline`: this rule forbids apps from inject custom styles into the document. Angular
23445          * makes use of this to include some CSS rules (e.g. {@link ngCloak} and {@link ngHide}).
23446          * To make these directives work when a CSP rule is blocking inline styles, you must link to the
23447          * `angular-csp.css` in your HTML manually.
23448          *
23449          * If you do not provide `ngCsp` then Angular tries to autodetect if CSP is blocking unsafe-eval
23450          * and automatically deactivates this feature in the {@link $parse} service. This autodetection,
23451          * however, triggers a CSP error to be logged in the console:
23452          *
23453          * ```
23454          * Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of
23455          * script in the following Content Security Policy directive: "default-src 'self'". Note that
23456          * 'script-src' was not explicitly set, so 'default-src' is used as a fallback.
23457          * ```
23458          *
23459          * This error is harmless but annoying. To prevent the error from showing up, put the `ngCsp`
23460          * directive on an element of the HTML document that appears before the `<script>` tag that loads
23461          * the `angular.js` file.
23462          *
23463          * *Note: This directive is only available in the `ng-csp` and `data-ng-csp` attribute form.*
23464          *
23465          * You can specify which of the CSP related Angular features should be deactivated by providing
23466          * a value for the `ng-csp` attribute. The options are as follows:
23467          *
23468          * * no-inline-style: this stops Angular from injecting CSS styles into the DOM
23469          *
23470          * * no-unsafe-eval: this stops Angular from optimising $parse with unsafe eval of strings
23471          *
23472          * You can use these values in the following combinations:
23473          *
23474          *
23475          * * No declaration means that Angular will assume that you can do inline styles, but it will do
23476          * a runtime check for unsafe-eval. E.g. `<body>`. This is backwardly compatible with previous versions
23477          * of Angular.
23478          *
23479          * * A simple `ng-csp` (or `data-ng-csp`) attribute will tell Angular to deactivate both inline
23480          * styles and unsafe eval. E.g. `<body ng-csp>`. This is backwardly compatible with previous versions
23481          * of Angular.
23482          *
23483          * * Specifying only `no-unsafe-eval` tells Angular that we must not use eval, but that we can inject
23484          * inline styles. E.g. `<body ng-csp="no-unsafe-eval">`.
23485          *
23486          * * Specifying only `no-inline-style` tells Angular that we must not inject styles, but that we can
23487          * run eval - no automcatic check for unsafe eval will occur. E.g. `<body ng-csp="no-inline-style">`
23488          *
23489          * * Specifying both `no-unsafe-eval` and `no-inline-style` tells Angular that we must not inject
23490          * styles nor use eval, which is the same as an empty: ng-csp.
23491          * E.g.`<body ng-csp="no-inline-style;no-unsafe-eval">`
23492          *
23493          * @example
23494          * This example shows how to apply the `ngCsp` directive to the `html` tag.
23495            ```html
23496              <!doctype html>
23497              <html ng-app ng-csp>
23498              ...
23499              ...
23500              </html>
23501            ```
23502           * @example
23503               // Note: the suffix `.csp` in the example name triggers
23504               // csp mode in our http server!
23505               <example name="example.csp" module="cspExample" ng-csp="true">
23506                 <file name="index.html">
23507                   <div ng-controller="MainController as ctrl">
23508                     <div>
23509                       <button ng-click="ctrl.inc()" id="inc">Increment</button>
23510                       <span id="counter">
23511                         {{ctrl.counter}}
23512                       </span>
23513                     </div>
23514
23515                     <div>
23516                       <button ng-click="ctrl.evil()" id="evil">Evil</button>
23517                       <span id="evilError">
23518                         {{ctrl.evilError}}
23519                       </span>
23520                     </div>
23521                   </div>
23522                 </file>
23523                 <file name="script.js">
23524                    angular.module('cspExample', [])
23525                      .controller('MainController', function() {
23526                         this.counter = 0;
23527                         this.inc = function() {
23528                           this.counter++;
23529                         };
23530                         this.evil = function() {
23531                           // jshint evil:true
23532                           try {
23533                             eval('1+2');
23534                           } catch (e) {
23535                             this.evilError = e.message;
23536                           }
23537                         };
23538                       });
23539                 </file>
23540                 <file name="protractor.js" type="protractor">
23541                   var util, webdriver;
23542
23543                   var incBtn = element(by.id('inc'));
23544                   var counter = element(by.id('counter'));
23545                   var evilBtn = element(by.id('evil'));
23546                   var evilError = element(by.id('evilError'));
23547
23548                   function getAndClearSevereErrors() {
23549                     return browser.manage().logs().get('browser').then(function(browserLog) {
23550                       return browserLog.filter(function(logEntry) {
23551                         return logEntry.level.value > webdriver.logging.Level.WARNING.value;
23552                       });
23553                     });
23554                   }
23555
23556                   function clearErrors() {
23557                     getAndClearSevereErrors();
23558                   }
23559
23560                   function expectNoErrors() {
23561                     getAndClearSevereErrors().then(function(filteredLog) {
23562                       expect(filteredLog.length).toEqual(0);
23563                       if (filteredLog.length) {
23564                         console.log('browser console errors: ' + util.inspect(filteredLog));
23565                       }
23566                     });
23567                   }
23568
23569                   function expectError(regex) {
23570                     getAndClearSevereErrors().then(function(filteredLog) {
23571                       var found = false;
23572                       filteredLog.forEach(function(log) {
23573                         if (log.message.match(regex)) {
23574                           found = true;
23575                         }
23576                       });
23577                       if (!found) {
23578                         throw new Error('expected an error that matches ' + regex);
23579                       }
23580                     });
23581                   }
23582
23583                   beforeEach(function() {
23584                     util = require('util');
23585                     webdriver = require('protractor/node_modules/selenium-webdriver');
23586                   });
23587
23588                   // For now, we only test on Chrome,
23589                   // as Safari does not load the page with Protractor's injected scripts,
23590                   // and Firefox webdriver always disables content security policy (#6358)
23591                   if (browser.params.browser !== 'chrome') {
23592                     return;
23593                   }
23594
23595                   it('should not report errors when the page is loaded', function() {
23596                     // clear errors so we are not dependent on previous tests
23597                     clearErrors();
23598                     // Need to reload the page as the page is already loaded when
23599                     // we come here
23600                     browser.driver.getCurrentUrl().then(function(url) {
23601                       browser.get(url);
23602                     });
23603                     expectNoErrors();
23604                   });
23605
23606                   it('should evaluate expressions', function() {
23607                     expect(counter.getText()).toEqual('0');
23608                     incBtn.click();
23609                     expect(counter.getText()).toEqual('1');
23610                     expectNoErrors();
23611                   });
23612
23613                   it('should throw and report an error when using "eval"', function() {
23614                     evilBtn.click();
23615                     expect(evilError.getText()).toMatch(/Content Security Policy/);
23616                     expectError(/Content Security Policy/);
23617                   });
23618                 </file>
23619               </example>
23620           */
23621
23622         // ngCsp is not implemented as a proper directive any more, because we need it be processed while we
23623         // bootstrap the system (before $parse is instantiated), for this reason we just have
23624         // the csp() fn that looks for the `ng-csp` attribute anywhere in the current doc
23625
23626         /**
23627          * @ngdoc directive
23628          * @name ngClick
23629          *
23630          * @description
23631          * The ngClick directive allows you to specify custom behavior when
23632          * an element is clicked.
23633          *
23634          * @element ANY
23635          * @priority 0
23636          * @param {expression} ngClick {@link guide/expression Expression} to evaluate upon
23637          * click. ({@link guide/expression#-event- Event object is available as `$event`})
23638          *
23639          * @example
23640            <example>
23641              <file name="index.html">
23642               <button ng-click="count = count + 1" ng-init="count=0">
23643                 Increment
23644               </button>
23645               <span>
23646                 count: {{count}}
23647               </span>
23648              </file>
23649              <file name="protractor.js" type="protractor">
23650                it('should check ng-click', function() {
23651                  expect(element(by.binding('count')).getText()).toMatch('0');
23652                  element(by.css('button')).click();
23653                  expect(element(by.binding('count')).getText()).toMatch('1');
23654                });
23655              </file>
23656            </example>
23657          */
23658         /*
23659          * A collection of directives that allows creation of custom event handlers that are defined as
23660          * angular expressions and are compiled and executed within the current scope.
23661          */
23662         var ngEventDirectives = {};
23663
23664         // For events that might fire synchronously during DOM manipulation
23665         // we need to execute their event handlers asynchronously using $evalAsync,
23666         // so that they are not executed in an inconsistent state.
23667         var forceAsyncEvents = {
23668           'blur': true,
23669           'focus': true
23670         };
23671         forEach(
23672           'click dblclick mousedown mouseup mouseover mouseout mousemove mouseenter mouseleave keydown keyup keypress submit focus blur copy cut paste'.split(' '),
23673           function(eventName) {
23674             var directiveName = directiveNormalize('ng-' + eventName);
23675             ngEventDirectives[directiveName] = ['$parse', '$rootScope', function($parse, $rootScope) {
23676               return {
23677                 restrict: 'A',
23678                 compile: function($element, attr) {
23679                   // We expose the powerful $event object on the scope that provides access to the Window,
23680                   // etc. that isn't protected by the fast paths in $parse.  We explicitly request better
23681                   // checks at the cost of speed since event handler expressions are not executed as
23682                   // frequently as regular change detection.
23683                   var fn = $parse(attr[directiveName], /* interceptorFn */ null, /* expensiveChecks */ true);
23684                   return function ngEventHandler(scope, element) {
23685                     element.on(eventName, function(event) {
23686                       var callback = function() {
23687                         fn(scope, {$event:event});
23688                       };
23689                       if (forceAsyncEvents[eventName] && $rootScope.$$phase) {
23690                         scope.$evalAsync(callback);
23691                       } else {
23692                         scope.$apply(callback);
23693                       }
23694                     });
23695                   };
23696                 }
23697               };
23698             }];
23699           }
23700         );
23701
23702         /**
23703          * @ngdoc directive
23704          * @name ngDblclick
23705          *
23706          * @description
23707          * The `ngDblclick` directive allows you to specify custom behavior on a dblclick event.
23708          *
23709          * @element ANY
23710          * @priority 0
23711          * @param {expression} ngDblclick {@link guide/expression Expression} to evaluate upon
23712          * a dblclick. (The Event object is available as `$event`)
23713          *
23714          * @example
23715            <example>
23716              <file name="index.html">
23717               <button ng-dblclick="count = count + 1" ng-init="count=0">
23718                 Increment (on double click)
23719               </button>
23720               count: {{count}}
23721              </file>
23722            </example>
23723          */
23724
23725
23726         /**
23727          * @ngdoc directive
23728          * @name ngMousedown
23729          *
23730          * @description
23731          * The ngMousedown directive allows you to specify custom behavior on mousedown event.
23732          *
23733          * @element ANY
23734          * @priority 0
23735          * @param {expression} ngMousedown {@link guide/expression Expression} to evaluate upon
23736          * mousedown. ({@link guide/expression#-event- Event object is available as `$event`})
23737          *
23738          * @example
23739            <example>
23740              <file name="index.html">
23741               <button ng-mousedown="count = count + 1" ng-init="count=0">
23742                 Increment (on mouse down)
23743               </button>
23744               count: {{count}}
23745              </file>
23746            </example>
23747          */
23748
23749
23750         /**
23751          * @ngdoc directive
23752          * @name ngMouseup
23753          *
23754          * @description
23755          * Specify custom behavior on mouseup event.
23756          *
23757          * @element ANY
23758          * @priority 0
23759          * @param {expression} ngMouseup {@link guide/expression Expression} to evaluate upon
23760          * mouseup. ({@link guide/expression#-event- Event object is available as `$event`})
23761          *
23762          * @example
23763            <example>
23764              <file name="index.html">
23765               <button ng-mouseup="count = count + 1" ng-init="count=0">
23766                 Increment (on mouse up)
23767               </button>
23768               count: {{count}}
23769              </file>
23770            </example>
23771          */
23772
23773         /**
23774          * @ngdoc directive
23775          * @name ngMouseover
23776          *
23777          * @description
23778          * Specify custom behavior on mouseover event.
23779          *
23780          * @element ANY
23781          * @priority 0
23782          * @param {expression} ngMouseover {@link guide/expression Expression} to evaluate upon
23783          * mouseover. ({@link guide/expression#-event- Event object is available as `$event`})
23784          *
23785          * @example
23786            <example>
23787              <file name="index.html">
23788               <button ng-mouseover="count = count + 1" ng-init="count=0">
23789                 Increment (when mouse is over)
23790               </button>
23791               count: {{count}}
23792              </file>
23793            </example>
23794          */
23795
23796
23797         /**
23798          * @ngdoc directive
23799          * @name ngMouseenter
23800          *
23801          * @description
23802          * Specify custom behavior on mouseenter event.
23803          *
23804          * @element ANY
23805          * @priority 0
23806          * @param {expression} ngMouseenter {@link guide/expression Expression} to evaluate upon
23807          * mouseenter. ({@link guide/expression#-event- Event object is available as `$event`})
23808          *
23809          * @example
23810            <example>
23811              <file name="index.html">
23812               <button ng-mouseenter="count = count + 1" ng-init="count=0">
23813                 Increment (when mouse enters)
23814               </button>
23815               count: {{count}}
23816              </file>
23817            </example>
23818          */
23819
23820
23821         /**
23822          * @ngdoc directive
23823          * @name ngMouseleave
23824          *
23825          * @description
23826          * Specify custom behavior on mouseleave event.
23827          *
23828          * @element ANY
23829          * @priority 0
23830          * @param {expression} ngMouseleave {@link guide/expression Expression} to evaluate upon
23831          * mouseleave. ({@link guide/expression#-event- Event object is available as `$event`})
23832          *
23833          * @example
23834            <example>
23835              <file name="index.html">
23836               <button ng-mouseleave="count = count + 1" ng-init="count=0">
23837                 Increment (when mouse leaves)
23838               </button>
23839               count: {{count}}
23840              </file>
23841            </example>
23842          */
23843
23844
23845         /**
23846          * @ngdoc directive
23847          * @name ngMousemove
23848          *
23849          * @description
23850          * Specify custom behavior on mousemove event.
23851          *
23852          * @element ANY
23853          * @priority 0
23854          * @param {expression} ngMousemove {@link guide/expression Expression} to evaluate upon
23855          * mousemove. ({@link guide/expression#-event- Event object is available as `$event`})
23856          *
23857          * @example
23858            <example>
23859              <file name="index.html">
23860               <button ng-mousemove="count = count + 1" ng-init="count=0">
23861                 Increment (when mouse moves)
23862               </button>
23863               count: {{count}}
23864              </file>
23865            </example>
23866          */
23867
23868
23869         /**
23870          * @ngdoc directive
23871          * @name ngKeydown
23872          *
23873          * @description
23874          * Specify custom behavior on keydown event.
23875          *
23876          * @element ANY
23877          * @priority 0
23878          * @param {expression} ngKeydown {@link guide/expression Expression} to evaluate upon
23879          * keydown. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23880          *
23881          * @example
23882            <example>
23883              <file name="index.html">
23884               <input ng-keydown="count = count + 1" ng-init="count=0">
23885               key down count: {{count}}
23886              </file>
23887            </example>
23888          */
23889
23890
23891         /**
23892          * @ngdoc directive
23893          * @name ngKeyup
23894          *
23895          * @description
23896          * Specify custom behavior on keyup event.
23897          *
23898          * @element ANY
23899          * @priority 0
23900          * @param {expression} ngKeyup {@link guide/expression Expression} to evaluate upon
23901          * keyup. (Event object is available as `$event` and can be interrogated for keyCode, altKey, etc.)
23902          *
23903          * @example
23904            <example>
23905              <file name="index.html">
23906                <p>Typing in the input box below updates the key count</p>
23907                <input ng-keyup="count = count + 1" ng-init="count=0"> key up count: {{count}}
23908
23909                <p>Typing in the input box below updates the keycode</p>
23910                <input ng-keyup="event=$event">
23911                <p>event keyCode: {{ event.keyCode }}</p>
23912                <p>event altKey: {{ event.altKey }}</p>
23913              </file>
23914            </example>
23915          */
23916
23917
23918         /**
23919          * @ngdoc directive
23920          * @name ngKeypress
23921          *
23922          * @description
23923          * Specify custom behavior on keypress event.
23924          *
23925          * @element ANY
23926          * @param {expression} ngKeypress {@link guide/expression Expression} to evaluate upon
23927          * keypress. ({@link guide/expression#-event- Event object is available as `$event`}
23928          * and can be interrogated for keyCode, altKey, etc.)
23929          *
23930          * @example
23931            <example>
23932              <file name="index.html">
23933               <input ng-keypress="count = count + 1" ng-init="count=0">
23934               key press count: {{count}}
23935              </file>
23936            </example>
23937          */
23938
23939
23940         /**
23941          * @ngdoc directive
23942          * @name ngSubmit
23943          *
23944          * @description
23945          * Enables binding angular expressions to onsubmit events.
23946          *
23947          * Additionally it prevents the default action (which for form means sending the request to the
23948          * server and reloading the current page), but only if the form does not contain `action`,
23949          * `data-action`, or `x-action` attributes.
23950          *
23951          * <div class="alert alert-warning">
23952          * **Warning:** Be careful not to cause "double-submission" by using both the `ngClick` and
23953          * `ngSubmit` handlers together. See the
23954          * {@link form#submitting-a-form-and-preventing-the-default-action `form` directive documentation}
23955          * for a detailed discussion of when `ngSubmit` may be triggered.
23956          * </div>
23957          *
23958          * @element form
23959          * @priority 0
23960          * @param {expression} ngSubmit {@link guide/expression Expression} to eval.
23961          * ({@link guide/expression#-event- Event object is available as `$event`})
23962          *
23963          * @example
23964            <example module="submitExample">
23965              <file name="index.html">
23966               <script>
23967                 angular.module('submitExample', [])
23968                   .controller('ExampleController', ['$scope', function($scope) {
23969                     $scope.list = [];
23970                     $scope.text = 'hello';
23971                     $scope.submit = function() {
23972                       if ($scope.text) {
23973                         $scope.list.push(this.text);
23974                         $scope.text = '';
23975                       }
23976                     };
23977                   }]);
23978               </script>
23979               <form ng-submit="submit()" ng-controller="ExampleController">
23980                 Enter text and hit enter:
23981                 <input type="text" ng-model="text" name="text" />
23982                 <input type="submit" id="submit" value="Submit" />
23983                 <pre>list={{list}}</pre>
23984               </form>
23985              </file>
23986              <file name="protractor.js" type="protractor">
23987                it('should check ng-submit', function() {
23988                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23989                  element(by.css('#submit')).click();
23990                  expect(element(by.binding('list')).getText()).toContain('hello');
23991                  expect(element(by.model('text')).getAttribute('value')).toBe('');
23992                });
23993                it('should ignore empty strings', function() {
23994                  expect(element(by.binding('list')).getText()).toBe('list=[]');
23995                  element(by.css('#submit')).click();
23996                  element(by.css('#submit')).click();
23997                  expect(element(by.binding('list')).getText()).toContain('hello');
23998                 });
23999              </file>
24000            </example>
24001          */
24002
24003         /**
24004          * @ngdoc directive
24005          * @name ngFocus
24006          *
24007          * @description
24008          * Specify custom behavior on focus event.
24009          *
24010          * Note: As the `focus` event is executed synchronously when calling `input.focus()`
24011          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24012          * during an `$apply` to ensure a consistent state.
24013          *
24014          * @element window, input, select, textarea, a
24015          * @priority 0
24016          * @param {expression} ngFocus {@link guide/expression Expression} to evaluate upon
24017          * focus. ({@link guide/expression#-event- Event object is available as `$event`})
24018          *
24019          * @example
24020          * See {@link ng.directive:ngClick ngClick}
24021          */
24022
24023         /**
24024          * @ngdoc directive
24025          * @name ngBlur
24026          *
24027          * @description
24028          * Specify custom behavior on blur event.
24029          *
24030          * A [blur event](https://developer.mozilla.org/en-US/docs/Web/Events/blur) fires when
24031          * an element has lost focus.
24032          *
24033          * Note: As the `blur` event is executed synchronously also during DOM manipulations
24034          * (e.g. removing a focussed input),
24035          * AngularJS executes the expression using `scope.$evalAsync` if the event is fired
24036          * during an `$apply` to ensure a consistent state.
24037          *
24038          * @element window, input, select, textarea, a
24039          * @priority 0
24040          * @param {expression} ngBlur {@link guide/expression Expression} to evaluate upon
24041          * blur. ({@link guide/expression#-event- Event object is available as `$event`})
24042          *
24043          * @example
24044          * See {@link ng.directive:ngClick ngClick}
24045          */
24046
24047         /**
24048          * @ngdoc directive
24049          * @name ngCopy
24050          *
24051          * @description
24052          * Specify custom behavior on copy event.
24053          *
24054          * @element window, input, select, textarea, a
24055          * @priority 0
24056          * @param {expression} ngCopy {@link guide/expression Expression} to evaluate upon
24057          * copy. ({@link guide/expression#-event- Event object is available as `$event`})
24058          *
24059          * @example
24060            <example>
24061              <file name="index.html">
24062               <input ng-copy="copied=true" ng-init="copied=false; value='copy me'" ng-model="value">
24063               copied: {{copied}}
24064              </file>
24065            </example>
24066          */
24067
24068         /**
24069          * @ngdoc directive
24070          * @name ngCut
24071          *
24072          * @description
24073          * Specify custom behavior on cut event.
24074          *
24075          * @element window, input, select, textarea, a
24076          * @priority 0
24077          * @param {expression} ngCut {@link guide/expression Expression} to evaluate upon
24078          * cut. ({@link guide/expression#-event- Event object is available as `$event`})
24079          *
24080          * @example
24081            <example>
24082              <file name="index.html">
24083               <input ng-cut="cut=true" ng-init="cut=false; value='cut me'" ng-model="value">
24084               cut: {{cut}}
24085              </file>
24086            </example>
24087          */
24088
24089         /**
24090          * @ngdoc directive
24091          * @name ngPaste
24092          *
24093          * @description
24094          * Specify custom behavior on paste event.
24095          *
24096          * @element window, input, select, textarea, a
24097          * @priority 0
24098          * @param {expression} ngPaste {@link guide/expression Expression} to evaluate upon
24099          * paste. ({@link guide/expression#-event- Event object is available as `$event`})
24100          *
24101          * @example
24102            <example>
24103              <file name="index.html">
24104               <input ng-paste="paste=true" ng-init="paste=false" placeholder='paste here'>
24105               pasted: {{paste}}
24106              </file>
24107            </example>
24108          */
24109
24110         /**
24111          * @ngdoc directive
24112          * @name ngIf
24113          * @restrict A
24114          * @multiElement
24115          *
24116          * @description
24117          * The `ngIf` directive removes or recreates a portion of the DOM tree based on an
24118          * {expression}. If the expression assigned to `ngIf` evaluates to a false
24119          * value then the element is removed from the DOM, otherwise a clone of the
24120          * element is reinserted into the DOM.
24121          *
24122          * `ngIf` differs from `ngShow` and `ngHide` in that `ngIf` completely removes and recreates the
24123          * element in the DOM rather than changing its visibility via the `display` css property.  A common
24124          * case when this difference is significant is when using css selectors that rely on an element's
24125          * position within the DOM, such as the `:first-child` or `:last-child` pseudo-classes.
24126          *
24127          * Note that when an element is removed using `ngIf` its scope is destroyed and a new scope
24128          * is created when the element is restored.  The scope created within `ngIf` inherits from
24129          * its parent scope using
24130          * [prototypal inheritance](https://github.com/angular/angular.js/wiki/Understanding-Scopes#javascript-prototypal-inheritance).
24131          * An important implication of this is if `ngModel` is used within `ngIf` to bind to
24132          * a javascript primitive defined in the parent scope. In this case any modifications made to the
24133          * variable within the child scope will override (hide) the value in the parent scope.
24134          *
24135          * Also, `ngIf` recreates elements using their compiled state. An example of this behavior
24136          * is if an element's class attribute is directly modified after it's compiled, using something like
24137          * jQuery's `.addClass()` method, and the element is later removed. When `ngIf` recreates the element
24138          * the added class will be lost because the original compiled state is used to regenerate the element.
24139          *
24140          * Additionally, you can provide animations via the `ngAnimate` module to animate the `enter`
24141          * and `leave` effects.
24142          *
24143          * @animations
24144          * enter - happens just after the `ngIf` contents change and a new DOM element is created and injected into the `ngIf` container
24145          * leave - happens just before the `ngIf` contents are removed from the DOM
24146          *
24147          * @element ANY
24148          * @scope
24149          * @priority 600
24150          * @param {expression} ngIf If the {@link guide/expression expression} is falsy then
24151          *     the element is removed from the DOM tree. If it is truthy a copy of the compiled
24152          *     element is added to the DOM tree.
24153          *
24154          * @example
24155           <example module="ngAnimate" deps="angular-animate.js" animations="true">
24156             <file name="index.html">
24157               <label>Click me: <input type="checkbox" ng-model="checked" ng-init="checked=true" /></label><br/>
24158               Show when checked:
24159               <span ng-if="checked" class="animate-if">
24160                 This is removed when the checkbox is unchecked.
24161               </span>
24162             </file>
24163             <file name="animations.css">
24164               .animate-if {
24165                 background:white;
24166                 border:1px solid black;
24167                 padding:10px;
24168               }
24169
24170               .animate-if.ng-enter, .animate-if.ng-leave {
24171                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24172               }
24173
24174               .animate-if.ng-enter,
24175               .animate-if.ng-leave.ng-leave-active {
24176                 opacity:0;
24177               }
24178
24179               .animate-if.ng-leave,
24180               .animate-if.ng-enter.ng-enter-active {
24181                 opacity:1;
24182               }
24183             </file>
24184           </example>
24185          */
24186         var ngIfDirective = ['$animate', function($animate) {
24187           return {
24188             multiElement: true,
24189             transclude: 'element',
24190             priority: 600,
24191             terminal: true,
24192             restrict: 'A',
24193             $$tlb: true,
24194             link: function($scope, $element, $attr, ctrl, $transclude) {
24195                 var block, childScope, previousElements;
24196                 $scope.$watch($attr.ngIf, function ngIfWatchAction(value) {
24197
24198                   if (value) {
24199                     if (!childScope) {
24200                       $transclude(function(clone, newScope) {
24201                         childScope = newScope;
24202                         clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
24203                         // Note: We only need the first/last node of the cloned nodes.
24204                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
24205                         // by a directive with templateUrl when its template arrives.
24206                         block = {
24207                           clone: clone
24208                         };
24209                         $animate.enter(clone, $element.parent(), $element);
24210                       });
24211                     }
24212                   } else {
24213                     if (previousElements) {
24214                       previousElements.remove();
24215                       previousElements = null;
24216                     }
24217                     if (childScope) {
24218                       childScope.$destroy();
24219                       childScope = null;
24220                     }
24221                     if (block) {
24222                       previousElements = getBlockNodes(block.clone);
24223                       $animate.leave(previousElements).then(function() {
24224                         previousElements = null;
24225                       });
24226                       block = null;
24227                     }
24228                   }
24229                 });
24230             }
24231           };
24232         }];
24233
24234         /**
24235          * @ngdoc directive
24236          * @name ngInclude
24237          * @restrict ECA
24238          *
24239          * @description
24240          * Fetches, compiles and includes an external HTML fragment.
24241          *
24242          * By default, the template URL is restricted to the same domain and protocol as the
24243          * application document. This is done by calling {@link $sce#getTrustedResourceUrl
24244          * $sce.getTrustedResourceUrl} on it. To load templates from other domains or protocols
24245          * you may either {@link ng.$sceDelegateProvider#resourceUrlWhitelist whitelist them} or
24246          * {@link $sce#trustAsResourceUrl wrap them} as trusted values. Refer to Angular's {@link
24247          * ng.$sce Strict Contextual Escaping}.
24248          *
24249          * In addition, the browser's
24250          * [Same Origin Policy](https://code.google.com/p/browsersec/wiki/Part2#Same-origin_policy_for_XMLHttpRequest)
24251          * and [Cross-Origin Resource Sharing (CORS)](http://www.w3.org/TR/cors/)
24252          * policy may further restrict whether the template is successfully loaded.
24253          * For example, `ngInclude` won't work for cross-domain requests on all browsers and for `file://`
24254          * access on some browsers.
24255          *
24256          * @animations
24257          * enter - animation is used to bring new content into the browser.
24258          * leave - animation is used to animate existing content away.
24259          *
24260          * The enter and leave animation occur concurrently.
24261          *
24262          * @scope
24263          * @priority 400
24264          *
24265          * @param {string} ngInclude|src angular expression evaluating to URL. If the source is a string constant,
24266          *                 make sure you wrap it in **single** quotes, e.g. `src="'myPartialTemplate.html'"`.
24267          * @param {string=} onload Expression to evaluate when a new partial is loaded.
24268          *                  <div class="alert alert-warning">
24269          *                  **Note:** When using onload on SVG elements in IE11, the browser will try to call
24270          *                  a function with the name on the window element, which will usually throw a
24271          *                  "function is undefined" error. To fix this, you can instead use `data-onload` or a
24272          *                  different form that {@link guide/directive#normalization matches} `onload`.
24273          *                  </div>
24274            *
24275          * @param {string=} autoscroll Whether `ngInclude` should call {@link ng.$anchorScroll
24276          *                  $anchorScroll} to scroll the viewport after the content is loaded.
24277          *
24278          *                  - If the attribute is not set, disable scrolling.
24279          *                  - If the attribute is set without value, enable scrolling.
24280          *                  - Otherwise enable scrolling only if the expression evaluates to truthy value.
24281          *
24282          * @example
24283           <example module="includeExample" deps="angular-animate.js" animations="true">
24284             <file name="index.html">
24285              <div ng-controller="ExampleController">
24286                <select ng-model="template" ng-options="t.name for t in templates">
24287                 <option value="">(blank)</option>
24288                </select>
24289                url of the template: <code>{{template.url}}</code>
24290                <hr/>
24291                <div class="slide-animate-container">
24292                  <div class="slide-animate" ng-include="template.url"></div>
24293                </div>
24294              </div>
24295             </file>
24296             <file name="script.js">
24297               angular.module('includeExample', ['ngAnimate'])
24298                 .controller('ExampleController', ['$scope', function($scope) {
24299                   $scope.templates =
24300                     [ { name: 'template1.html', url: 'template1.html'},
24301                       { name: 'template2.html', url: 'template2.html'} ];
24302                   $scope.template = $scope.templates[0];
24303                 }]);
24304              </file>
24305             <file name="template1.html">
24306               Content of template1.html
24307             </file>
24308             <file name="template2.html">
24309               Content of template2.html
24310             </file>
24311             <file name="animations.css">
24312               .slide-animate-container {
24313                 position:relative;
24314                 background:white;
24315                 border:1px solid black;
24316                 height:40px;
24317                 overflow:hidden;
24318               }
24319
24320               .slide-animate {
24321                 padding:10px;
24322               }
24323
24324               .slide-animate.ng-enter, .slide-animate.ng-leave {
24325                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
24326
24327                 position:absolute;
24328                 top:0;
24329                 left:0;
24330                 right:0;
24331                 bottom:0;
24332                 display:block;
24333                 padding:10px;
24334               }
24335
24336               .slide-animate.ng-enter {
24337                 top:-50px;
24338               }
24339               .slide-animate.ng-enter.ng-enter-active {
24340                 top:0;
24341               }
24342
24343               .slide-animate.ng-leave {
24344                 top:0;
24345               }
24346               .slide-animate.ng-leave.ng-leave-active {
24347                 top:50px;
24348               }
24349             </file>
24350             <file name="protractor.js" type="protractor">
24351               var templateSelect = element(by.model('template'));
24352               var includeElem = element(by.css('[ng-include]'));
24353
24354               it('should load template1.html', function() {
24355                 expect(includeElem.getText()).toMatch(/Content of template1.html/);
24356               });
24357
24358               it('should load template2.html', function() {
24359                 if (browser.params.browser == 'firefox') {
24360                   // Firefox can't handle using selects
24361                   // See https://github.com/angular/protractor/issues/480
24362                   return;
24363                 }
24364                 templateSelect.click();
24365                 templateSelect.all(by.css('option')).get(2).click();
24366                 expect(includeElem.getText()).toMatch(/Content of template2.html/);
24367               });
24368
24369               it('should change to blank', function() {
24370                 if (browser.params.browser == 'firefox') {
24371                   // Firefox can't handle using selects
24372                   return;
24373                 }
24374                 templateSelect.click();
24375                 templateSelect.all(by.css('option')).get(0).click();
24376                 expect(includeElem.isPresent()).toBe(false);
24377               });
24378             </file>
24379           </example>
24380          */
24381
24382
24383         /**
24384          * @ngdoc event
24385          * @name ngInclude#$includeContentRequested
24386          * @eventType emit on the scope ngInclude was declared in
24387          * @description
24388          * Emitted every time the ngInclude content is requested.
24389          *
24390          * @param {Object} angularEvent Synthetic event object.
24391          * @param {String} src URL of content to load.
24392          */
24393
24394
24395         /**
24396          * @ngdoc event
24397          * @name ngInclude#$includeContentLoaded
24398          * @eventType emit on the current ngInclude scope
24399          * @description
24400          * Emitted every time the ngInclude content is reloaded.
24401          *
24402          * @param {Object} angularEvent Synthetic event object.
24403          * @param {String} src URL of content to load.
24404          */
24405
24406
24407         /**
24408          * @ngdoc event
24409          * @name ngInclude#$includeContentError
24410          * @eventType emit on the scope ngInclude was declared in
24411          * @description
24412          * Emitted when a template HTTP request yields an erroneous response (status < 200 || status > 299)
24413          *
24414          * @param {Object} angularEvent Synthetic event object.
24415          * @param {String} src URL of content to load.
24416          */
24417         var ngIncludeDirective = ['$templateRequest', '$anchorScroll', '$animate',
24418                           function($templateRequest,   $anchorScroll,   $animate) {
24419           return {
24420             restrict: 'ECA',
24421             priority: 400,
24422             terminal: true,
24423             transclude: 'element',
24424             controller: angular.noop,
24425             compile: function(element, attr) {
24426               var srcExp = attr.ngInclude || attr.src,
24427                   onloadExp = attr.onload || '',
24428                   autoScrollExp = attr.autoscroll;
24429
24430               return function(scope, $element, $attr, ctrl, $transclude) {
24431                 var changeCounter = 0,
24432                     currentScope,
24433                     previousElement,
24434                     currentElement;
24435
24436                 var cleanupLastIncludeContent = function() {
24437                   if (previousElement) {
24438                     previousElement.remove();
24439                     previousElement = null;
24440                   }
24441                   if (currentScope) {
24442                     currentScope.$destroy();
24443                     currentScope = null;
24444                   }
24445                   if (currentElement) {
24446                     $animate.leave(currentElement).then(function() {
24447                       previousElement = null;
24448                     });
24449                     previousElement = currentElement;
24450                     currentElement = null;
24451                   }
24452                 };
24453
24454                 scope.$watch(srcExp, function ngIncludeWatchAction(src) {
24455                   var afterAnimation = function() {
24456                     if (isDefined(autoScrollExp) && (!autoScrollExp || scope.$eval(autoScrollExp))) {
24457                       $anchorScroll();
24458                     }
24459                   };
24460                   var thisChangeId = ++changeCounter;
24461
24462                   if (src) {
24463                     //set the 2nd param to true to ignore the template request error so that the inner
24464                     //contents and scope can be cleaned up.
24465                     $templateRequest(src, true).then(function(response) {
24466                       if (thisChangeId !== changeCounter) return;
24467                       var newScope = scope.$new();
24468                       ctrl.template = response;
24469
24470                       // Note: This will also link all children of ng-include that were contained in the original
24471                       // html. If that content contains controllers, ... they could pollute/change the scope.
24472                       // However, using ng-include on an element with additional content does not make sense...
24473                       // Note: We can't remove them in the cloneAttchFn of $transclude as that
24474                       // function is called before linking the content, which would apply child
24475                       // directives to non existing elements.
24476                       var clone = $transclude(newScope, function(clone) {
24477                         cleanupLastIncludeContent();
24478                         $animate.enter(clone, null, $element).then(afterAnimation);
24479                       });
24480
24481                       currentScope = newScope;
24482                       currentElement = clone;
24483
24484                       currentScope.$emit('$includeContentLoaded', src);
24485                       scope.$eval(onloadExp);
24486                     }, function() {
24487                       if (thisChangeId === changeCounter) {
24488                         cleanupLastIncludeContent();
24489                         scope.$emit('$includeContentError', src);
24490                       }
24491                     });
24492                     scope.$emit('$includeContentRequested', src);
24493                   } else {
24494                     cleanupLastIncludeContent();
24495                     ctrl.template = null;
24496                   }
24497                 });
24498               };
24499             }
24500           };
24501         }];
24502
24503         // This directive is called during the $transclude call of the first `ngInclude` directive.
24504         // It will replace and compile the content of the element with the loaded template.
24505         // We need this directive so that the element content is already filled when
24506         // the link function of another directive on the same element as ngInclude
24507         // is called.
24508         var ngIncludeFillContentDirective = ['$compile',
24509           function($compile) {
24510             return {
24511               restrict: 'ECA',
24512               priority: -400,
24513               require: 'ngInclude',
24514               link: function(scope, $element, $attr, ctrl) {
24515                 if (/SVG/.test($element[0].toString())) {
24516                   // WebKit: https://bugs.webkit.org/show_bug.cgi?id=135698 --- SVG elements do not
24517                   // support innerHTML, so detect this here and try to generate the contents
24518                   // specially.
24519                   $element.empty();
24520                   $compile(jqLiteBuildFragment(ctrl.template, document).childNodes)(scope,
24521                       function namespaceAdaptedClone(clone) {
24522                     $element.append(clone);
24523                   }, {futureParentElement: $element});
24524                   return;
24525                 }
24526
24527                 $element.html(ctrl.template);
24528                 $compile($element.contents())(scope);
24529               }
24530             };
24531           }];
24532
24533         /**
24534          * @ngdoc directive
24535          * @name ngInit
24536          * @restrict AC
24537          *
24538          * @description
24539          * The `ngInit` directive allows you to evaluate an expression in the
24540          * current scope.
24541          *
24542          * <div class="alert alert-danger">
24543          * This directive can be abused to add unnecessary amounts of logic into your templates.
24544          * There are only a few appropriate uses of `ngInit`, such as for aliasing special properties of
24545          * {@link ng.directive:ngRepeat `ngRepeat`}, as seen in the demo below; and for injecting data via
24546          * server side scripting. Besides these few cases, you should use {@link guide/controller controllers}
24547          * rather than `ngInit` to initialize values on a scope.
24548          * </div>
24549          *
24550          * <div class="alert alert-warning">
24551          * **Note**: If you have assignment in `ngInit` along with a {@link ng.$filter `filter`}, make
24552          * sure you have parentheses to ensure correct operator precedence:
24553          * <pre class="prettyprint">
24554          * `<div ng-init="test1 = ($index | toString)"></div>`
24555          * </pre>
24556          * </div>
24557          *
24558          * @priority 450
24559          *
24560          * @element ANY
24561          * @param {expression} ngInit {@link guide/expression Expression} to eval.
24562          *
24563          * @example
24564            <example module="initExample">
24565              <file name="index.html">
24566            <script>
24567              angular.module('initExample', [])
24568                .controller('ExampleController', ['$scope', function($scope) {
24569                  $scope.list = [['a', 'b'], ['c', 'd']];
24570                }]);
24571            </script>
24572            <div ng-controller="ExampleController">
24573              <div ng-repeat="innerList in list" ng-init="outerIndex = $index">
24574                <div ng-repeat="value in innerList" ng-init="innerIndex = $index">
24575                   <span class="example-init">list[ {{outerIndex}} ][ {{innerIndex}} ] = {{value}};</span>
24576                </div>
24577              </div>
24578            </div>
24579              </file>
24580              <file name="protractor.js" type="protractor">
24581                it('should alias index positions', function() {
24582                  var elements = element.all(by.css('.example-init'));
24583                  expect(elements.get(0).getText()).toBe('list[ 0 ][ 0 ] = a;');
24584                  expect(elements.get(1).getText()).toBe('list[ 0 ][ 1 ] = b;');
24585                  expect(elements.get(2).getText()).toBe('list[ 1 ][ 0 ] = c;');
24586                  expect(elements.get(3).getText()).toBe('list[ 1 ][ 1 ] = d;');
24587                });
24588              </file>
24589            </example>
24590          */
24591         var ngInitDirective = ngDirective({
24592           priority: 450,
24593           compile: function() {
24594             return {
24595               pre: function(scope, element, attrs) {
24596                 scope.$eval(attrs.ngInit);
24597               }
24598             };
24599           }
24600         });
24601
24602         /**
24603          * @ngdoc directive
24604          * @name ngList
24605          *
24606          * @description
24607          * Text input that converts between a delimited string and an array of strings. The default
24608          * delimiter is a comma followed by a space - equivalent to `ng-list=", "`. You can specify a custom
24609          * delimiter as the value of the `ngList` attribute - for example, `ng-list=" | "`.
24610          *
24611          * The behaviour of the directive is affected by the use of the `ngTrim` attribute.
24612          * * If `ngTrim` is set to `"false"` then whitespace around both the separator and each
24613          *   list item is respected. This implies that the user of the directive is responsible for
24614          *   dealing with whitespace but also allows you to use whitespace as a delimiter, such as a
24615          *   tab or newline character.
24616          * * Otherwise whitespace around the delimiter is ignored when splitting (although it is respected
24617          *   when joining the list items back together) and whitespace around each list item is stripped
24618          *   before it is added to the model.
24619          *
24620          * ### Example with Validation
24621          *
24622          * <example name="ngList-directive" module="listExample">
24623          *   <file name="app.js">
24624          *      angular.module('listExample', [])
24625          *        .controller('ExampleController', ['$scope', function($scope) {
24626          *          $scope.names = ['morpheus', 'neo', 'trinity'];
24627          *        }]);
24628          *   </file>
24629          *   <file name="index.html">
24630          *    <form name="myForm" ng-controller="ExampleController">
24631          *      <label>List: <input name="namesInput" ng-model="names" ng-list required></label>
24632          *      <span role="alert">
24633          *        <span class="error" ng-show="myForm.namesInput.$error.required">
24634          *        Required!</span>
24635          *      </span>
24636          *      <br>
24637          *      <tt>names = {{names}}</tt><br/>
24638          *      <tt>myForm.namesInput.$valid = {{myForm.namesInput.$valid}}</tt><br/>
24639          *      <tt>myForm.namesInput.$error = {{myForm.namesInput.$error}}</tt><br/>
24640          *      <tt>myForm.$valid = {{myForm.$valid}}</tt><br/>
24641          *      <tt>myForm.$error.required = {{!!myForm.$error.required}}</tt><br/>
24642          *     </form>
24643          *   </file>
24644          *   <file name="protractor.js" type="protractor">
24645          *     var listInput = element(by.model('names'));
24646          *     var names = element(by.exactBinding('names'));
24647          *     var valid = element(by.binding('myForm.namesInput.$valid'));
24648          *     var error = element(by.css('span.error'));
24649          *
24650          *     it('should initialize to model', function() {
24651          *       expect(names.getText()).toContain('["morpheus","neo","trinity"]');
24652          *       expect(valid.getText()).toContain('true');
24653          *       expect(error.getCssValue('display')).toBe('none');
24654          *     });
24655          *
24656          *     it('should be invalid if empty', function() {
24657          *       listInput.clear();
24658          *       listInput.sendKeys('');
24659          *
24660          *       expect(names.getText()).toContain('');
24661          *       expect(valid.getText()).toContain('false');
24662          *       expect(error.getCssValue('display')).not.toBe('none');
24663          *     });
24664          *   </file>
24665          * </example>
24666          *
24667          * ### Example - splitting on newline
24668          * <example name="ngList-directive-newlines">
24669          *   <file name="index.html">
24670          *    <textarea ng-model="list" ng-list="&#10;" ng-trim="false"></textarea>
24671          *    <pre>{{ list | json }}</pre>
24672          *   </file>
24673          *   <file name="protractor.js" type="protractor">
24674          *     it("should split the text by newlines", function() {
24675          *       var listInput = element(by.model('list'));
24676          *       var output = element(by.binding('list | json'));
24677          *       listInput.sendKeys('abc\ndef\nghi');
24678          *       expect(output.getText()).toContain('[\n  "abc",\n  "def",\n  "ghi"\n]');
24679          *     });
24680          *   </file>
24681          * </example>
24682          *
24683          * @element input
24684          * @param {string=} ngList optional delimiter that should be used to split the value.
24685          */
24686         var ngListDirective = function() {
24687           return {
24688             restrict: 'A',
24689             priority: 100,
24690             require: 'ngModel',
24691             link: function(scope, element, attr, ctrl) {
24692               // We want to control whitespace trimming so we use this convoluted approach
24693               // to access the ngList attribute, which doesn't pre-trim the attribute
24694               var ngList = element.attr(attr.$attr.ngList) || ', ';
24695               var trimValues = attr.ngTrim !== 'false';
24696               var separator = trimValues ? trim(ngList) : ngList;
24697
24698               var parse = function(viewValue) {
24699                 // If the viewValue is invalid (say required but empty) it will be `undefined`
24700                 if (isUndefined(viewValue)) return;
24701
24702                 var list = [];
24703
24704                 if (viewValue) {
24705                   forEach(viewValue.split(separator), function(value) {
24706                     if (value) list.push(trimValues ? trim(value) : value);
24707                   });
24708                 }
24709
24710                 return list;
24711               };
24712
24713               ctrl.$parsers.push(parse);
24714               ctrl.$formatters.push(function(value) {
24715                 if (isArray(value)) {
24716                   return value.join(ngList);
24717                 }
24718
24719                 return undefined;
24720               });
24721
24722               // Override the standard $isEmpty because an empty array means the input is empty.
24723               ctrl.$isEmpty = function(value) {
24724                 return !value || !value.length;
24725               };
24726             }
24727           };
24728         };
24729
24730         /* global VALID_CLASS: true,
24731           INVALID_CLASS: true,
24732           PRISTINE_CLASS: true,
24733           DIRTY_CLASS: true,
24734           UNTOUCHED_CLASS: true,
24735           TOUCHED_CLASS: true,
24736         */
24737
24738         var VALID_CLASS = 'ng-valid',
24739             INVALID_CLASS = 'ng-invalid',
24740             PRISTINE_CLASS = 'ng-pristine',
24741             DIRTY_CLASS = 'ng-dirty',
24742             UNTOUCHED_CLASS = 'ng-untouched',
24743             TOUCHED_CLASS = 'ng-touched',
24744             PENDING_CLASS = 'ng-pending';
24745
24746         var ngModelMinErr = minErr('ngModel');
24747
24748         /**
24749          * @ngdoc type
24750          * @name ngModel.NgModelController
24751          *
24752          * @property {*} $viewValue The actual value from the control's view. For `input` elements, this is a
24753          * String. See {@link ngModel.NgModelController#$setViewValue} for information about when the $viewValue
24754          * is set.
24755          * @property {*} $modelValue The value in the model that the control is bound to.
24756          * @property {Array.<Function>} $parsers Array of functions to execute, as a pipeline, whenever
24757                the control reads value from the DOM. The functions are called in array order, each passing
24758                its return value through to the next. The last return value is forwarded to the
24759                {@link ngModel.NgModelController#$validators `$validators`} collection.
24760
24761         Parsers are used to sanitize / convert the {@link ngModel.NgModelController#$viewValue
24762         `$viewValue`}.
24763
24764         Returning `undefined` from a parser means a parse error occurred. In that case,
24765         no {@link ngModel.NgModelController#$validators `$validators`} will run and the `ngModel`
24766         will be set to `undefined` unless {@link ngModelOptions `ngModelOptions.allowInvalid`}
24767         is set to `true`. The parse error is stored in `ngModel.$error.parse`.
24768
24769          *
24770          * @property {Array.<Function>} $formatters Array of functions to execute, as a pipeline, whenever
24771                the model value changes. The functions are called in reverse array order, each passing the value through to the
24772                next. The last return value is used as the actual DOM value.
24773                Used to format / convert values for display in the control.
24774          * ```js
24775          * function formatter(value) {
24776          *   if (value) {
24777          *     return value.toUpperCase();
24778          *   }
24779          * }
24780          * ngModel.$formatters.push(formatter);
24781          * ```
24782          *
24783          * @property {Object.<string, function>} $validators A collection of validators that are applied
24784          *      whenever the model value changes. The key value within the object refers to the name of the
24785          *      validator while the function refers to the validation operation. The validation operation is
24786          *      provided with the model value as an argument and must return a true or false value depending
24787          *      on the response of that validation.
24788          *
24789          * ```js
24790          * ngModel.$validators.validCharacters = function(modelValue, viewValue) {
24791          *   var value = modelValue || viewValue;
24792          *   return /[0-9]+/.test(value) &&
24793          *          /[a-z]+/.test(value) &&
24794          *          /[A-Z]+/.test(value) &&
24795          *          /\W+/.test(value);
24796          * };
24797          * ```
24798          *
24799          * @property {Object.<string, function>} $asyncValidators A collection of validations that are expected to
24800          *      perform an asynchronous validation (e.g. a HTTP request). The validation function that is provided
24801          *      is expected to return a promise when it is run during the model validation process. Once the promise
24802          *      is delivered then the validation status will be set to true when fulfilled and false when rejected.
24803          *      When the asynchronous validators are triggered, each of the validators will run in parallel and the model
24804          *      value will only be updated once all validators have been fulfilled. As long as an asynchronous validator
24805          *      is unfulfilled, its key will be added to the controllers `$pending` property. Also, all asynchronous validators
24806          *      will only run once all synchronous validators have passed.
24807          *
24808          * Please note that if $http is used then it is important that the server returns a success HTTP response code
24809          * in order to fulfill the validation and a status level of `4xx` in order to reject the validation.
24810          *
24811          * ```js
24812          * ngModel.$asyncValidators.uniqueUsername = function(modelValue, viewValue) {
24813          *   var value = modelValue || viewValue;
24814          *
24815          *   // Lookup user by username
24816          *   return $http.get('/api/users/' + value).
24817          *      then(function resolved() {
24818          *        //username exists, this means validation fails
24819          *        return $q.reject('exists');
24820          *      }, function rejected() {
24821          *        //username does not exist, therefore this validation passes
24822          *        return true;
24823          *      });
24824          * };
24825          * ```
24826          *
24827          * @property {Array.<Function>} $viewChangeListeners Array of functions to execute whenever the
24828          *     view value has changed. It is called with no arguments, and its return value is ignored.
24829          *     This can be used in place of additional $watches against the model value.
24830          *
24831          * @property {Object} $error An object hash with all failing validator ids as keys.
24832          * @property {Object} $pending An object hash with all pending validator ids as keys.
24833          *
24834          * @property {boolean} $untouched True if control has not lost focus yet.
24835          * @property {boolean} $touched True if control has lost focus.
24836          * @property {boolean} $pristine True if user has not interacted with the control yet.
24837          * @property {boolean} $dirty True if user has already interacted with the control.
24838          * @property {boolean} $valid True if there is no error.
24839          * @property {boolean} $invalid True if at least one error on the control.
24840          * @property {string} $name The name attribute of the control.
24841          *
24842          * @description
24843          *
24844          * `NgModelController` provides API for the {@link ngModel `ngModel`} directive.
24845          * The controller contains services for data-binding, validation, CSS updates, and value formatting
24846          * and parsing. It purposefully does not contain any logic which deals with DOM rendering or
24847          * listening to DOM events.
24848          * Such DOM related logic should be provided by other directives which make use of
24849          * `NgModelController` for data-binding to control elements.
24850          * Angular provides this DOM logic for most {@link input `input`} elements.
24851          * At the end of this page you can find a {@link ngModel.NgModelController#custom-control-example
24852          * custom control example} that uses `ngModelController` to bind to `contenteditable` elements.
24853          *
24854          * @example
24855          * ### Custom Control Example
24856          * This example shows how to use `NgModelController` with a custom control to achieve
24857          * data-binding. Notice how different directives (`contenteditable`, `ng-model`, and `required`)
24858          * collaborate together to achieve the desired result.
24859          *
24860          * `contenteditable` is an HTML5 attribute, which tells the browser to let the element
24861          * contents be edited in place by the user.
24862          *
24863          * We are using the {@link ng.service:$sce $sce} service here and include the {@link ngSanitize $sanitize}
24864          * module to automatically remove "bad" content like inline event listener (e.g. `<span onclick="...">`).
24865          * However, as we are using `$sce` the model can still decide to provide unsafe content if it marks
24866          * that content using the `$sce` service.
24867          *
24868          * <example name="NgModelController" module="customControl" deps="angular-sanitize.js">
24869             <file name="style.css">
24870               [contenteditable] {
24871                 border: 1px solid black;
24872                 background-color: white;
24873                 min-height: 20px;
24874               }
24875
24876               .ng-invalid {
24877                 border: 1px solid red;
24878               }
24879
24880             </file>
24881             <file name="script.js">
24882               angular.module('customControl', ['ngSanitize']).
24883                 directive('contenteditable', ['$sce', function($sce) {
24884                   return {
24885                     restrict: 'A', // only activate on element attribute
24886                     require: '?ngModel', // get a hold of NgModelController
24887                     link: function(scope, element, attrs, ngModel) {
24888                       if (!ngModel) return; // do nothing if no ng-model
24889
24890                       // Specify how UI should be updated
24891                       ngModel.$render = function() {
24892                         element.html($sce.getTrustedHtml(ngModel.$viewValue || ''));
24893                       };
24894
24895                       // Listen for change events to enable binding
24896                       element.on('blur keyup change', function() {
24897                         scope.$evalAsync(read);
24898                       });
24899                       read(); // initialize
24900
24901                       // Write data to the model
24902                       function read() {
24903                         var html = element.html();
24904                         // When we clear the content editable the browser leaves a <br> behind
24905                         // If strip-br attribute is provided then we strip this out
24906                         if ( attrs.stripBr && html == '<br>' ) {
24907                           html = '';
24908                         }
24909                         ngModel.$setViewValue(html);
24910                       }
24911                     }
24912                   };
24913                 }]);
24914             </file>
24915             <file name="index.html">
24916               <form name="myForm">
24917                <div contenteditable
24918                     name="myWidget" ng-model="userContent"
24919                     strip-br="true"
24920                     required>Change me!</div>
24921                 <span ng-show="myForm.myWidget.$error.required">Required!</span>
24922                <hr>
24923                <textarea ng-model="userContent" aria-label="Dynamic textarea"></textarea>
24924               </form>
24925             </file>
24926             <file name="protractor.js" type="protractor">
24927             it('should data-bind and become invalid', function() {
24928               if (browser.params.browser == 'safari' || browser.params.browser == 'firefox') {
24929                 // SafariDriver can't handle contenteditable
24930                 // and Firefox driver can't clear contenteditables very well
24931                 return;
24932               }
24933               var contentEditable = element(by.css('[contenteditable]'));
24934               var content = 'Change me!';
24935
24936               expect(contentEditable.getText()).toEqual(content);
24937
24938               contentEditable.clear();
24939               contentEditable.sendKeys(protractor.Key.BACK_SPACE);
24940               expect(contentEditable.getText()).toEqual('');
24941               expect(contentEditable.getAttribute('class')).toMatch(/ng-invalid-required/);
24942             });
24943             </file>
24944          * </example>
24945          *
24946          *
24947          */
24948         var NgModelController = ['$scope', '$exceptionHandler', '$attrs', '$element', '$parse', '$animate', '$timeout', '$rootScope', '$q', '$interpolate',
24949             function($scope, $exceptionHandler, $attr, $element, $parse, $animate, $timeout, $rootScope, $q, $interpolate) {
24950           this.$viewValue = Number.NaN;
24951           this.$modelValue = Number.NaN;
24952           this.$$rawModelValue = undefined; // stores the parsed modelValue / model set from scope regardless of validity.
24953           this.$validators = {};
24954           this.$asyncValidators = {};
24955           this.$parsers = [];
24956           this.$formatters = [];
24957           this.$viewChangeListeners = [];
24958           this.$untouched = true;
24959           this.$touched = false;
24960           this.$pristine = true;
24961           this.$dirty = false;
24962           this.$valid = true;
24963           this.$invalid = false;
24964           this.$error = {}; // keep invalid keys here
24965           this.$$success = {}; // keep valid keys here
24966           this.$pending = undefined; // keep pending keys here
24967           this.$name = $interpolate($attr.name || '', false)($scope);
24968           this.$$parentForm = nullFormCtrl;
24969
24970           var parsedNgModel = $parse($attr.ngModel),
24971               parsedNgModelAssign = parsedNgModel.assign,
24972               ngModelGet = parsedNgModel,
24973               ngModelSet = parsedNgModelAssign,
24974               pendingDebounce = null,
24975               parserValid,
24976               ctrl = this;
24977
24978           this.$$setOptions = function(options) {
24979             ctrl.$options = options;
24980             if (options && options.getterSetter) {
24981               var invokeModelGetter = $parse($attr.ngModel + '()'),
24982                   invokeModelSetter = $parse($attr.ngModel + '($$$p)');
24983
24984               ngModelGet = function($scope) {
24985                 var modelValue = parsedNgModel($scope);
24986                 if (isFunction(modelValue)) {
24987                   modelValue = invokeModelGetter($scope);
24988                 }
24989                 return modelValue;
24990               };
24991               ngModelSet = function($scope, newValue) {
24992                 if (isFunction(parsedNgModel($scope))) {
24993                   invokeModelSetter($scope, {$$$p: ctrl.$modelValue});
24994                 } else {
24995                   parsedNgModelAssign($scope, ctrl.$modelValue);
24996                 }
24997               };
24998             } else if (!parsedNgModel.assign) {
24999               throw ngModelMinErr('nonassign', "Expression '{0}' is non-assignable. Element: {1}",
25000                   $attr.ngModel, startingTag($element));
25001             }
25002           };
25003
25004           /**
25005            * @ngdoc method
25006            * @name ngModel.NgModelController#$render
25007            *
25008            * @description
25009            * Called when the view needs to be updated. It is expected that the user of the ng-model
25010            * directive will implement this method.
25011            *
25012            * The `$render()` method is invoked in the following situations:
25013            *
25014            * * `$rollbackViewValue()` is called.  If we are rolling back the view value to the last
25015            *   committed value then `$render()` is called to update the input control.
25016            * * The value referenced by `ng-model` is changed programmatically and both the `$modelValue` and
25017            *   the `$viewValue` are different from last time.
25018            *
25019            * Since `ng-model` does not do a deep watch, `$render()` is only invoked if the values of
25020            * `$modelValue` and `$viewValue` are actually different from their previous value. If `$modelValue`
25021            * or `$viewValue` are objects (rather than a string or number) then `$render()` will not be
25022            * invoked if you only change a property on the objects.
25023            */
25024           this.$render = noop;
25025
25026           /**
25027            * @ngdoc method
25028            * @name ngModel.NgModelController#$isEmpty
25029            *
25030            * @description
25031            * This is called when we need to determine if the value of an input is empty.
25032            *
25033            * For instance, the required directive does this to work out if the input has data or not.
25034            *
25035            * The default `$isEmpty` function checks whether the value is `undefined`, `''`, `null` or `NaN`.
25036            *
25037            * You can override this for input directives whose concept of being empty is different from the
25038            * default. The `checkboxInputType` directive does this because in its case a value of `false`
25039            * implies empty.
25040            *
25041            * @param {*} value The value of the input to check for emptiness.
25042            * @returns {boolean} True if `value` is "empty".
25043            */
25044           this.$isEmpty = function(value) {
25045             return isUndefined(value) || value === '' || value === null || value !== value;
25046           };
25047
25048           var currentValidationRunId = 0;
25049
25050           /**
25051            * @ngdoc method
25052            * @name ngModel.NgModelController#$setValidity
25053            *
25054            * @description
25055            * Change the validity state, and notify the form.
25056            *
25057            * This method can be called within $parsers/$formatters or a custom validation implementation.
25058            * However, in most cases it should be sufficient to use the `ngModel.$validators` and
25059            * `ngModel.$asyncValidators` collections which will call `$setValidity` automatically.
25060            *
25061            * @param {string} validationErrorKey Name of the validator. The `validationErrorKey` will be assigned
25062            *        to either `$error[validationErrorKey]` or `$pending[validationErrorKey]`
25063            *        (for unfulfilled `$asyncValidators`), so that it is available for data-binding.
25064            *        The `validationErrorKey` should be in camelCase and will get converted into dash-case
25065            *        for class name. Example: `myError` will result in `ng-valid-my-error` and `ng-invalid-my-error`
25066            *        class and can be bound to as  `{{someForm.someControl.$error.myError}}` .
25067            * @param {boolean} isValid Whether the current state is valid (true), invalid (false), pending (undefined),
25068            *                          or skipped (null). Pending is used for unfulfilled `$asyncValidators`.
25069            *                          Skipped is used by Angular when validators do not run because of parse errors and
25070            *                          when `$asyncValidators` do not run because any of the `$validators` failed.
25071            */
25072           addSetValidityMethod({
25073             ctrl: this,
25074             $element: $element,
25075             set: function(object, property) {
25076               object[property] = true;
25077             },
25078             unset: function(object, property) {
25079               delete object[property];
25080             },
25081             $animate: $animate
25082           });
25083
25084           /**
25085            * @ngdoc method
25086            * @name ngModel.NgModelController#$setPristine
25087            *
25088            * @description
25089            * Sets the control to its pristine state.
25090            *
25091            * This method can be called to remove the `ng-dirty` class and set the control to its pristine
25092            * state (`ng-pristine` class). A model is considered to be pristine when the control
25093            * has not been changed from when first compiled.
25094            */
25095           this.$setPristine = function() {
25096             ctrl.$dirty = false;
25097             ctrl.$pristine = true;
25098             $animate.removeClass($element, DIRTY_CLASS);
25099             $animate.addClass($element, PRISTINE_CLASS);
25100           };
25101
25102           /**
25103            * @ngdoc method
25104            * @name ngModel.NgModelController#$setDirty
25105            *
25106            * @description
25107            * Sets the control to its dirty state.
25108            *
25109            * This method can be called to remove the `ng-pristine` class and set the control to its dirty
25110            * state (`ng-dirty` class). A model is considered to be dirty when the control has been changed
25111            * from when first compiled.
25112            */
25113           this.$setDirty = function() {
25114             ctrl.$dirty = true;
25115             ctrl.$pristine = false;
25116             $animate.removeClass($element, PRISTINE_CLASS);
25117             $animate.addClass($element, DIRTY_CLASS);
25118             ctrl.$$parentForm.$setDirty();
25119           };
25120
25121           /**
25122            * @ngdoc method
25123            * @name ngModel.NgModelController#$setUntouched
25124            *
25125            * @description
25126            * Sets the control to its untouched state.
25127            *
25128            * This method can be called to remove the `ng-touched` class and set the control to its
25129            * untouched state (`ng-untouched` class). Upon compilation, a model is set as untouched
25130            * by default, however this function can be used to restore that state if the model has
25131            * already been touched by the user.
25132            */
25133           this.$setUntouched = function() {
25134             ctrl.$touched = false;
25135             ctrl.$untouched = true;
25136             $animate.setClass($element, UNTOUCHED_CLASS, TOUCHED_CLASS);
25137           };
25138
25139           /**
25140            * @ngdoc method
25141            * @name ngModel.NgModelController#$setTouched
25142            *
25143            * @description
25144            * Sets the control to its touched state.
25145            *
25146            * This method can be called to remove the `ng-untouched` class and set the control to its
25147            * touched state (`ng-touched` class). A model is considered to be touched when the user has
25148            * first focused the control element and then shifted focus away from the control (blur event).
25149            */
25150           this.$setTouched = function() {
25151             ctrl.$touched = true;
25152             ctrl.$untouched = false;
25153             $animate.setClass($element, TOUCHED_CLASS, UNTOUCHED_CLASS);
25154           };
25155
25156           /**
25157            * @ngdoc method
25158            * @name ngModel.NgModelController#$rollbackViewValue
25159            *
25160            * @description
25161            * Cancel an update and reset the input element's value to prevent an update to the `$modelValue`,
25162            * which may be caused by a pending debounced event or because the input is waiting for a some
25163            * future event.
25164            *
25165            * If you have an input that uses `ng-model-options` to set up debounced events or events such
25166            * as blur you can have a situation where there is a period when the `$viewValue`
25167            * is out of synch with the ngModel's `$modelValue`.
25168            *
25169            * In this case, you can run into difficulties if you try to update the ngModel's `$modelValue`
25170            * programmatically before these debounced/future events have resolved/occurred, because Angular's
25171            * dirty checking mechanism is not able to tell whether the model has actually changed or not.
25172            *
25173            * The `$rollbackViewValue()` method should be called before programmatically changing the model of an
25174            * input which may have such events pending. This is important in order to make sure that the
25175            * input field will be updated with the new model value and any pending operations are cancelled.
25176            *
25177            * <example name="ng-model-cancel-update" module="cancel-update-example">
25178            *   <file name="app.js">
25179            *     angular.module('cancel-update-example', [])
25180            *
25181            *     .controller('CancelUpdateController', ['$scope', function($scope) {
25182            *       $scope.resetWithCancel = function(e) {
25183            *         if (e.keyCode == 27) {
25184            *           $scope.myForm.myInput1.$rollbackViewValue();
25185            *           $scope.myValue = '';
25186            *         }
25187            *       };
25188            *       $scope.resetWithoutCancel = function(e) {
25189            *         if (e.keyCode == 27) {
25190            *           $scope.myValue = '';
25191            *         }
25192            *       };
25193            *     }]);
25194            *   </file>
25195            *   <file name="index.html">
25196            *     <div ng-controller="CancelUpdateController">
25197            *       <p>Try typing something in each input.  See that the model only updates when you
25198            *          blur off the input.
25199            *        </p>
25200            *        <p>Now see what happens if you start typing then press the Escape key</p>
25201            *
25202            *       <form name="myForm" ng-model-options="{ updateOn: 'blur' }">
25203            *         <p id="inputDescription1">With $rollbackViewValue()</p>
25204            *         <input name="myInput1" aria-describedby="inputDescription1" ng-model="myValue"
25205            *                ng-keydown="resetWithCancel($event)"><br/>
25206            *         myValue: "{{ myValue }}"
25207            *
25208            *         <p id="inputDescription2">Without $rollbackViewValue()</p>
25209            *         <input name="myInput2" aria-describedby="inputDescription2" ng-model="myValue"
25210            *                ng-keydown="resetWithoutCancel($event)"><br/>
25211            *         myValue: "{{ myValue }}"
25212            *       </form>
25213            *     </div>
25214            *   </file>
25215            * </example>
25216            */
25217           this.$rollbackViewValue = function() {
25218             $timeout.cancel(pendingDebounce);
25219             ctrl.$viewValue = ctrl.$$lastCommittedViewValue;
25220             ctrl.$render();
25221           };
25222
25223           /**
25224            * @ngdoc method
25225            * @name ngModel.NgModelController#$validate
25226            *
25227            * @description
25228            * Runs each of the registered validators (first synchronous validators and then
25229            * asynchronous validators).
25230            * If the validity changes to invalid, the model will be set to `undefined`,
25231            * unless {@link ngModelOptions `ngModelOptions.allowInvalid`} is `true`.
25232            * If the validity changes to valid, it will set the model to the last available valid
25233            * `$modelValue`, i.e. either the last parsed value or the last value set from the scope.
25234            */
25235           this.$validate = function() {
25236             // ignore $validate before model is initialized
25237             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25238               return;
25239             }
25240
25241             var viewValue = ctrl.$$lastCommittedViewValue;
25242             // Note: we use the $$rawModelValue as $modelValue might have been
25243             // set to undefined during a view -> model update that found validation
25244             // errors. We can't parse the view here, since that could change
25245             // the model although neither viewValue nor the model on the scope changed
25246             var modelValue = ctrl.$$rawModelValue;
25247
25248             var prevValid = ctrl.$valid;
25249             var prevModelValue = ctrl.$modelValue;
25250
25251             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25252
25253             ctrl.$$runValidators(modelValue, viewValue, function(allValid) {
25254               // If there was no change in validity, don't update the model
25255               // This prevents changing an invalid modelValue to undefined
25256               if (!allowInvalid && prevValid !== allValid) {
25257                 // Note: Don't check ctrl.$valid here, as we could have
25258                 // external validators (e.g. calculated on the server),
25259                 // that just call $setValidity and need the model value
25260                 // to calculate their validity.
25261                 ctrl.$modelValue = allValid ? modelValue : undefined;
25262
25263                 if (ctrl.$modelValue !== prevModelValue) {
25264                   ctrl.$$writeModelToScope();
25265                 }
25266               }
25267             });
25268
25269           };
25270
25271           this.$$runValidators = function(modelValue, viewValue, doneCallback) {
25272             currentValidationRunId++;
25273             var localValidationRunId = currentValidationRunId;
25274
25275             // check parser error
25276             if (!processParseErrors()) {
25277               validationDone(false);
25278               return;
25279             }
25280             if (!processSyncValidators()) {
25281               validationDone(false);
25282               return;
25283             }
25284             processAsyncValidators();
25285
25286             function processParseErrors() {
25287               var errorKey = ctrl.$$parserName || 'parse';
25288               if (isUndefined(parserValid)) {
25289                 setValidity(errorKey, null);
25290               } else {
25291                 if (!parserValid) {
25292                   forEach(ctrl.$validators, function(v, name) {
25293                     setValidity(name, null);
25294                   });
25295                   forEach(ctrl.$asyncValidators, function(v, name) {
25296                     setValidity(name, null);
25297                   });
25298                 }
25299                 // Set the parse error last, to prevent unsetting it, should a $validators key == parserName
25300                 setValidity(errorKey, parserValid);
25301                 return parserValid;
25302               }
25303               return true;
25304             }
25305
25306             function processSyncValidators() {
25307               var syncValidatorsValid = true;
25308               forEach(ctrl.$validators, function(validator, name) {
25309                 var result = validator(modelValue, viewValue);
25310                 syncValidatorsValid = syncValidatorsValid && result;
25311                 setValidity(name, result);
25312               });
25313               if (!syncValidatorsValid) {
25314                 forEach(ctrl.$asyncValidators, function(v, name) {
25315                   setValidity(name, null);
25316                 });
25317                 return false;
25318               }
25319               return true;
25320             }
25321
25322             function processAsyncValidators() {
25323               var validatorPromises = [];
25324               var allValid = true;
25325               forEach(ctrl.$asyncValidators, function(validator, name) {
25326                 var promise = validator(modelValue, viewValue);
25327                 if (!isPromiseLike(promise)) {
25328                   throw ngModelMinErr("$asyncValidators",
25329                     "Expected asynchronous validator to return a promise but got '{0}' instead.", promise);
25330                 }
25331                 setValidity(name, undefined);
25332                 validatorPromises.push(promise.then(function() {
25333                   setValidity(name, true);
25334                 }, function(error) {
25335                   allValid = false;
25336                   setValidity(name, false);
25337                 }));
25338               });
25339               if (!validatorPromises.length) {
25340                 validationDone(true);
25341               } else {
25342                 $q.all(validatorPromises).then(function() {
25343                   validationDone(allValid);
25344                 }, noop);
25345               }
25346             }
25347
25348             function setValidity(name, isValid) {
25349               if (localValidationRunId === currentValidationRunId) {
25350                 ctrl.$setValidity(name, isValid);
25351               }
25352             }
25353
25354             function validationDone(allValid) {
25355               if (localValidationRunId === currentValidationRunId) {
25356
25357                 doneCallback(allValid);
25358               }
25359             }
25360           };
25361
25362           /**
25363            * @ngdoc method
25364            * @name ngModel.NgModelController#$commitViewValue
25365            *
25366            * @description
25367            * Commit a pending update to the `$modelValue`.
25368            *
25369            * Updates may be pending by a debounced event or because the input is waiting for a some future
25370            * event defined in `ng-model-options`. this method is rarely needed as `NgModelController`
25371            * usually handles calling this in response to input events.
25372            */
25373           this.$commitViewValue = function() {
25374             var viewValue = ctrl.$viewValue;
25375
25376             $timeout.cancel(pendingDebounce);
25377
25378             // If the view value has not changed then we should just exit, except in the case where there is
25379             // a native validator on the element. In this case the validation state may have changed even though
25380             // the viewValue has stayed empty.
25381             if (ctrl.$$lastCommittedViewValue === viewValue && (viewValue !== '' || !ctrl.$$hasNativeValidators)) {
25382               return;
25383             }
25384             ctrl.$$lastCommittedViewValue = viewValue;
25385
25386             // change to dirty
25387             if (ctrl.$pristine) {
25388               this.$setDirty();
25389             }
25390             this.$$parseAndValidate();
25391           };
25392
25393           this.$$parseAndValidate = function() {
25394             var viewValue = ctrl.$$lastCommittedViewValue;
25395             var modelValue = viewValue;
25396             parserValid = isUndefined(modelValue) ? undefined : true;
25397
25398             if (parserValid) {
25399               for (var i = 0; i < ctrl.$parsers.length; i++) {
25400                 modelValue = ctrl.$parsers[i](modelValue);
25401                 if (isUndefined(modelValue)) {
25402                   parserValid = false;
25403                   break;
25404                 }
25405               }
25406             }
25407             if (isNumber(ctrl.$modelValue) && isNaN(ctrl.$modelValue)) {
25408               // ctrl.$modelValue has not been touched yet...
25409               ctrl.$modelValue = ngModelGet($scope);
25410             }
25411             var prevModelValue = ctrl.$modelValue;
25412             var allowInvalid = ctrl.$options && ctrl.$options.allowInvalid;
25413             ctrl.$$rawModelValue = modelValue;
25414
25415             if (allowInvalid) {
25416               ctrl.$modelValue = modelValue;
25417               writeToModelIfNeeded();
25418             }
25419
25420             // Pass the $$lastCommittedViewValue here, because the cached viewValue might be out of date.
25421             // This can happen if e.g. $setViewValue is called from inside a parser
25422             ctrl.$$runValidators(modelValue, ctrl.$$lastCommittedViewValue, function(allValid) {
25423               if (!allowInvalid) {
25424                 // Note: Don't check ctrl.$valid here, as we could have
25425                 // external validators (e.g. calculated on the server),
25426                 // that just call $setValidity and need the model value
25427                 // to calculate their validity.
25428                 ctrl.$modelValue = allValid ? modelValue : undefined;
25429                 writeToModelIfNeeded();
25430               }
25431             });
25432
25433             function writeToModelIfNeeded() {
25434               if (ctrl.$modelValue !== prevModelValue) {
25435                 ctrl.$$writeModelToScope();
25436               }
25437             }
25438           };
25439
25440           this.$$writeModelToScope = function() {
25441             ngModelSet($scope, ctrl.$modelValue);
25442             forEach(ctrl.$viewChangeListeners, function(listener) {
25443               try {
25444                 listener();
25445               } catch (e) {
25446                 $exceptionHandler(e);
25447               }
25448             });
25449           };
25450
25451           /**
25452            * @ngdoc method
25453            * @name ngModel.NgModelController#$setViewValue
25454            *
25455            * @description
25456            * Update the view value.
25457            *
25458            * This method should be called when a control wants to change the view value; typically,
25459            * this is done from within a DOM event handler. For example, the {@link ng.directive:input input}
25460            * directive calls it when the value of the input changes and {@link ng.directive:select select}
25461            * calls it when an option is selected.
25462            *
25463            * When `$setViewValue` is called, the new `value` will be staged for committing through the `$parsers`
25464            * and `$validators` pipelines. If there are no special {@link ngModelOptions} specified then the staged
25465            * value sent directly for processing, finally to be applied to `$modelValue` and then the
25466            * **expression** specified in the `ng-model` attribute. Lastly, all the registered change listeners,
25467            * in the `$viewChangeListeners` list, are called.
25468            *
25469            * In case the {@link ng.directive:ngModelOptions ngModelOptions} directive is used with `updateOn`
25470            * and the `default` trigger is not listed, all those actions will remain pending until one of the
25471            * `updateOn` events is triggered on the DOM element.
25472            * All these actions will be debounced if the {@link ng.directive:ngModelOptions ngModelOptions}
25473            * directive is used with a custom debounce for this particular event.
25474            * Note that a `$digest` is only triggered once the `updateOn` events are fired, or if `debounce`
25475            * is specified, once the timer runs out.
25476            *
25477            * When used with standard inputs, the view value will always be a string (which is in some cases
25478            * parsed into another type, such as a `Date` object for `input[date]`.)
25479            * However, custom controls might also pass objects to this method. In this case, we should make
25480            * a copy of the object before passing it to `$setViewValue`. This is because `ngModel` does not
25481            * perform a deep watch of objects, it only looks for a change of identity. If you only change
25482            * the property of the object then ngModel will not realise that the object has changed and
25483            * will not invoke the `$parsers` and `$validators` pipelines. For this reason, you should
25484            * not change properties of the copy once it has been passed to `$setViewValue`.
25485            * Otherwise you may cause the model value on the scope to change incorrectly.
25486            *
25487            * <div class="alert alert-info">
25488            * In any case, the value passed to the method should always reflect the current value
25489            * of the control. For example, if you are calling `$setViewValue` for an input element,
25490            * you should pass the input DOM value. Otherwise, the control and the scope model become
25491            * out of sync. It's also important to note that `$setViewValue` does not call `$render` or change
25492            * the control's DOM value in any way. If we want to change the control's DOM value
25493            * programmatically, we should update the `ngModel` scope expression. Its new value will be
25494            * picked up by the model controller, which will run it through the `$formatters`, `$render` it
25495            * to update the DOM, and finally call `$validate` on it.
25496            * </div>
25497            *
25498            * @param {*} value value from the view.
25499            * @param {string} trigger Event that triggered the update.
25500            */
25501           this.$setViewValue = function(value, trigger) {
25502             ctrl.$viewValue = value;
25503             if (!ctrl.$options || ctrl.$options.updateOnDefault) {
25504               ctrl.$$debounceViewValueCommit(trigger);
25505             }
25506           };
25507
25508           this.$$debounceViewValueCommit = function(trigger) {
25509             var debounceDelay = 0,
25510                 options = ctrl.$options,
25511                 debounce;
25512
25513             if (options && isDefined(options.debounce)) {
25514               debounce = options.debounce;
25515               if (isNumber(debounce)) {
25516                 debounceDelay = debounce;
25517               } else if (isNumber(debounce[trigger])) {
25518                 debounceDelay = debounce[trigger];
25519               } else if (isNumber(debounce['default'])) {
25520                 debounceDelay = debounce['default'];
25521               }
25522             }
25523
25524             $timeout.cancel(pendingDebounce);
25525             if (debounceDelay) {
25526               pendingDebounce = $timeout(function() {
25527                 ctrl.$commitViewValue();
25528               }, debounceDelay);
25529             } else if ($rootScope.$$phase) {
25530               ctrl.$commitViewValue();
25531             } else {
25532               $scope.$apply(function() {
25533                 ctrl.$commitViewValue();
25534               });
25535             }
25536           };
25537
25538           // model -> value
25539           // Note: we cannot use a normal scope.$watch as we want to detect the following:
25540           // 1. scope value is 'a'
25541           // 2. user enters 'b'
25542           // 3. ng-change kicks in and reverts scope value to 'a'
25543           //    -> scope value did not change since the last digest as
25544           //       ng-change executes in apply phase
25545           // 4. view should be changed back to 'a'
25546           $scope.$watch(function ngModelWatch() {
25547             var modelValue = ngModelGet($scope);
25548
25549             // if scope model value and ngModel value are out of sync
25550             // TODO(perf): why not move this to the action fn?
25551             if (modelValue !== ctrl.$modelValue &&
25552                // checks for NaN is needed to allow setting the model to NaN when there's an asyncValidator
25553                (ctrl.$modelValue === ctrl.$modelValue || modelValue === modelValue)
25554             ) {
25555               ctrl.$modelValue = ctrl.$$rawModelValue = modelValue;
25556               parserValid = undefined;
25557
25558               var formatters = ctrl.$formatters,
25559                   idx = formatters.length;
25560
25561               var viewValue = modelValue;
25562               while (idx--) {
25563                 viewValue = formatters[idx](viewValue);
25564               }
25565               if (ctrl.$viewValue !== viewValue) {
25566                 ctrl.$viewValue = ctrl.$$lastCommittedViewValue = viewValue;
25567                 ctrl.$render();
25568
25569                 ctrl.$$runValidators(modelValue, viewValue, noop);
25570               }
25571             }
25572
25573             return modelValue;
25574           });
25575         }];
25576
25577
25578         /**
25579          * @ngdoc directive
25580          * @name ngModel
25581          *
25582          * @element input
25583          * @priority 1
25584          *
25585          * @description
25586          * The `ngModel` directive binds an `input`,`select`, `textarea` (or custom form control) to a
25587          * property on the scope using {@link ngModel.NgModelController NgModelController},
25588          * which is created and exposed by this directive.
25589          *
25590          * `ngModel` is responsible for:
25591          *
25592          * - Binding the view into the model, which other directives such as `input`, `textarea` or `select`
25593          *   require.
25594          * - Providing validation behavior (i.e. required, number, email, url).
25595          * - Keeping the state of the control (valid/invalid, dirty/pristine, touched/untouched, validation errors).
25596          * - Setting related css classes on the element (`ng-valid`, `ng-invalid`, `ng-dirty`, `ng-pristine`, `ng-touched`, `ng-untouched`) including animations.
25597          * - Registering the control with its parent {@link ng.directive:form form}.
25598          *
25599          * Note: `ngModel` will try to bind to the property given by evaluating the expression on the
25600          * current scope. If the property doesn't already exist on this scope, it will be created
25601          * implicitly and added to the scope.
25602          *
25603          * For best practices on using `ngModel`, see:
25604          *
25605          *  - [Understanding Scopes](https://github.com/angular/angular.js/wiki/Understanding-Scopes)
25606          *
25607          * For basic examples, how to use `ngModel`, see:
25608          *
25609          *  - {@link ng.directive:input input}
25610          *    - {@link input[text] text}
25611          *    - {@link input[checkbox] checkbox}
25612          *    - {@link input[radio] radio}
25613          *    - {@link input[number] number}
25614          *    - {@link input[email] email}
25615          *    - {@link input[url] url}
25616          *    - {@link input[date] date}
25617          *    - {@link input[datetime-local] datetime-local}
25618          *    - {@link input[time] time}
25619          *    - {@link input[month] month}
25620          *    - {@link input[week] week}
25621          *  - {@link ng.directive:select select}
25622          *  - {@link ng.directive:textarea textarea}
25623          *
25624          * # CSS classes
25625          * The following CSS classes are added and removed on the associated input/select/textarea element
25626          * depending on the validity of the model.
25627          *
25628          *  - `ng-valid`: the model is valid
25629          *  - `ng-invalid`: the model is invalid
25630          *  - `ng-valid-[key]`: for each valid key added by `$setValidity`
25631          *  - `ng-invalid-[key]`: for each invalid key added by `$setValidity`
25632          *  - `ng-pristine`: the control hasn't been interacted with yet
25633          *  - `ng-dirty`: the control has been interacted with
25634          *  - `ng-touched`: the control has been blurred
25635          *  - `ng-untouched`: the control hasn't been blurred
25636          *  - `ng-pending`: any `$asyncValidators` are unfulfilled
25637          *
25638          * Keep in mind that ngAnimate can detect each of these classes when added and removed.
25639          *
25640          * ## Animation Hooks
25641          *
25642          * Animations within models are triggered when any of the associated CSS classes are added and removed
25643          * on the input element which is attached to the model. These classes are: `.ng-pristine`, `.ng-dirty`,
25644          * `.ng-invalid` and `.ng-valid` as well as any other validations that are performed on the model itself.
25645          * The animations that are triggered within ngModel are similar to how they work in ngClass and
25646          * animations can be hooked into using CSS transitions, keyframes as well as JS animations.
25647          *
25648          * The following example shows a simple way to utilize CSS transitions to style an input element
25649          * that has been rendered as invalid after it has been validated:
25650          *
25651          * <pre>
25652          * //be sure to include ngAnimate as a module to hook into more
25653          * //advanced animations
25654          * .my-input {
25655          *   transition:0.5s linear all;
25656          *   background: white;
25657          * }
25658          * .my-input.ng-invalid {
25659          *   background: red;
25660          *   color:white;
25661          * }
25662          * </pre>
25663          *
25664          * @example
25665          * <example deps="angular-animate.js" animations="true" fixBase="true" module="inputExample">
25666              <file name="index.html">
25667                <script>
25668                 angular.module('inputExample', [])
25669                   .controller('ExampleController', ['$scope', function($scope) {
25670                     $scope.val = '1';
25671                   }]);
25672                </script>
25673                <style>
25674                  .my-input {
25675                    transition:all linear 0.5s;
25676                    background: transparent;
25677                  }
25678                  .my-input.ng-invalid {
25679                    color:white;
25680                    background: red;
25681                  }
25682                </style>
25683                <p id="inputDescription">
25684                 Update input to see transitions when valid/invalid.
25685                 Integer is a valid value.
25686                </p>
25687                <form name="testForm" ng-controller="ExampleController">
25688                  <input ng-model="val" ng-pattern="/^\d+$/" name="anim" class="my-input"
25689                         aria-describedby="inputDescription" />
25690                </form>
25691              </file>
25692          * </example>
25693          *
25694          * ## Binding to a getter/setter
25695          *
25696          * Sometimes it's helpful to bind `ngModel` to a getter/setter function.  A getter/setter is a
25697          * function that returns a representation of the model when called with zero arguments, and sets
25698          * the internal state of a model when called with an argument. It's sometimes useful to use this
25699          * for models that have an internal representation that's different from what the model exposes
25700          * to the view.
25701          *
25702          * <div class="alert alert-success">
25703          * **Best Practice:** It's best to keep getters fast because Angular is likely to call them more
25704          * frequently than other parts of your code.
25705          * </div>
25706          *
25707          * You use this behavior by adding `ng-model-options="{ getterSetter: true }"` to an element that
25708          * has `ng-model` attached to it. You can also add `ng-model-options="{ getterSetter: true }"` to
25709          * a `<form>`, which will enable this behavior for all `<input>`s within it. See
25710          * {@link ng.directive:ngModelOptions `ngModelOptions`} for more.
25711          *
25712          * The following example shows how to use `ngModel` with a getter/setter:
25713          *
25714          * @example
25715          * <example name="ngModel-getter-setter" module="getterSetterExample">
25716              <file name="index.html">
25717                <div ng-controller="ExampleController">
25718                  <form name="userForm">
25719                    <label>Name:
25720                      <input type="text" name="userName"
25721                             ng-model="user.name"
25722                             ng-model-options="{ getterSetter: true }" />
25723                    </label>
25724                  </form>
25725                  <pre>user.name = <span ng-bind="user.name()"></span></pre>
25726                </div>
25727              </file>
25728              <file name="app.js">
25729                angular.module('getterSetterExample', [])
25730                  .controller('ExampleController', ['$scope', function($scope) {
25731                    var _name = 'Brian';
25732                    $scope.user = {
25733                      name: function(newName) {
25734                       // Note that newName can be undefined for two reasons:
25735                       // 1. Because it is called as a getter and thus called with no arguments
25736                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25737                       //    input is invalid
25738                       return arguments.length ? (_name = newName) : _name;
25739                      }
25740                    };
25741                  }]);
25742              </file>
25743          * </example>
25744          */
25745         var ngModelDirective = ['$rootScope', function($rootScope) {
25746           return {
25747             restrict: 'A',
25748             require: ['ngModel', '^?form', '^?ngModelOptions'],
25749             controller: NgModelController,
25750             // Prelink needs to run before any input directive
25751             // so that we can set the NgModelOptions in NgModelController
25752             // before anyone else uses it.
25753             priority: 1,
25754             compile: function ngModelCompile(element) {
25755               // Setup initial state of the control
25756               element.addClass(PRISTINE_CLASS).addClass(UNTOUCHED_CLASS).addClass(VALID_CLASS);
25757
25758               return {
25759                 pre: function ngModelPreLink(scope, element, attr, ctrls) {
25760                   var modelCtrl = ctrls[0],
25761                       formCtrl = ctrls[1] || modelCtrl.$$parentForm;
25762
25763                   modelCtrl.$$setOptions(ctrls[2] && ctrls[2].$options);
25764
25765                   // notify others, especially parent forms
25766                   formCtrl.$addControl(modelCtrl);
25767
25768                   attr.$observe('name', function(newValue) {
25769                     if (modelCtrl.$name !== newValue) {
25770                       modelCtrl.$$parentForm.$$renameControl(modelCtrl, newValue);
25771                     }
25772                   });
25773
25774                   scope.$on('$destroy', function() {
25775                     modelCtrl.$$parentForm.$removeControl(modelCtrl);
25776                   });
25777                 },
25778                 post: function ngModelPostLink(scope, element, attr, ctrls) {
25779                   var modelCtrl = ctrls[0];
25780                   if (modelCtrl.$options && modelCtrl.$options.updateOn) {
25781                     element.on(modelCtrl.$options.updateOn, function(ev) {
25782                       modelCtrl.$$debounceViewValueCommit(ev && ev.type);
25783                     });
25784                   }
25785
25786                   element.on('blur', function(ev) {
25787                     if (modelCtrl.$touched) return;
25788
25789                     if ($rootScope.$$phase) {
25790                       scope.$evalAsync(modelCtrl.$setTouched);
25791                     } else {
25792                       scope.$apply(modelCtrl.$setTouched);
25793                     }
25794                   });
25795                 }
25796               };
25797             }
25798           };
25799         }];
25800
25801         var DEFAULT_REGEXP = /(\s+|^)default(\s+|$)/;
25802
25803         /**
25804          * @ngdoc directive
25805          * @name ngModelOptions
25806          *
25807          * @description
25808          * Allows tuning how model updates are done. Using `ngModelOptions` you can specify a custom list of
25809          * events that will trigger a model update and/or a debouncing delay so that the actual update only
25810          * takes place when a timer expires; this timer will be reset after another change takes place.
25811          *
25812          * Given the nature of `ngModelOptions`, the value displayed inside input fields in the view might
25813          * be different from the value in the actual model. This means that if you update the model you
25814          * should also invoke {@link ngModel.NgModelController `$rollbackViewValue`} on the relevant input field in
25815          * order to make sure it is synchronized with the model and that any debounced action is canceled.
25816          *
25817          * The easiest way to reference the control's {@link ngModel.NgModelController `$rollbackViewValue`}
25818          * method is by making sure the input is placed inside a form that has a `name` attribute. This is
25819          * important because `form` controllers are published to the related scope under the name in their
25820          * `name` attribute.
25821          *
25822          * Any pending changes will take place immediately when an enclosing form is submitted via the
25823          * `submit` event. Note that `ngClick` events will occur before the model is updated. Use `ngSubmit`
25824          * to have access to the updated model.
25825          *
25826          * `ngModelOptions` has an effect on the element it's declared on and its descendants.
25827          *
25828          * @param {Object} ngModelOptions options to apply to the current model. Valid keys are:
25829          *   - `updateOn`: string specifying which event should the input be bound to. You can set several
25830          *     events using an space delimited list. There is a special event called `default` that
25831          *     matches the default events belonging of the control.
25832          *   - `debounce`: integer value which contains the debounce model update value in milliseconds. A
25833          *     value of 0 triggers an immediate update. If an object is supplied instead, you can specify a
25834          *     custom value for each event. For example:
25835          *     `ng-model-options="{ updateOn: 'default blur', debounce: { 'default': 500, 'blur': 0 } }"`
25836          *   - `allowInvalid`: boolean value which indicates that the model can be set with values that did
25837          *     not validate correctly instead of the default behavior of setting the model to undefined.
25838          *   - `getterSetter`: boolean value which determines whether or not to treat functions bound to
25839                `ngModel` as getters/setters.
25840          *   - `timezone`: Defines the timezone to be used to read/write the `Date` instance in the model for
25841          *     `<input type="date">`, `<input type="time">`, ... . It understands UTC/GMT and the
25842          *     continental US time zone abbreviations, but for general use, use a time zone offset, for
25843          *     example, `'+0430'` (4 hours, 30 minutes east of the Greenwich meridian)
25844          *     If not specified, the timezone of the browser will be used.
25845          *
25846          * @example
25847
25848           The following example shows how to override immediate updates. Changes on the inputs within the
25849           form will update the model only when the control loses focus (blur event). If `escape` key is
25850           pressed while the input field is focused, the value is reset to the value in the current model.
25851
25852           <example name="ngModelOptions-directive-blur" module="optionsExample">
25853             <file name="index.html">
25854               <div ng-controller="ExampleController">
25855                 <form name="userForm">
25856                   <label>Name:
25857                     <input type="text" name="userName"
25858                            ng-model="user.name"
25859                            ng-model-options="{ updateOn: 'blur' }"
25860                            ng-keyup="cancel($event)" />
25861                   </label><br />
25862                   <label>Other data:
25863                     <input type="text" ng-model="user.data" />
25864                   </label><br />
25865                 </form>
25866                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25867                 <pre>user.data = <span ng-bind="user.data"></span></pre>
25868               </div>
25869             </file>
25870             <file name="app.js">
25871               angular.module('optionsExample', [])
25872                 .controller('ExampleController', ['$scope', function($scope) {
25873                   $scope.user = { name: 'John', data: '' };
25874
25875                   $scope.cancel = function(e) {
25876                     if (e.keyCode == 27) {
25877                       $scope.userForm.userName.$rollbackViewValue();
25878                     }
25879                   };
25880                 }]);
25881             </file>
25882             <file name="protractor.js" type="protractor">
25883               var model = element(by.binding('user.name'));
25884               var input = element(by.model('user.name'));
25885               var other = element(by.model('user.data'));
25886
25887               it('should allow custom events', function() {
25888                 input.sendKeys(' Doe');
25889                 input.click();
25890                 expect(model.getText()).toEqual('John');
25891                 other.click();
25892                 expect(model.getText()).toEqual('John Doe');
25893               });
25894
25895               it('should $rollbackViewValue when model changes', function() {
25896                 input.sendKeys(' Doe');
25897                 expect(input.getAttribute('value')).toEqual('John Doe');
25898                 input.sendKeys(protractor.Key.ESCAPE);
25899                 expect(input.getAttribute('value')).toEqual('John');
25900                 other.click();
25901                 expect(model.getText()).toEqual('John');
25902               });
25903             </file>
25904           </example>
25905
25906           This one shows how to debounce model changes. Model will be updated only 1 sec after last change.
25907           If the `Clear` button is pressed, any debounced action is canceled and the value becomes empty.
25908
25909           <example name="ngModelOptions-directive-debounce" module="optionsExample">
25910             <file name="index.html">
25911               <div ng-controller="ExampleController">
25912                 <form name="userForm">
25913                   <label>Name:
25914                     <input type="text" name="userName"
25915                            ng-model="user.name"
25916                            ng-model-options="{ debounce: 1000 }" />
25917                   </label>
25918                   <button ng-click="userForm.userName.$rollbackViewValue(); user.name=''">Clear</button>
25919                   <br />
25920                 </form>
25921                 <pre>user.name = <span ng-bind="user.name"></span></pre>
25922               </div>
25923             </file>
25924             <file name="app.js">
25925               angular.module('optionsExample', [])
25926                 .controller('ExampleController', ['$scope', function($scope) {
25927                   $scope.user = { name: 'Igor' };
25928                 }]);
25929             </file>
25930           </example>
25931
25932           This one shows how to bind to getter/setters:
25933
25934           <example name="ngModelOptions-directive-getter-setter" module="getterSetterExample">
25935             <file name="index.html">
25936               <div ng-controller="ExampleController">
25937                 <form name="userForm">
25938                   <label>Name:
25939                     <input type="text" name="userName"
25940                            ng-model="user.name"
25941                            ng-model-options="{ getterSetter: true }" />
25942                   </label>
25943                 </form>
25944                 <pre>user.name = <span ng-bind="user.name()"></span></pre>
25945               </div>
25946             </file>
25947             <file name="app.js">
25948               angular.module('getterSetterExample', [])
25949                 .controller('ExampleController', ['$scope', function($scope) {
25950                   var _name = 'Brian';
25951                   $scope.user = {
25952                     name: function(newName) {
25953                       // Note that newName can be undefined for two reasons:
25954                       // 1. Because it is called as a getter and thus called with no arguments
25955                       // 2. Because the property should actually be set to undefined. This happens e.g. if the
25956                       //    input is invalid
25957                       return arguments.length ? (_name = newName) : _name;
25958                     }
25959                   };
25960                 }]);
25961             </file>
25962           </example>
25963          */
25964         var ngModelOptionsDirective = function() {
25965           return {
25966             restrict: 'A',
25967             controller: ['$scope', '$attrs', function($scope, $attrs) {
25968               var that = this;
25969               this.$options = copy($scope.$eval($attrs.ngModelOptions));
25970               // Allow adding/overriding bound events
25971               if (isDefined(this.$options.updateOn)) {
25972                 this.$options.updateOnDefault = false;
25973                 // extract "default" pseudo-event from list of events that can trigger a model update
25974                 this.$options.updateOn = trim(this.$options.updateOn.replace(DEFAULT_REGEXP, function() {
25975                   that.$options.updateOnDefault = true;
25976                   return ' ';
25977                 }));
25978               } else {
25979                 this.$options.updateOnDefault = true;
25980               }
25981             }]
25982           };
25983         };
25984
25985
25986
25987         // helper methods
25988         function addSetValidityMethod(context) {
25989           var ctrl = context.ctrl,
25990               $element = context.$element,
25991               classCache = {},
25992               set = context.set,
25993               unset = context.unset,
25994               $animate = context.$animate;
25995
25996           classCache[INVALID_CLASS] = !(classCache[VALID_CLASS] = $element.hasClass(VALID_CLASS));
25997
25998           ctrl.$setValidity = setValidity;
25999
26000           function setValidity(validationErrorKey, state, controller) {
26001             if (isUndefined(state)) {
26002               createAndSet('$pending', validationErrorKey, controller);
26003             } else {
26004               unsetAndCleanup('$pending', validationErrorKey, controller);
26005             }
26006             if (!isBoolean(state)) {
26007               unset(ctrl.$error, validationErrorKey, controller);
26008               unset(ctrl.$$success, validationErrorKey, controller);
26009             } else {
26010               if (state) {
26011                 unset(ctrl.$error, validationErrorKey, controller);
26012                 set(ctrl.$$success, validationErrorKey, controller);
26013               } else {
26014                 set(ctrl.$error, validationErrorKey, controller);
26015                 unset(ctrl.$$success, validationErrorKey, controller);
26016               }
26017             }
26018             if (ctrl.$pending) {
26019               cachedToggleClass(PENDING_CLASS, true);
26020               ctrl.$valid = ctrl.$invalid = undefined;
26021               toggleValidationCss('', null);
26022             } else {
26023               cachedToggleClass(PENDING_CLASS, false);
26024               ctrl.$valid = isObjectEmpty(ctrl.$error);
26025               ctrl.$invalid = !ctrl.$valid;
26026               toggleValidationCss('', ctrl.$valid);
26027             }
26028
26029             // re-read the state as the set/unset methods could have
26030             // combined state in ctrl.$error[validationError] (used for forms),
26031             // where setting/unsetting only increments/decrements the value,
26032             // and does not replace it.
26033             var combinedState;
26034             if (ctrl.$pending && ctrl.$pending[validationErrorKey]) {
26035               combinedState = undefined;
26036             } else if (ctrl.$error[validationErrorKey]) {
26037               combinedState = false;
26038             } else if (ctrl.$$success[validationErrorKey]) {
26039               combinedState = true;
26040             } else {
26041               combinedState = null;
26042             }
26043
26044             toggleValidationCss(validationErrorKey, combinedState);
26045             ctrl.$$parentForm.$setValidity(validationErrorKey, combinedState, ctrl);
26046           }
26047
26048           function createAndSet(name, value, controller) {
26049             if (!ctrl[name]) {
26050               ctrl[name] = {};
26051             }
26052             set(ctrl[name], value, controller);
26053           }
26054
26055           function unsetAndCleanup(name, value, controller) {
26056             if (ctrl[name]) {
26057               unset(ctrl[name], value, controller);
26058             }
26059             if (isObjectEmpty(ctrl[name])) {
26060               ctrl[name] = undefined;
26061             }
26062           }
26063
26064           function cachedToggleClass(className, switchValue) {
26065             if (switchValue && !classCache[className]) {
26066               $animate.addClass($element, className);
26067               classCache[className] = true;
26068             } else if (!switchValue && classCache[className]) {
26069               $animate.removeClass($element, className);
26070               classCache[className] = false;
26071             }
26072           }
26073
26074           function toggleValidationCss(validationErrorKey, isValid) {
26075             validationErrorKey = validationErrorKey ? '-' + snake_case(validationErrorKey, '-') : '';
26076
26077             cachedToggleClass(VALID_CLASS + validationErrorKey, isValid === true);
26078             cachedToggleClass(INVALID_CLASS + validationErrorKey, isValid === false);
26079           }
26080         }
26081
26082         function isObjectEmpty(obj) {
26083           if (obj) {
26084             for (var prop in obj) {
26085               if (obj.hasOwnProperty(prop)) {
26086                 return false;
26087               }
26088             }
26089           }
26090           return true;
26091         }
26092
26093         /**
26094          * @ngdoc directive
26095          * @name ngNonBindable
26096          * @restrict AC
26097          * @priority 1000
26098          *
26099          * @description
26100          * The `ngNonBindable` directive tells Angular not to compile or bind the contents of the current
26101          * DOM element. This is useful if the element contains what appears to be Angular directives and
26102          * bindings but which should be ignored by Angular. This could be the case if you have a site that
26103          * displays snippets of code, for instance.
26104          *
26105          * @element ANY
26106          *
26107          * @example
26108          * In this example there are two locations where a simple interpolation binding (`{{}}`) is present,
26109          * but the one wrapped in `ngNonBindable` is left alone.
26110          *
26111          * @example
26112             <example>
26113               <file name="index.html">
26114                 <div>Normal: {{1 + 2}}</div>
26115                 <div ng-non-bindable>Ignored: {{1 + 2}}</div>
26116               </file>
26117               <file name="protractor.js" type="protractor">
26118                it('should check ng-non-bindable', function() {
26119                  expect(element(by.binding('1 + 2')).getText()).toContain('3');
26120                  expect(element.all(by.css('div')).last().getText()).toMatch(/1 \+ 2/);
26121                });
26122               </file>
26123             </example>
26124          */
26125         var ngNonBindableDirective = ngDirective({ terminal: true, priority: 1000 });
26126
26127         /* global jqLiteRemove */
26128
26129         var ngOptionsMinErr = minErr('ngOptions');
26130
26131         /**
26132          * @ngdoc directive
26133          * @name ngOptions
26134          * @restrict A
26135          *
26136          * @description
26137          *
26138          * The `ngOptions` attribute can be used to dynamically generate a list of `<option>`
26139          * elements for the `<select>` element using the array or object obtained by evaluating the
26140          * `ngOptions` comprehension expression.
26141          *
26142          * In many cases, `ngRepeat` can be used on `<option>` elements instead of `ngOptions` to achieve a
26143          * similar result. However, `ngOptions` provides some benefits such as reducing memory and
26144          * increasing speed by not creating a new scope for each repeated instance, as well as providing
26145          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
26146          * comprehension expression. `ngOptions` should be used when the `<select>` model needs to be bound
26147          *  to a non-string value. This is because an option element can only be bound to string values at
26148          * present.
26149          *
26150          * When an item in the `<select>` menu is selected, the array element or object property
26151          * represented by the selected option will be bound to the model identified by the `ngModel`
26152          * directive.
26153          *
26154          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
26155          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
26156          * option. See example below for demonstration.
26157          *
26158          * ## Complex Models (objects or collections)
26159          *
26160          * By default, `ngModel` watches the model by reference, not value. This is important to know when
26161          * binding the select to a model that is an object or a collection.
26162          *
26163          * One issue occurs if you want to preselect an option. For example, if you set
26164          * the model to an object that is equal to an object in your collection, `ngOptions` won't be able to set the selection,
26165          * because the objects are not identical. So by default, you should always reference the item in your collection
26166          * for preselections, e.g.: `$scope.selected = $scope.collection[3]`.
26167          *
26168          * Another solution is to use a `track by` clause, because then `ngOptions` will track the identity
26169          * of the item not by reference, but by the result of the `track by` expression. For example, if your
26170          * collection items have an id property, you would `track by item.id`.
26171          *
26172          * A different issue with objects or collections is that ngModel won't detect if an object property or
26173          * a collection item changes. For that reason, `ngOptions` additionally watches the model using
26174          * `$watchCollection`, when the expression contains a `track by` clause or the the select has the `multiple` attribute.
26175          * This allows ngOptions to trigger a re-rendering of the options even if the actual object/collection
26176          * has not changed identity, but only a property on the object or an item in the collection changes.
26177          *
26178          * Note that `$watchCollection` does a shallow comparison of the properties of the object (or the items in the collection
26179          * if the model is an array). This means that changing a property deeper than the first level inside the
26180          * object/collection will not trigger a re-rendering.
26181          *
26182          * ## `select` **`as`**
26183          *
26184          * Using `select` **`as`** will bind the result of the `select` expression to the model, but
26185          * the value of the `<select>` and `<option>` html elements will be either the index (for array data sources)
26186          * or property name (for object data sources) of the value within the collection. If a **`track by`** expression
26187          * is used, the result of that expression will be set as the value of the `option` and `select` elements.
26188          *
26189          *
26190          * ### `select` **`as`** and **`track by`**
26191          *
26192          * <div class="alert alert-warning">
26193          * Be careful when using `select` **`as`** and **`track by`** in the same expression.
26194          * </div>
26195          *
26196          * Given this array of items on the $scope:
26197          *
26198          * ```js
26199          * $scope.items = [{
26200          *   id: 1,
26201          *   label: 'aLabel',
26202          *   subItem: { name: 'aSubItem' }
26203          * }, {
26204          *   id: 2,
26205          *   label: 'bLabel',
26206          *   subItem: { name: 'bSubItem' }
26207          * }];
26208          * ```
26209          *
26210          * This will work:
26211          *
26212          * ```html
26213          * <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
26214          * ```
26215          * ```js
26216          * $scope.selected = $scope.items[0];
26217          * ```
26218          *
26219          * but this will not work:
26220          *
26221          * ```html
26222          * <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
26223          * ```
26224          * ```js
26225          * $scope.selected = $scope.items[0].subItem;
26226          * ```
26227          *
26228          * In both examples, the **`track by`** expression is applied successfully to each `item` in the
26229          * `items` array. Because the selected option has been set programmatically in the controller, the
26230          * **`track by`** expression is also applied to the `ngModel` value. In the first example, the
26231          * `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
26232          * no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
26233          * expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
26234          * is not matched against any `<option>` and the `<select>` appears as having no selected value.
26235          *
26236          *
26237          * @param {string} ngModel Assignable angular expression to data-bind to.
26238          * @param {string=} name Property name of the form under which the control is published.
26239          * @param {string=} required The control is considered valid only if value is entered.
26240          * @param {string=} ngRequired Adds `required` attribute and `required` validation constraint to
26241          *    the element when the ngRequired expression evaluates to true. Use `ngRequired` instead of
26242          *    `required` when you want to data-bind to the `required` attribute.
26243          * @param {comprehension_expression=} ngOptions in one of the following forms:
26244          *
26245          *   * for array data sources:
26246          *     * `label` **`for`** `value` **`in`** `array`
26247          *     * `select` **`as`** `label` **`for`** `value` **`in`** `array`
26248          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array`
26249          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array`
26250          *     * `label` **`group by`** `group` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26251          *     * `label` **`disable when`** `disable` **`for`** `value` **`in`** `array` **`track by`** `trackexpr`
26252          *     * `label` **`for`** `value` **`in`** `array` | orderBy:`orderexpr` **`track by`** `trackexpr`
26253          *        (for including a filter with `track by`)
26254          *   * for object data sources:
26255          *     * `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26256          *     * `select` **`as`** `label` **`for (`**`key` **`,`** `value`**`) in`** `object`
26257          *     * `label` **`group by`** `group` **`for (`**`key`**`,`** `value`**`) in`** `object`
26258          *     * `label` **`disable when`** `disable` **`for (`**`key`**`,`** `value`**`) in`** `object`
26259          *     * `select` **`as`** `label` **`group by`** `group`
26260          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26261          *     * `select` **`as`** `label` **`disable when`** `disable`
26262          *         **`for` `(`**`key`**`,`** `value`**`) in`** `object`
26263          *
26264          * Where:
26265          *
26266          *   * `array` / `object`: an expression which evaluates to an array / object to iterate over.
26267          *   * `value`: local variable which will refer to each item in the `array` or each property value
26268          *      of `object` during iteration.
26269          *   * `key`: local variable which will refer to a property name in `object` during iteration.
26270          *   * `label`: The result of this expression will be the label for `<option>` element. The
26271          *     `expression` will most likely refer to the `value` variable (e.g. `value.propertyName`).
26272          *   * `select`: The result of this expression will be bound to the model of the parent `<select>`
26273          *      element. If not specified, `select` expression will default to `value`.
26274          *   * `group`: The result of this expression will be used to group options using the `<optgroup>`
26275          *      DOM element.
26276          *   * `disable`: The result of this expression will be used to disable the rendered `<option>`
26277          *      element. Return `true` to disable.
26278          *   * `trackexpr`: Used when working with an array of objects. The result of this expression will be
26279          *      used to identify the objects in the array. The `trackexpr` will most likely refer to the
26280          *     `value` variable (e.g. `value.propertyName`). With this the selection is preserved
26281          *      even when the options are recreated (e.g. reloaded from the server).
26282          *
26283          * @example
26284             <example module="selectExample">
26285               <file name="index.html">
26286                 <script>
26287                 angular.module('selectExample', [])
26288                   .controller('ExampleController', ['$scope', function($scope) {
26289                     $scope.colors = [
26290                       {name:'black', shade:'dark'},
26291                       {name:'white', shade:'light', notAnOption: true},
26292                       {name:'red', shade:'dark'},
26293                       {name:'blue', shade:'dark', notAnOption: true},
26294                       {name:'yellow', shade:'light', notAnOption: false}
26295                     ];
26296                     $scope.myColor = $scope.colors[2]; // red
26297                   }]);
26298                 </script>
26299                 <div ng-controller="ExampleController">
26300                   <ul>
26301                     <li ng-repeat="color in colors">
26302                       <label>Name: <input ng-model="color.name"></label>
26303                       <label><input type="checkbox" ng-model="color.notAnOption"> Disabled?</label>
26304                       <button ng-click="colors.splice($index, 1)" aria-label="Remove">X</button>
26305                     </li>
26306                     <li>
26307                       <button ng-click="colors.push({})">add</button>
26308                     </li>
26309                   </ul>
26310                   <hr/>
26311                   <label>Color (null not allowed):
26312                     <select ng-model="myColor" ng-options="color.name for color in colors"></select>
26313                   </label><br/>
26314                   <label>Color (null allowed):
26315                   <span  class="nullable">
26316                     <select ng-model="myColor" ng-options="color.name for color in colors">
26317                       <option value="">-- choose color --</option>
26318                     </select>
26319                   </span></label><br/>
26320
26321                   <label>Color grouped by shade:
26322                     <select ng-model="myColor" ng-options="color.name group by color.shade for color in colors">
26323                     </select>
26324                   </label><br/>
26325
26326                   <label>Color grouped by shade, with some disabled:
26327                     <select ng-model="myColor"
26328                           ng-options="color.name group by color.shade disable when color.notAnOption for color in colors">
26329                     </select>
26330                   </label><br/>
26331
26332
26333
26334                   Select <button ng-click="myColor = { name:'not in list', shade: 'other' }">bogus</button>.
26335                   <br/>
26336                   <hr/>
26337                   Currently selected: {{ {selected_color:myColor} }}
26338                   <div style="border:solid 1px black; height:20px"
26339                        ng-style="{'background-color':myColor.name}">
26340                   </div>
26341                 </div>
26342               </file>
26343               <file name="protractor.js" type="protractor">
26344                  it('should check ng-options', function() {
26345                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('red');
26346                    element.all(by.model('myColor')).first().click();
26347                    element.all(by.css('select[ng-model="myColor"] option')).first().click();
26348                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('black');
26349                    element(by.css('.nullable select[ng-model="myColor"]')).click();
26350                    element.all(by.css('.nullable select[ng-model="myColor"] option')).first().click();
26351                    expect(element(by.binding('{selected_color:myColor}')).getText()).toMatch('null');
26352                  });
26353               </file>
26354             </example>
26355          */
26356
26357         // jshint maxlen: false
26358         //                     //00001111111111000000000002222222222000000000000000000000333333333300000000000000000000000004444444444400000000000005555555555555550000000006666666666666660000000777777777777777000000000000000888888888800000000000000000009999999999
26359         var 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]+?))?$/;
26360                                 // 1: value expression (valueFn)
26361                                 // 2: label expression (displayFn)
26362                                 // 3: group by expression (groupByFn)
26363                                 // 4: disable when expression (disableWhenFn)
26364                                 // 5: array item variable name
26365                                 // 6: object item key variable name
26366                                 // 7: object item value variable name
26367                                 // 8: collection expression
26368                                 // 9: track by expression
26369         // jshint maxlen: 100
26370
26371
26372         var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
26373
26374           function parseOptionsExpression(optionsExp, selectElement, scope) {
26375
26376             var match = optionsExp.match(NG_OPTIONS_REGEXP);
26377             if (!(match)) {
26378               throw ngOptionsMinErr('iexp',
26379                 "Expected expression in form of " +
26380                 "'_select_ (as _label_)? for (_key_,)?_value_ in _collection_'" +
26381                 " but got '{0}'. Element: {1}",
26382                 optionsExp, startingTag(selectElement));
26383             }
26384
26385             // Extract the parts from the ngOptions expression
26386
26387             // The variable name for the value of the item in the collection
26388             var valueName = match[5] || match[7];
26389             // The variable name for the key of the item in the collection
26390             var keyName = match[6];
26391
26392             // An expression that generates the viewValue for an option if there is a label expression
26393             var selectAs = / as /.test(match[0]) && match[1];
26394             // An expression that is used to track the id of each object in the options collection
26395             var trackBy = match[9];
26396             // An expression that generates the viewValue for an option if there is no label expression
26397             var valueFn = $parse(match[2] ? match[1] : valueName);
26398             var selectAsFn = selectAs && $parse(selectAs);
26399             var viewValueFn = selectAsFn || valueFn;
26400             var trackByFn = trackBy && $parse(trackBy);
26401
26402             // Get the value by which we are going to track the option
26403             // if we have a trackFn then use that (passing scope and locals)
26404             // otherwise just hash the given viewValue
26405             var getTrackByValueFn = trackBy ?
26406                                       function(value, locals) { return trackByFn(scope, locals); } :
26407                                       function getHashOfValue(value) { return hashKey(value); };
26408             var getTrackByValue = function(value, key) {
26409               return getTrackByValueFn(value, getLocals(value, key));
26410             };
26411
26412             var displayFn = $parse(match[2] || match[1]);
26413             var groupByFn = $parse(match[3] || '');
26414             var disableWhenFn = $parse(match[4] || '');
26415             var valuesFn = $parse(match[8]);
26416
26417             var locals = {};
26418             var getLocals = keyName ? function(value, key) {
26419               locals[keyName] = key;
26420               locals[valueName] = value;
26421               return locals;
26422             } : function(value) {
26423               locals[valueName] = value;
26424               return locals;
26425             };
26426
26427
26428             function Option(selectValue, viewValue, label, group, disabled) {
26429               this.selectValue = selectValue;
26430               this.viewValue = viewValue;
26431               this.label = label;
26432               this.group = group;
26433               this.disabled = disabled;
26434             }
26435
26436             function getOptionValuesKeys(optionValues) {
26437               var optionValuesKeys;
26438
26439               if (!keyName && isArrayLike(optionValues)) {
26440                 optionValuesKeys = optionValues;
26441               } else {
26442                 // if object, extract keys, in enumeration order, unsorted
26443                 optionValuesKeys = [];
26444                 for (var itemKey in optionValues) {
26445                   if (optionValues.hasOwnProperty(itemKey) && itemKey.charAt(0) !== '$') {
26446                     optionValuesKeys.push(itemKey);
26447                   }
26448                 }
26449               }
26450               return optionValuesKeys;
26451             }
26452
26453             return {
26454               trackBy: trackBy,
26455               getTrackByValue: getTrackByValue,
26456               getWatchables: $parse(valuesFn, function(optionValues) {
26457                 // Create a collection of things that we would like to watch (watchedArray)
26458                 // so that they can all be watched using a single $watchCollection
26459                 // that only runs the handler once if anything changes
26460                 var watchedArray = [];
26461                 optionValues = optionValues || [];
26462
26463                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26464                 var optionValuesLength = optionValuesKeys.length;
26465                 for (var index = 0; index < optionValuesLength; index++) {
26466                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26467                   var value = optionValues[key];
26468
26469                   var locals = getLocals(optionValues[key], key);
26470                   var selectValue = getTrackByValueFn(optionValues[key], locals);
26471                   watchedArray.push(selectValue);
26472
26473                   // Only need to watch the displayFn if there is a specific label expression
26474                   if (match[2] || match[1]) {
26475                     var label = displayFn(scope, locals);
26476                     watchedArray.push(label);
26477                   }
26478
26479                   // Only need to watch the disableWhenFn if there is a specific disable expression
26480                   if (match[4]) {
26481                     var disableWhen = disableWhenFn(scope, locals);
26482                     watchedArray.push(disableWhen);
26483                   }
26484                 }
26485                 return watchedArray;
26486               }),
26487
26488               getOptions: function() {
26489
26490                 var optionItems = [];
26491                 var selectValueMap = {};
26492
26493                 // The option values were already computed in the `getWatchables` fn,
26494                 // which must have been called to trigger `getOptions`
26495                 var optionValues = valuesFn(scope) || [];
26496                 var optionValuesKeys = getOptionValuesKeys(optionValues);
26497                 var optionValuesLength = optionValuesKeys.length;
26498
26499                 for (var index = 0; index < optionValuesLength; index++) {
26500                   var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
26501                   var value = optionValues[key];
26502                   var locals = getLocals(value, key);
26503                   var viewValue = viewValueFn(scope, locals);
26504                   var selectValue = getTrackByValueFn(viewValue, locals);
26505                   var label = displayFn(scope, locals);
26506                   var group = groupByFn(scope, locals);
26507                   var disabled = disableWhenFn(scope, locals);
26508                   var optionItem = new Option(selectValue, viewValue, label, group, disabled);
26509
26510                   optionItems.push(optionItem);
26511                   selectValueMap[selectValue] = optionItem;
26512                 }
26513
26514                 return {
26515                   items: optionItems,
26516                   selectValueMap: selectValueMap,
26517                   getOptionFromViewValue: function(value) {
26518                     return selectValueMap[getTrackByValue(value)];
26519                   },
26520                   getViewValueFromOption: function(option) {
26521                     // If the viewValue could be an object that may be mutated by the application,
26522                     // we need to make a copy and not return the reference to the value on the option.
26523                     return trackBy ? angular.copy(option.viewValue) : option.viewValue;
26524                   }
26525                 };
26526               }
26527             };
26528           }
26529
26530
26531           // we can't just jqLite('<option>') since jqLite is not smart enough
26532           // to create it in <select> and IE barfs otherwise.
26533           var optionTemplate = document.createElement('option'),
26534               optGroupTemplate = document.createElement('optgroup');
26535
26536
26537             function ngOptionsPostLink(scope, selectElement, attr, ctrls) {
26538
26539               // if ngModel is not defined, we don't need to do anything
26540               var ngModelCtrl = ctrls[1];
26541               if (!ngModelCtrl) return;
26542
26543               var selectCtrl = ctrls[0];
26544               var multiple = attr.multiple;
26545
26546               // The emptyOption allows the application developer to provide their own custom "empty"
26547               // option when the viewValue does not match any of the option values.
26548               var emptyOption;
26549               for (var i = 0, children = selectElement.children(), ii = children.length; i < ii; i++) {
26550                 if (children[i].value === '') {
26551                   emptyOption = children.eq(i);
26552                   break;
26553                 }
26554               }
26555
26556               var providedEmptyOption = !!emptyOption;
26557
26558               var unknownOption = jqLite(optionTemplate.cloneNode(false));
26559               unknownOption.val('?');
26560
26561               var options;
26562               var ngOptions = parseOptionsExpression(attr.ngOptions, selectElement, scope);
26563
26564
26565               var renderEmptyOption = function() {
26566                 if (!providedEmptyOption) {
26567                   selectElement.prepend(emptyOption);
26568                 }
26569                 selectElement.val('');
26570                 emptyOption.prop('selected', true); // needed for IE
26571                 emptyOption.attr('selected', true);
26572               };
26573
26574               var removeEmptyOption = function() {
26575                 if (!providedEmptyOption) {
26576                   emptyOption.remove();
26577                 }
26578               };
26579
26580
26581               var renderUnknownOption = function() {
26582                 selectElement.prepend(unknownOption);
26583                 selectElement.val('?');
26584                 unknownOption.prop('selected', true); // needed for IE
26585                 unknownOption.attr('selected', true);
26586               };
26587
26588               var removeUnknownOption = function() {
26589                 unknownOption.remove();
26590               };
26591
26592               // Update the controller methods for multiple selectable options
26593               if (!multiple) {
26594
26595                 selectCtrl.writeValue = function writeNgOptionsValue(value) {
26596                   var option = options.getOptionFromViewValue(value);
26597
26598                   if (option && !option.disabled) {
26599                     if (selectElement[0].value !== option.selectValue) {
26600                       removeUnknownOption();
26601                       removeEmptyOption();
26602
26603                       selectElement[0].value = option.selectValue;
26604                       option.element.selected = true;
26605                       option.element.setAttribute('selected', 'selected');
26606                     }
26607                   } else {
26608                     if (value === null || providedEmptyOption) {
26609                       removeUnknownOption();
26610                       renderEmptyOption();
26611                     } else {
26612                       removeEmptyOption();
26613                       renderUnknownOption();
26614                     }
26615                   }
26616                 };
26617
26618                 selectCtrl.readValue = function readNgOptionsValue() {
26619
26620                   var selectedOption = options.selectValueMap[selectElement.val()];
26621
26622                   if (selectedOption && !selectedOption.disabled) {
26623                     removeEmptyOption();
26624                     removeUnknownOption();
26625                     return options.getViewValueFromOption(selectedOption);
26626                   }
26627                   return null;
26628                 };
26629
26630                 // If we are using `track by` then we must watch the tracked value on the model
26631                 // since ngModel only watches for object identity change
26632                 if (ngOptions.trackBy) {
26633                   scope.$watch(
26634                     function() { return ngOptions.getTrackByValue(ngModelCtrl.$viewValue); },
26635                     function() { ngModelCtrl.$render(); }
26636                   );
26637                 }
26638
26639               } else {
26640
26641                 ngModelCtrl.$isEmpty = function(value) {
26642                   return !value || value.length === 0;
26643                 };
26644
26645
26646                 selectCtrl.writeValue = function writeNgOptionsMultiple(value) {
26647                   options.items.forEach(function(option) {
26648                     option.element.selected = false;
26649                   });
26650
26651                   if (value) {
26652                     value.forEach(function(item) {
26653                       var option = options.getOptionFromViewValue(item);
26654                       if (option && !option.disabled) option.element.selected = true;
26655                     });
26656                   }
26657                 };
26658
26659
26660                 selectCtrl.readValue = function readNgOptionsMultiple() {
26661                   var selectedValues = selectElement.val() || [],
26662                       selections = [];
26663
26664                   forEach(selectedValues, function(value) {
26665                     var option = options.selectValueMap[value];
26666                     if (option && !option.disabled) selections.push(options.getViewValueFromOption(option));
26667                   });
26668
26669                   return selections;
26670                 };
26671
26672                 // If we are using `track by` then we must watch these tracked values on the model
26673                 // since ngModel only watches for object identity change
26674                 if (ngOptions.trackBy) {
26675
26676                   scope.$watchCollection(function() {
26677                     if (isArray(ngModelCtrl.$viewValue)) {
26678                       return ngModelCtrl.$viewValue.map(function(value) {
26679                         return ngOptions.getTrackByValue(value);
26680                       });
26681                     }
26682                   }, function() {
26683                     ngModelCtrl.$render();
26684                   });
26685
26686                 }
26687               }
26688
26689
26690               if (providedEmptyOption) {
26691
26692                 // we need to remove it before calling selectElement.empty() because otherwise IE will
26693                 // remove the label from the element. wtf?
26694                 emptyOption.remove();
26695
26696                 // compile the element since there might be bindings in it
26697                 $compile(emptyOption)(scope);
26698
26699                 // remove the class, which is added automatically because we recompile the element and it
26700                 // becomes the compilation root
26701                 emptyOption.removeClass('ng-scope');
26702               } else {
26703                 emptyOption = jqLite(optionTemplate.cloneNode(false));
26704               }
26705
26706               // We need to do this here to ensure that the options object is defined
26707               // when we first hit it in writeNgOptionsValue
26708               updateOptions();
26709
26710               // We will re-render the option elements if the option values or labels change
26711               scope.$watchCollection(ngOptions.getWatchables, updateOptions);
26712
26713               // ------------------------------------------------------------------ //
26714
26715
26716               function updateOptionElement(option, element) {
26717                 option.element = element;
26718                 element.disabled = option.disabled;
26719                 // NOTE: The label must be set before the value, otherwise IE10/11/EDGE create unresponsive
26720                 // selects in certain circumstances when multiple selects are next to each other and display
26721                 // the option list in listbox style, i.e. the select is [multiple], or specifies a [size].
26722                 // See https://github.com/angular/angular.js/issues/11314 for more info.
26723                 // This is unfortunately untestable with unit / e2e tests
26724                 if (option.label !== element.label) {
26725                   element.label = option.label;
26726                   element.textContent = option.label;
26727                 }
26728                 if (option.value !== element.value) element.value = option.selectValue;
26729               }
26730
26731               function addOrReuseElement(parent, current, type, templateElement) {
26732                 var element;
26733                 // Check whether we can reuse the next element
26734                 if (current && lowercase(current.nodeName) === type) {
26735                   // The next element is the right type so reuse it
26736                   element = current;
26737                 } else {
26738                   // The next element is not the right type so create a new one
26739                   element = templateElement.cloneNode(false);
26740                   if (!current) {
26741                     // There are no more elements so just append it to the select
26742                     parent.appendChild(element);
26743                   } else {
26744                     // The next element is not a group so insert the new one
26745                     parent.insertBefore(element, current);
26746                   }
26747                 }
26748                 return element;
26749               }
26750
26751
26752               function removeExcessElements(current) {
26753                 var next;
26754                 while (current) {
26755                   next = current.nextSibling;
26756                   jqLiteRemove(current);
26757                   current = next;
26758                 }
26759               }
26760
26761
26762               function skipEmptyAndUnknownOptions(current) {
26763                 var emptyOption_ = emptyOption && emptyOption[0];
26764                 var unknownOption_ = unknownOption && unknownOption[0];
26765
26766                 // We cannot rely on the extracted empty option being the same as the compiled empty option,
26767                 // because the compiled empty option might have been replaced by a comment because
26768                 // it had an "element" transclusion directive on it (such as ngIf)
26769                 if (emptyOption_ || unknownOption_) {
26770                   while (current &&
26771                         (current === emptyOption_ ||
26772                         current === unknownOption_ ||
26773                         current.nodeType === NODE_TYPE_COMMENT ||
26774                         current.value === '')) {
26775                     current = current.nextSibling;
26776                   }
26777                 }
26778                 return current;
26779               }
26780
26781
26782               function updateOptions() {
26783
26784                 var previousValue = options && selectCtrl.readValue();
26785
26786                 options = ngOptions.getOptions();
26787
26788                 var groupMap = {};
26789                 var currentElement = selectElement[0].firstChild;
26790
26791                 // Ensure that the empty option is always there if it was explicitly provided
26792                 if (providedEmptyOption) {
26793                   selectElement.prepend(emptyOption);
26794                 }
26795
26796                 currentElement = skipEmptyAndUnknownOptions(currentElement);
26797
26798                 options.items.forEach(function updateOption(option) {
26799                   var group;
26800                   var groupElement;
26801                   var optionElement;
26802
26803                   if (option.group) {
26804
26805                     // This option is to live in a group
26806                     // See if we have already created this group
26807                     group = groupMap[option.group];
26808
26809                     if (!group) {
26810
26811                       // We have not already created this group
26812                       groupElement = addOrReuseElement(selectElement[0],
26813                                                        currentElement,
26814                                                        'optgroup',
26815                                                        optGroupTemplate);
26816                       // Move to the next element
26817                       currentElement = groupElement.nextSibling;
26818
26819                       // Update the label on the group element
26820                       groupElement.label = option.group;
26821
26822                       // Store it for use later
26823                       group = groupMap[option.group] = {
26824                         groupElement: groupElement,
26825                         currentOptionElement: groupElement.firstChild
26826                       };
26827
26828                     }
26829
26830                     // So now we have a group for this option we add the option to the group
26831                     optionElement = addOrReuseElement(group.groupElement,
26832                                                       group.currentOptionElement,
26833                                                       'option',
26834                                                       optionTemplate);
26835                     updateOptionElement(option, optionElement);
26836                     // Move to the next element
26837                     group.currentOptionElement = optionElement.nextSibling;
26838
26839                   } else {
26840
26841                     // This option is not in a group
26842                     optionElement = addOrReuseElement(selectElement[0],
26843                                                       currentElement,
26844                                                       'option',
26845                                                       optionTemplate);
26846                     updateOptionElement(option, optionElement);
26847                     // Move to the next element
26848                     currentElement = optionElement.nextSibling;
26849                   }
26850                 });
26851
26852
26853                 // Now remove all excess options and group
26854                 Object.keys(groupMap).forEach(function(key) {
26855                   removeExcessElements(groupMap[key].currentOptionElement);
26856                 });
26857                 removeExcessElements(currentElement);
26858
26859                 ngModelCtrl.$render();
26860
26861                 // Check to see if the value has changed due to the update to the options
26862                 if (!ngModelCtrl.$isEmpty(previousValue)) {
26863                   var nextValue = selectCtrl.readValue();
26864                   if (ngOptions.trackBy ? !equals(previousValue, nextValue) : previousValue !== nextValue) {
26865                     ngModelCtrl.$setViewValue(nextValue);
26866                     ngModelCtrl.$render();
26867                   }
26868                 }
26869
26870               }
26871           }
26872
26873           return {
26874             restrict: 'A',
26875             terminal: true,
26876             require: ['select', '?ngModel'],
26877             link: {
26878               pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
26879                 // Deactivate the SelectController.register method to prevent
26880                 // option directives from accidentally registering themselves
26881                 // (and unwanted $destroy handlers etc.)
26882                 ctrls[0].registerOption = noop;
26883               },
26884               post: ngOptionsPostLink
26885             }
26886           };
26887         }];
26888
26889         /**
26890          * @ngdoc directive
26891          * @name ngPluralize
26892          * @restrict EA
26893          *
26894          * @description
26895          * `ngPluralize` is a directive that displays messages according to en-US localization rules.
26896          * These rules are bundled with angular.js, but can be overridden
26897          * (see {@link guide/i18n Angular i18n} dev guide). You configure ngPluralize directive
26898          * by specifying the mappings between
26899          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26900          * and the strings to be displayed.
26901          *
26902          * # Plural categories and explicit number rules
26903          * There are two
26904          * [plural categories](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
26905          * in Angular's default en-US locale: "one" and "other".
26906          *
26907          * While a plural category may match many numbers (for example, in en-US locale, "other" can match
26908          * any number that is not 1), an explicit number rule can only match one number. For example, the
26909          * explicit number rule for "3" matches the number 3. There are examples of plural categories
26910          * and explicit number rules throughout the rest of this documentation.
26911          *
26912          * # Configuring ngPluralize
26913          * You configure ngPluralize by providing 2 attributes: `count` and `when`.
26914          * You can also provide an optional attribute, `offset`.
26915          *
26916          * The value of the `count` attribute can be either a string or an {@link guide/expression
26917          * Angular expression}; these are evaluated on the current scope for its bound value.
26918          *
26919          * The `when` attribute specifies the mappings between plural categories and the actual
26920          * string to be displayed. The value of the attribute should be a JSON object.
26921          *
26922          * The following example shows how to configure ngPluralize:
26923          *
26924          * ```html
26925          * <ng-pluralize count="personCount"
26926                          when="{'0': 'Nobody is viewing.',
26927          *                      'one': '1 person is viewing.',
26928          *                      'other': '{} people are viewing.'}">
26929          * </ng-pluralize>
26930          *```
26931          *
26932          * In the example, `"0: Nobody is viewing."` is an explicit number rule. If you did not
26933          * specify this rule, 0 would be matched to the "other" category and "0 people are viewing"
26934          * would be shown instead of "Nobody is viewing". You can specify an explicit number rule for
26935          * other numbers, for example 12, so that instead of showing "12 people are viewing", you can
26936          * show "a dozen people are viewing".
26937          *
26938          * You can use a set of closed braces (`{}`) as a placeholder for the number that you want substituted
26939          * into pluralized strings. In the previous example, Angular will replace `{}` with
26940          * <span ng-non-bindable>`{{personCount}}`</span>. The closed braces `{}` is a placeholder
26941          * for <span ng-non-bindable>{{numberExpression}}</span>.
26942          *
26943          * If no rule is defined for a category, then an empty string is displayed and a warning is generated.
26944          * Note that some locales define more categories than `one` and `other`. For example, fr-fr defines `few` and `many`.
26945          *
26946          * # Configuring ngPluralize with offset
26947          * The `offset` attribute allows further customization of pluralized text, which can result in
26948          * a better user experience. For example, instead of the message "4 people are viewing this document",
26949          * you might display "John, Kate and 2 others are viewing this document".
26950          * The offset attribute allows you to offset a number by any desired value.
26951          * Let's take a look at an example:
26952          *
26953          * ```html
26954          * <ng-pluralize count="personCount" offset=2
26955          *               when="{'0': 'Nobody is viewing.',
26956          *                      '1': '{{person1}} is viewing.',
26957          *                      '2': '{{person1}} and {{person2}} are viewing.',
26958          *                      'one': '{{person1}}, {{person2}} and one other person are viewing.',
26959          *                      'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
26960          * </ng-pluralize>
26961          * ```
26962          *
26963          * Notice that we are still using two plural categories(one, other), but we added
26964          * three explicit number rules 0, 1 and 2.
26965          * When one person, perhaps John, views the document, "John is viewing" will be shown.
26966          * When three people view the document, no explicit number rule is found, so
26967          * an offset of 2 is taken off 3, and Angular uses 1 to decide the plural category.
26968          * In this case, plural category 'one' is matched and "John, Mary and one other person are viewing"
26969          * is shown.
26970          *
26971          * Note that when you specify offsets, you must provide explicit number rules for
26972          * numbers from 0 up to and including the offset. If you use an offset of 3, for example,
26973          * you must provide explicit number rules for 0, 1, 2 and 3. You must also provide plural strings for
26974          * plural categories "one" and "other".
26975          *
26976          * @param {string|expression} count The variable to be bound to.
26977          * @param {string} when The mapping between plural category to its corresponding strings.
26978          * @param {number=} offset Offset to deduct from the total number.
26979          *
26980          * @example
26981             <example module="pluralizeExample">
26982               <file name="index.html">
26983                 <script>
26984                   angular.module('pluralizeExample', [])
26985                     .controller('ExampleController', ['$scope', function($scope) {
26986                       $scope.person1 = 'Igor';
26987                       $scope.person2 = 'Misko';
26988                       $scope.personCount = 1;
26989                     }]);
26990                 </script>
26991                 <div ng-controller="ExampleController">
26992                   <label>Person 1:<input type="text" ng-model="person1" value="Igor" /></label><br/>
26993                   <label>Person 2:<input type="text" ng-model="person2" value="Misko" /></label><br/>
26994                   <label>Number of People:<input type="text" ng-model="personCount" value="1" /></label><br/>
26995
26996                   <!--- Example with simple pluralization rules for en locale --->
26997                   Without Offset:
26998                   <ng-pluralize count="personCount"
26999                                 when="{'0': 'Nobody is viewing.',
27000                                        'one': '1 person is viewing.',
27001                                        'other': '{} people are viewing.'}">
27002                   </ng-pluralize><br>
27003
27004                   <!--- Example with offset --->
27005                   With Offset(2):
27006                   <ng-pluralize count="personCount" offset=2
27007                                 when="{'0': 'Nobody is viewing.',
27008                                        '1': '{{person1}} is viewing.',
27009                                        '2': '{{person1}} and {{person2}} are viewing.',
27010                                        'one': '{{person1}}, {{person2}} and one other person are viewing.',
27011                                        'other': '{{person1}}, {{person2}} and {} other people are viewing.'}">
27012                   </ng-pluralize>
27013                 </div>
27014               </file>
27015               <file name="protractor.js" type="protractor">
27016                 it('should show correct pluralized string', function() {
27017                   var withoutOffset = element.all(by.css('ng-pluralize')).get(0);
27018                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27019                   var countInput = element(by.model('personCount'));
27020
27021                   expect(withoutOffset.getText()).toEqual('1 person is viewing.');
27022                   expect(withOffset.getText()).toEqual('Igor is viewing.');
27023
27024                   countInput.clear();
27025                   countInput.sendKeys('0');
27026
27027                   expect(withoutOffset.getText()).toEqual('Nobody is viewing.');
27028                   expect(withOffset.getText()).toEqual('Nobody is viewing.');
27029
27030                   countInput.clear();
27031                   countInput.sendKeys('2');
27032
27033                   expect(withoutOffset.getText()).toEqual('2 people are viewing.');
27034                   expect(withOffset.getText()).toEqual('Igor and Misko are viewing.');
27035
27036                   countInput.clear();
27037                   countInput.sendKeys('3');
27038
27039                   expect(withoutOffset.getText()).toEqual('3 people are viewing.');
27040                   expect(withOffset.getText()).toEqual('Igor, Misko and one other person are viewing.');
27041
27042                   countInput.clear();
27043                   countInput.sendKeys('4');
27044
27045                   expect(withoutOffset.getText()).toEqual('4 people are viewing.');
27046                   expect(withOffset.getText()).toEqual('Igor, Misko and 2 other people are viewing.');
27047                 });
27048                 it('should show data-bound names', function() {
27049                   var withOffset = element.all(by.css('ng-pluralize')).get(1);
27050                   var personCount = element(by.model('personCount'));
27051                   var person1 = element(by.model('person1'));
27052                   var person2 = element(by.model('person2'));
27053                   personCount.clear();
27054                   personCount.sendKeys('4');
27055                   person1.clear();
27056                   person1.sendKeys('Di');
27057                   person2.clear();
27058                   person2.sendKeys('Vojta');
27059                   expect(withOffset.getText()).toEqual('Di, Vojta and 2 other people are viewing.');
27060                 });
27061               </file>
27062             </example>
27063          */
27064         var ngPluralizeDirective = ['$locale', '$interpolate', '$log', function($locale, $interpolate, $log) {
27065           var BRACE = /{}/g,
27066               IS_WHEN = /^when(Minus)?(.+)$/;
27067
27068           return {
27069             link: function(scope, element, attr) {
27070               var numberExp = attr.count,
27071                   whenExp = attr.$attr.when && element.attr(attr.$attr.when), // we have {{}} in attrs
27072                   offset = attr.offset || 0,
27073                   whens = scope.$eval(whenExp) || {},
27074                   whensExpFns = {},
27075                   startSymbol = $interpolate.startSymbol(),
27076                   endSymbol = $interpolate.endSymbol(),
27077                   braceReplacement = startSymbol + numberExp + '-' + offset + endSymbol,
27078                   watchRemover = angular.noop,
27079                   lastCount;
27080
27081               forEach(attr, function(expression, attributeName) {
27082                 var tmpMatch = IS_WHEN.exec(attributeName);
27083                 if (tmpMatch) {
27084                   var whenKey = (tmpMatch[1] ? '-' : '') + lowercase(tmpMatch[2]);
27085                   whens[whenKey] = element.attr(attr.$attr[attributeName]);
27086                 }
27087               });
27088               forEach(whens, function(expression, key) {
27089                 whensExpFns[key] = $interpolate(expression.replace(BRACE, braceReplacement));
27090
27091               });
27092
27093               scope.$watch(numberExp, function ngPluralizeWatchAction(newVal) {
27094                 var count = parseFloat(newVal);
27095                 var countIsNaN = isNaN(count);
27096
27097                 if (!countIsNaN && !(count in whens)) {
27098                   // If an explicit number rule such as 1, 2, 3... is defined, just use it.
27099                   // Otherwise, check it against pluralization rules in $locale service.
27100                   count = $locale.pluralCat(count - offset);
27101                 }
27102
27103                 // If both `count` and `lastCount` are NaN, we don't need to re-register a watch.
27104                 // In JS `NaN !== NaN`, so we have to exlicitly check.
27105                 if ((count !== lastCount) && !(countIsNaN && isNumber(lastCount) && isNaN(lastCount))) {
27106                   watchRemover();
27107                   var whenExpFn = whensExpFns[count];
27108                   if (isUndefined(whenExpFn)) {
27109                     if (newVal != null) {
27110                       $log.debug("ngPluralize: no rule defined for '" + count + "' in " + whenExp);
27111                     }
27112                     watchRemover = noop;
27113                     updateElementText();
27114                   } else {
27115                     watchRemover = scope.$watch(whenExpFn, updateElementText);
27116                   }
27117                   lastCount = count;
27118                 }
27119               });
27120
27121               function updateElementText(newText) {
27122                 element.text(newText || '');
27123               }
27124             }
27125           };
27126         }];
27127
27128         /**
27129          * @ngdoc directive
27130          * @name ngRepeat
27131          * @multiElement
27132          *
27133          * @description
27134          * The `ngRepeat` directive instantiates a template once per item from a collection. Each template
27135          * instance gets its own scope, where the given loop variable is set to the current collection item,
27136          * and `$index` is set to the item index or key.
27137          *
27138          * Special properties are exposed on the local scope of each template instance, including:
27139          *
27140          * | Variable  | Type            | Details                                                                     |
27141          * |-----------|-----------------|-----------------------------------------------------------------------------|
27142          * | `$index`  | {@type number}  | iterator offset of the repeated element (0..length-1)                       |
27143          * | `$first`  | {@type boolean} | true if the repeated element is first in the iterator.                      |
27144          * | `$middle` | {@type boolean} | true if the repeated element is between the first and last in the iterator. |
27145          * | `$last`   | {@type boolean} | true if the repeated element is last in the iterator.                       |
27146          * | `$even`   | {@type boolean} | true if the iterator position `$index` is even (otherwise false).           |
27147          * | `$odd`    | {@type boolean} | true if the iterator position `$index` is odd (otherwise false).            |
27148          *
27149          * <div class="alert alert-info">
27150          *   Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
27151          *   This may be useful when, for instance, nesting ngRepeats.
27152          * </div>
27153          *
27154          *
27155          * # Iterating over object properties
27156          *
27157          * It is possible to get `ngRepeat` to iterate over the properties of an object using the following
27158          * syntax:
27159          *
27160          * ```js
27161          * <div ng-repeat="(key, value) in myObj"> ... </div>
27162          * ```
27163          *
27164          * You need to be aware that the JavaScript specification does not define the order of keys
27165          * returned for an object. (To mitigate this in Angular 1.3 the `ngRepeat` directive
27166          * used to sort the keys alphabetically.)
27167          *
27168          * Version 1.4 removed the alphabetic sorting. We now rely on the order returned by the browser
27169          * when running `for key in myObj`. It seems that browsers generally follow the strategy of providing
27170          * keys in the order in which they were defined, although there are exceptions when keys are deleted
27171          * and reinstated. See the [MDN page on `delete` for more info](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/delete#Cross-browser_notes).
27172          *
27173          * If this is not desired, the recommended workaround is to convert your object into an array
27174          * that is sorted into the order that you prefer before providing it to `ngRepeat`.  You could
27175          * do this with a filter such as [toArrayFilter](http://ngmodules.org/modules/angular-toArrayFilter)
27176          * or implement a `$watch` on the object yourself.
27177          *
27178          *
27179          * # Tracking and Duplicates
27180          *
27181          * `ngRepeat` uses {@link $rootScope.Scope#$watchCollection $watchCollection} to detect changes in
27182          * the collection. When a change happens, ngRepeat then makes the corresponding changes to the DOM:
27183          *
27184          * * When an item is added, a new instance of the template is added to the DOM.
27185          * * When an item is removed, its template instance is removed from the DOM.
27186          * * When items are reordered, their respective templates are reordered in the DOM.
27187          *
27188          * To minimize creation of DOM elements, `ngRepeat` uses a function
27189          * to "keep track" of all items in the collection and their corresponding DOM elements.
27190          * For example, if an item is added to the collection, ngRepeat will know that all other items
27191          * already have DOM elements, and will not re-render them.
27192          *
27193          * The default tracking function (which tracks items by their identity) does not allow
27194          * duplicate items in arrays. This is because when there are duplicates, it is not possible
27195          * to maintain a one-to-one mapping between collection items and DOM elements.
27196          *
27197          * If you do need to repeat duplicate items, you can substitute the default tracking behavior
27198          * with your own using the `track by` expression.
27199          *
27200          * For example, you may track items by the index of each item in the collection, using the
27201          * special scope property `$index`:
27202          * ```html
27203          *    <div ng-repeat="n in [42, 42, 43, 43] track by $index">
27204          *      {{n}}
27205          *    </div>
27206          * ```
27207          *
27208          * You may also use arbitrary expressions in `track by`, including references to custom functions
27209          * on the scope:
27210          * ```html
27211          *    <div ng-repeat="n in [42, 42, 43, 43] track by myTrackingFunction(n)">
27212          *      {{n}}
27213          *    </div>
27214          * ```
27215          *
27216          * <div class="alert alert-success">
27217          * If you are working with objects that have an identifier property, you should track
27218          * by the identifier instead of the whole object. Should you reload your data later, `ngRepeat`
27219          * will not have to rebuild the DOM elements for items it has already rendered, even if the
27220          * JavaScript objects in the collection have been substituted for new ones. For large collections,
27221          * this signifincantly improves rendering performance. If you don't have a unique identifier,
27222          * `track by $index` can also provide a performance boost.
27223          * </div>
27224          * ```html
27225          *    <div ng-repeat="model in collection track by model.id">
27226          *      {{model.name}}
27227          *    </div>
27228          * ```
27229          *
27230          * When no `track by` expression is provided, it is equivalent to tracking by the built-in
27231          * `$id` function, which tracks items by their identity:
27232          * ```html
27233          *    <div ng-repeat="obj in collection track by $id(obj)">
27234          *      {{obj.prop}}
27235          *    </div>
27236          * ```
27237          *
27238          * <div class="alert alert-warning">
27239          * **Note:** `track by` must always be the last expression:
27240          * </div>
27241          * ```
27242          * <div ng-repeat="model in collection | orderBy: 'id' as filtered_result track by model.id">
27243          *     {{model.name}}
27244          * </div>
27245          * ```
27246          *
27247          * # Special repeat start and end points
27248          * To repeat a series of elements instead of just one parent element, ngRepeat (as well as other ng directives) supports extending
27249          * the range of the repeater by defining explicit start and end points by using **ng-repeat-start** and **ng-repeat-end** respectively.
27250          * 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)
27251          * up to and including the ending HTML tag where **ng-repeat-end** is placed.
27252          *
27253          * The example below makes use of this feature:
27254          * ```html
27255          *   <header ng-repeat-start="item in items">
27256          *     Header {{ item }}
27257          *   </header>
27258          *   <div class="body">
27259          *     Body {{ item }}
27260          *   </div>
27261          *   <footer ng-repeat-end>
27262          *     Footer {{ item }}
27263          *   </footer>
27264          * ```
27265          *
27266          * And with an input of {@type ['A','B']} for the items variable in the example above, the output will evaluate to:
27267          * ```html
27268          *   <header>
27269          *     Header A
27270          *   </header>
27271          *   <div class="body">
27272          *     Body A
27273          *   </div>
27274          *   <footer>
27275          *     Footer A
27276          *   </footer>
27277          *   <header>
27278          *     Header B
27279          *   </header>
27280          *   <div class="body">
27281          *     Body B
27282          *   </div>
27283          *   <footer>
27284          *     Footer B
27285          *   </footer>
27286          * ```
27287          *
27288          * The custom start and end points for ngRepeat also support all other HTML directive syntax flavors provided in AngularJS (such
27289          * as **data-ng-repeat-start**, **x-ng-repeat-start** and **ng:repeat-start**).
27290          *
27291          * @animations
27292          * **.enter** - when a new item is added to the list or when an item is revealed after a filter
27293          *
27294          * **.leave** - when an item is removed from the list or when an item is filtered out
27295          *
27296          * **.move** - when an adjacent item is filtered out causing a reorder or when the item contents are reordered
27297          *
27298          * @element ANY
27299          * @scope
27300          * @priority 1000
27301          * @param {repeat_expression} ngRepeat The expression indicating how to enumerate a collection. These
27302          *   formats are currently supported:
27303          *
27304          *   * `variable in expression` – where variable is the user defined loop variable and `expression`
27305          *     is a scope expression giving the collection to enumerate.
27306          *
27307          *     For example: `album in artist.albums`.
27308          *
27309          *   * `(key, value) in expression` – where `key` and `value` can be any user defined identifiers,
27310          *     and `expression` is the scope expression giving the collection to enumerate.
27311          *
27312          *     For example: `(name, age) in {'adam':10, 'amalie':12}`.
27313          *
27314          *   * `variable in expression track by tracking_expression` – You can also provide an optional tracking expression
27315          *     which can be used to associate the objects in the collection with the DOM elements. If no tracking expression
27316          *     is specified, ng-repeat associates elements by identity. It is an error to have
27317          *     more than one tracking expression value resolve to the same key. (This would mean that two distinct objects are
27318          *     mapped to the same DOM element, which is not possible.)
27319          *
27320          *     Note that the tracking expression must come last, after any filters, and the alias expression.
27321          *
27322          *     For example: `item in items` is equivalent to `item in items track by $id(item)`. This implies that the DOM elements
27323          *     will be associated by item identity in the array.
27324          *
27325          *     For example: `item in items track by $id(item)`. A built in `$id()` function can be used to assign a unique
27326          *     `$$hashKey` property to each item in the array. This property is then used as a key to associated DOM elements
27327          *     with the corresponding item in the array by identity. Moving the same object in array would move the DOM
27328          *     element in the same way in the DOM.
27329          *
27330          *     For example: `item in items track by item.id` is a typical pattern when the items come from the database. In this
27331          *     case the object identity does not matter. Two objects are considered equivalent as long as their `id`
27332          *     property is same.
27333          *
27334          *     For example: `item in items | filter:searchText track by item.id` is a pattern that might be used to apply a filter
27335          *     to items in conjunction with a tracking expression.
27336          *
27337          *   * `variable in expression as alias_expression` – You can also provide an optional alias expression which will then store the
27338          *     intermediate results of the repeater after the filters have been applied. Typically this is used to render a special message
27339          *     when a filter is active on the repeater, but the filtered result set is empty.
27340          *
27341          *     For example: `item in items | filter:x as results` will store the fragment of the repeated items as `results`, but only after
27342          *     the items have been processed through the filter.
27343          *
27344          *     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
27345          *     (and not as operator, inside an expression).
27346          *
27347          *     For example: `item in items | filter : x | orderBy : order | limitTo : limit as results` .
27348          *
27349          * @example
27350          * This example initializes the scope to a list of names and
27351          * then uses `ngRepeat` to display every person:
27352           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27353             <file name="index.html">
27354               <div ng-init="friends = [
27355                 {name:'John', age:25, gender:'boy'},
27356                 {name:'Jessie', age:30, gender:'girl'},
27357                 {name:'Johanna', age:28, gender:'girl'},
27358                 {name:'Joy', age:15, gender:'girl'},
27359                 {name:'Mary', age:28, gender:'girl'},
27360                 {name:'Peter', age:95, gender:'boy'},
27361                 {name:'Sebastian', age:50, gender:'boy'},
27362                 {name:'Erika', age:27, gender:'girl'},
27363                 {name:'Patrick', age:40, gender:'boy'},
27364                 {name:'Samantha', age:60, gender:'girl'}
27365               ]">
27366                 I have {{friends.length}} friends. They are:
27367                 <input type="search" ng-model="q" placeholder="filter friends..." aria-label="filter friends" />
27368                 <ul class="example-animate-container">
27369                   <li class="animate-repeat" ng-repeat="friend in friends | filter:q as results">
27370                     [{{$index + 1}}] {{friend.name}} who is {{friend.age}} years old.
27371                   </li>
27372                   <li class="animate-repeat" ng-if="results.length == 0">
27373                     <strong>No results found...</strong>
27374                   </li>
27375                 </ul>
27376               </div>
27377             </file>
27378             <file name="animations.css">
27379               .example-animate-container {
27380                 background:white;
27381                 border:1px solid black;
27382                 list-style:none;
27383                 margin:0;
27384                 padding:0 10px;
27385               }
27386
27387               .animate-repeat {
27388                 line-height:40px;
27389                 list-style:none;
27390                 box-sizing:border-box;
27391               }
27392
27393               .animate-repeat.ng-move,
27394               .animate-repeat.ng-enter,
27395               .animate-repeat.ng-leave {
27396                 transition:all linear 0.5s;
27397               }
27398
27399               .animate-repeat.ng-leave.ng-leave-active,
27400               .animate-repeat.ng-move,
27401               .animate-repeat.ng-enter {
27402                 opacity:0;
27403                 max-height:0;
27404               }
27405
27406               .animate-repeat.ng-leave,
27407               .animate-repeat.ng-move.ng-move-active,
27408               .animate-repeat.ng-enter.ng-enter-active {
27409                 opacity:1;
27410                 max-height:40px;
27411               }
27412             </file>
27413             <file name="protractor.js" type="protractor">
27414               var friends = element.all(by.repeater('friend in friends'));
27415
27416               it('should render initial data set', function() {
27417                 expect(friends.count()).toBe(10);
27418                 expect(friends.get(0).getText()).toEqual('[1] John who is 25 years old.');
27419                 expect(friends.get(1).getText()).toEqual('[2] Jessie who is 30 years old.');
27420                 expect(friends.last().getText()).toEqual('[10] Samantha who is 60 years old.');
27421                 expect(element(by.binding('friends.length')).getText())
27422                     .toMatch("I have 10 friends. They are:");
27423               });
27424
27425                it('should update repeater when filter predicate changes', function() {
27426                  expect(friends.count()).toBe(10);
27427
27428                  element(by.model('q')).sendKeys('ma');
27429
27430                  expect(friends.count()).toBe(2);
27431                  expect(friends.get(0).getText()).toEqual('[1] Mary who is 28 years old.');
27432                  expect(friends.last().getText()).toEqual('[2] Samantha who is 60 years old.');
27433                });
27434               </file>
27435             </example>
27436          */
27437         var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
27438           var NG_REMOVED = '$$NG_REMOVED';
27439           var ngRepeatMinErr = minErr('ngRepeat');
27440
27441           var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) {
27442             // TODO(perf): generate setters to shave off ~40ms or 1-1.5%
27443             scope[valueIdentifier] = value;
27444             if (keyIdentifier) scope[keyIdentifier] = key;
27445             scope.$index = index;
27446             scope.$first = (index === 0);
27447             scope.$last = (index === (arrayLength - 1));
27448             scope.$middle = !(scope.$first || scope.$last);
27449             // jshint bitwise: false
27450             scope.$odd = !(scope.$even = (index&1) === 0);
27451             // jshint bitwise: true
27452           };
27453
27454           var getBlockStart = function(block) {
27455             return block.clone[0];
27456           };
27457
27458           var getBlockEnd = function(block) {
27459             return block.clone[block.clone.length - 1];
27460           };
27461
27462
27463           return {
27464             restrict: 'A',
27465             multiElement: true,
27466             transclude: 'element',
27467             priority: 1000,
27468             terminal: true,
27469             $$tlb: true,
27470             compile: function ngRepeatCompile($element, $attr) {
27471               var expression = $attr.ngRepeat;
27472               var ngRepeatEndComment = document.createComment(' end ngRepeat: ' + expression + ' ');
27473
27474               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*$/);
27475
27476               if (!match) {
27477                 throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
27478                     expression);
27479               }
27480
27481               var lhs = match[1];
27482               var rhs = match[2];
27483               var aliasAs = match[3];
27484               var trackByExp = match[4];
27485
27486               match = lhs.match(/^(?:(\s*[\$\w]+)|\(\s*([\$\w]+)\s*,\s*([\$\w]+)\s*\))$/);
27487
27488               if (!match) {
27489                 throw ngRepeatMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
27490                     lhs);
27491               }
27492               var valueIdentifier = match[3] || match[1];
27493               var keyIdentifier = match[2];
27494
27495               if (aliasAs && (!/^[$a-zA-Z_][$a-zA-Z0-9_]*$/.test(aliasAs) ||
27496                   /^(null|undefined|this|\$index|\$first|\$middle|\$last|\$even|\$odd|\$parent|\$root|\$id)$/.test(aliasAs))) {
27497                 throw ngRepeatMinErr('badident', "alias '{0}' is invalid --- must be a valid JS identifier which is not a reserved name.",
27498                   aliasAs);
27499               }
27500
27501               var trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn;
27502               var hashFnLocals = {$id: hashKey};
27503
27504               if (trackByExp) {
27505                 trackByExpGetter = $parse(trackByExp);
27506               } else {
27507                 trackByIdArrayFn = function(key, value) {
27508                   return hashKey(value);
27509                 };
27510                 trackByIdObjFn = function(key) {
27511                   return key;
27512                 };
27513               }
27514
27515               return function ngRepeatLink($scope, $element, $attr, ctrl, $transclude) {
27516
27517                 if (trackByExpGetter) {
27518                   trackByIdExpFn = function(key, value, index) {
27519                     // assign key, value, and $index to the locals so that they can be used in hash functions
27520                     if (keyIdentifier) hashFnLocals[keyIdentifier] = key;
27521                     hashFnLocals[valueIdentifier] = value;
27522                     hashFnLocals.$index = index;
27523                     return trackByExpGetter($scope, hashFnLocals);
27524                   };
27525                 }
27526
27527                 // Store a list of elements from previous run. This is a hash where key is the item from the
27528                 // iterator, and the value is objects with following properties.
27529                 //   - scope: bound scope
27530                 //   - element: previous element.
27531                 //   - index: position
27532                 //
27533                 // We are using no-proto object so that we don't need to guard against inherited props via
27534                 // hasOwnProperty.
27535                 var lastBlockMap = createMap();
27536
27537                 //watch props
27538                 $scope.$watchCollection(rhs, function ngRepeatAction(collection) {
27539                   var index, length,
27540                       previousNode = $element[0],     // node that cloned nodes should be inserted after
27541                                                       // initialized to the comment node anchor
27542                       nextNode,
27543                       // Same as lastBlockMap but it has the current state. It will become the
27544                       // lastBlockMap on the next iteration.
27545                       nextBlockMap = createMap(),
27546                       collectionLength,
27547                       key, value, // key/value of iteration
27548                       trackById,
27549                       trackByIdFn,
27550                       collectionKeys,
27551                       block,       // last object information {scope, element, id}
27552                       nextBlockOrder,
27553                       elementsToRemove;
27554
27555                   if (aliasAs) {
27556                     $scope[aliasAs] = collection;
27557                   }
27558
27559                   if (isArrayLike(collection)) {
27560                     collectionKeys = collection;
27561                     trackByIdFn = trackByIdExpFn || trackByIdArrayFn;
27562                   } else {
27563                     trackByIdFn = trackByIdExpFn || trackByIdObjFn;
27564                     // if object, extract keys, in enumeration order, unsorted
27565                     collectionKeys = [];
27566                     for (var itemKey in collection) {
27567                       if (hasOwnProperty.call(collection, itemKey) && itemKey.charAt(0) !== '$') {
27568                         collectionKeys.push(itemKey);
27569                       }
27570                     }
27571                   }
27572
27573                   collectionLength = collectionKeys.length;
27574                   nextBlockOrder = new Array(collectionLength);
27575
27576                   // locate existing items
27577                   for (index = 0; index < collectionLength; index++) {
27578                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27579                     value = collection[key];
27580                     trackById = trackByIdFn(key, value, index);
27581                     if (lastBlockMap[trackById]) {
27582                       // found previously seen block
27583                       block = lastBlockMap[trackById];
27584                       delete lastBlockMap[trackById];
27585                       nextBlockMap[trackById] = block;
27586                       nextBlockOrder[index] = block;
27587                     } else if (nextBlockMap[trackById]) {
27588                       // if collision detected. restore lastBlockMap and throw an error
27589                       forEach(nextBlockOrder, function(block) {
27590                         if (block && block.scope) lastBlockMap[block.id] = block;
27591                       });
27592                       throw ngRepeatMinErr('dupes',
27593                           "Duplicates in a repeater are not allowed. Use 'track by' expression to specify unique keys. Repeater: {0}, Duplicate key: {1}, Duplicate value: {2}",
27594                           expression, trackById, value);
27595                     } else {
27596                       // new never before seen block
27597                       nextBlockOrder[index] = {id: trackById, scope: undefined, clone: undefined};
27598                       nextBlockMap[trackById] = true;
27599                     }
27600                   }
27601
27602                   // remove leftover items
27603                   for (var blockKey in lastBlockMap) {
27604                     block = lastBlockMap[blockKey];
27605                     elementsToRemove = getBlockNodes(block.clone);
27606                     $animate.leave(elementsToRemove);
27607                     if (elementsToRemove[0].parentNode) {
27608                       // if the element was not removed yet because of pending animation, mark it as deleted
27609                       // so that we can ignore it later
27610                       for (index = 0, length = elementsToRemove.length; index < length; index++) {
27611                         elementsToRemove[index][NG_REMOVED] = true;
27612                       }
27613                     }
27614                     block.scope.$destroy();
27615                   }
27616
27617                   // we are not using forEach for perf reasons (trying to avoid #call)
27618                   for (index = 0; index < collectionLength; index++) {
27619                     key = (collection === collectionKeys) ? index : collectionKeys[index];
27620                     value = collection[key];
27621                     block = nextBlockOrder[index];
27622
27623                     if (block.scope) {
27624                       // if we have already seen this object, then we need to reuse the
27625                       // associated scope/element
27626
27627                       nextNode = previousNode;
27628
27629                       // skip nodes that are already pending removal via leave animation
27630                       do {
27631                         nextNode = nextNode.nextSibling;
27632                       } while (nextNode && nextNode[NG_REMOVED]);
27633
27634                       if (getBlockStart(block) != nextNode) {
27635                         // existing item which got moved
27636                         $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode));
27637                       }
27638                       previousNode = getBlockEnd(block);
27639                       updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27640                     } else {
27641                       // new item which we don't know about
27642                       $transclude(function ngRepeatTransclude(clone, scope) {
27643                         block.scope = scope;
27644                         // http://jsperf.com/clone-vs-createcomment
27645                         var endNode = ngRepeatEndComment.cloneNode(false);
27646                         clone[clone.length++] = endNode;
27647
27648                         // TODO(perf): support naked previousNode in `enter` to avoid creation of jqLite wrapper?
27649                         $animate.enter(clone, null, jqLite(previousNode));
27650                         previousNode = endNode;
27651                         // Note: We only need the first/last node of the cloned nodes.
27652                         // However, we need to keep the reference to the jqlite wrapper as it might be changed later
27653                         // by a directive with templateUrl when its template arrives.
27654                         block.clone = clone;
27655                         nextBlockMap[block.id] = block;
27656                         updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength);
27657                       });
27658                     }
27659                   }
27660                   lastBlockMap = nextBlockMap;
27661                 });
27662               };
27663             }
27664           };
27665         }];
27666
27667         var NG_HIDE_CLASS = 'ng-hide';
27668         var NG_HIDE_IN_PROGRESS_CLASS = 'ng-hide-animate';
27669         /**
27670          * @ngdoc directive
27671          * @name ngShow
27672          * @multiElement
27673          *
27674          * @description
27675          * The `ngShow` directive shows or hides the given HTML element based on the expression
27676          * provided to the `ngShow` attribute. The element is shown or hidden by removing or adding
27677          * the `.ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27678          * in AngularJS and sets the display style to none (using an !important flag).
27679          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27680          *
27681          * ```html
27682          * <!-- when $scope.myValue is truthy (element is visible) -->
27683          * <div ng-show="myValue"></div>
27684          *
27685          * <!-- when $scope.myValue is falsy (element is hidden) -->
27686          * <div ng-show="myValue" class="ng-hide"></div>
27687          * ```
27688          *
27689          * When the `ngShow` expression evaluates to a falsy value then the `.ng-hide` CSS class is added to the class
27690          * attribute on the element causing it to become hidden. When truthy, the `.ng-hide` CSS class is removed
27691          * from the element causing the element not to appear hidden.
27692          *
27693          * ## Why is !important used?
27694          *
27695          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27696          * can be easily overridden by heavier selectors. For example, something as simple
27697          * as changing the display style on a HTML list item would make hidden elements appear visible.
27698          * This also becomes a bigger issue when dealing with CSS frameworks.
27699          *
27700          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27701          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27702          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27703          *
27704          * ### Overriding `.ng-hide`
27705          *
27706          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27707          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27708          * class CSS. Note that the selector that needs to be used is actually `.ng-hide:not(.ng-hide-animate)` to cope
27709          * with extra animation classes that can be added.
27710          *
27711          * ```css
27712          * .ng-hide:not(.ng-hide-animate) {
27713          *   /&#42; this is just another form of hiding an element &#42;/
27714          *   display: block!important;
27715          *   position: absolute;
27716          *   top: -9999px;
27717          *   left: -9999px;
27718          * }
27719          * ```
27720          *
27721          * By default you don't need to override in CSS anything and the animations will work around the display style.
27722          *
27723          * ## A note about animations with `ngShow`
27724          *
27725          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27726          * is true and false. This system works like the animation system present with ngClass except that
27727          * you must also include the !important flag to override the display property
27728          * so that you can perform an animation when the element is hidden during the time of the animation.
27729          *
27730          * ```css
27731          * //
27732          * //a working example can be found at the bottom of this page
27733          * //
27734          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27735          *   /&#42; this is required as of 1.3x to properly
27736          *      apply all styling in a show/hide animation &#42;/
27737          *   transition: 0s linear all;
27738          * }
27739          *
27740          * .my-element.ng-hide-add-active,
27741          * .my-element.ng-hide-remove-active {
27742          *   /&#42; the transition is defined in the active class &#42;/
27743          *   transition: 1s linear all;
27744          * }
27745          *
27746          * .my-element.ng-hide-add { ... }
27747          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27748          * .my-element.ng-hide-remove { ... }
27749          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27750          * ```
27751          *
27752          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27753          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27754          *
27755          * @animations
27756          * addClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a truthy value and the just before contents are set to visible
27757          * removeClass: `.ng-hide` - happens after the `ngShow` expression evaluates to a non truthy value and just before the contents are set to hidden
27758          *
27759          * @element ANY
27760          * @param {expression} ngShow If the {@link guide/expression expression} is truthy
27761          *     then the element is shown or hidden respectively.
27762          *
27763          * @example
27764           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27765             <file name="index.html">
27766               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngHide"><br/>
27767               <div>
27768                 Show:
27769                 <div class="check-element animate-show" ng-show="checked">
27770                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27771                 </div>
27772               </div>
27773               <div>
27774                 Hide:
27775                 <div class="check-element animate-show" ng-hide="checked">
27776                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27777                 </div>
27778               </div>
27779             </file>
27780             <file name="glyphicons.css">
27781               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27782             </file>
27783             <file name="animations.css">
27784               .animate-show {
27785                 line-height: 20px;
27786                 opacity: 1;
27787                 padding: 10px;
27788                 border: 1px solid black;
27789                 background: white;
27790               }
27791
27792               .animate-show.ng-hide-add, .animate-show.ng-hide-remove {
27793                 transition: all linear 0.5s;
27794               }
27795
27796               .animate-show.ng-hide {
27797                 line-height: 0;
27798                 opacity: 0;
27799                 padding: 0 10px;
27800               }
27801
27802               .check-element {
27803                 padding: 10px;
27804                 border: 1px solid black;
27805                 background: white;
27806               }
27807             </file>
27808             <file name="protractor.js" type="protractor">
27809               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27810               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27811
27812               it('should check ng-show / ng-hide', function() {
27813                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27814                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27815
27816                 element(by.model('checked')).click();
27817
27818                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27819                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27820               });
27821             </file>
27822           </example>
27823          */
27824         var ngShowDirective = ['$animate', function($animate) {
27825           return {
27826             restrict: 'A',
27827             multiElement: true,
27828             link: function(scope, element, attr) {
27829               scope.$watch(attr.ngShow, function ngShowWatchAction(value) {
27830                 // we're adding a temporary, animation-specific class for ng-hide since this way
27831                 // we can control when the element is actually displayed on screen without having
27832                 // to have a global/greedy CSS selector that breaks when other animations are run.
27833                 // Read: https://github.com/angular/angular.js/issues/9103#issuecomment-58335845
27834                 $animate[value ? 'removeClass' : 'addClass'](element, NG_HIDE_CLASS, {
27835                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27836                 });
27837               });
27838             }
27839           };
27840         }];
27841
27842
27843         /**
27844          * @ngdoc directive
27845          * @name ngHide
27846          * @multiElement
27847          *
27848          * @description
27849          * The `ngHide` directive shows or hides the given HTML element based on the expression
27850          * provided to the `ngHide` attribute. The element is shown or hidden by removing or adding
27851          * the `ng-hide` CSS class onto the element. The `.ng-hide` CSS class is predefined
27852          * in AngularJS and sets the display style to none (using an !important flag).
27853          * For CSP mode please add `angular-csp.css` to your html file (see {@link ng.directive:ngCsp ngCsp}).
27854          *
27855          * ```html
27856          * <!-- when $scope.myValue is truthy (element is hidden) -->
27857          * <div ng-hide="myValue" class="ng-hide"></div>
27858          *
27859          * <!-- when $scope.myValue is falsy (element is visible) -->
27860          * <div ng-hide="myValue"></div>
27861          * ```
27862          *
27863          * When the `ngHide` expression evaluates to a truthy value then the `.ng-hide` CSS class is added to the class
27864          * attribute on the element causing it to become hidden. When falsy, the `.ng-hide` CSS class is removed
27865          * from the element causing the element not to appear hidden.
27866          *
27867          * ## Why is !important used?
27868          *
27869          * You may be wondering why !important is used for the `.ng-hide` CSS class. This is because the `.ng-hide` selector
27870          * can be easily overridden by heavier selectors. For example, something as simple
27871          * as changing the display style on a HTML list item would make hidden elements appear visible.
27872          * This also becomes a bigger issue when dealing with CSS frameworks.
27873          *
27874          * By using !important, the show and hide behavior will work as expected despite any clash between CSS selector
27875          * specificity (when !important isn't used with any conflicting styles). If a developer chooses to override the
27876          * styling to change how to hide an element then it is just a matter of using !important in their own CSS code.
27877          *
27878          * ### Overriding `.ng-hide`
27879          *
27880          * By default, the `.ng-hide` class will style the element with `display: none!important`. If you wish to change
27881          * the hide behavior with ngShow/ngHide then this can be achieved by restating the styles for the `.ng-hide`
27882          * class in CSS:
27883          *
27884          * ```css
27885          * .ng-hide {
27886          *   /&#42; this is just another form of hiding an element &#42;/
27887          *   display: block!important;
27888          *   position: absolute;
27889          *   top: -9999px;
27890          *   left: -9999px;
27891          * }
27892          * ```
27893          *
27894          * By default you don't need to override in CSS anything and the animations will work around the display style.
27895          *
27896          * ## A note about animations with `ngHide`
27897          *
27898          * Animations in ngShow/ngHide work with the show and hide events that are triggered when the directive expression
27899          * is true and false. This system works like the animation system present with ngClass, except that the `.ng-hide`
27900          * CSS class is added and removed for you instead of your own CSS class.
27901          *
27902          * ```css
27903          * //
27904          * //a working example can be found at the bottom of this page
27905          * //
27906          * .my-element.ng-hide-add, .my-element.ng-hide-remove {
27907          *   transition: 0.5s linear all;
27908          * }
27909          *
27910          * .my-element.ng-hide-add { ... }
27911          * .my-element.ng-hide-add.ng-hide-add-active { ... }
27912          * .my-element.ng-hide-remove { ... }
27913          * .my-element.ng-hide-remove.ng-hide-remove-active { ... }
27914          * ```
27915          *
27916          * Keep in mind that, as of AngularJS version 1.3.0-beta.11, there is no need to change the display
27917          * property to block during animation states--ngAnimate will handle the style toggling automatically for you.
27918          *
27919          * @animations
27920          * removeClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a truthy value and just before the contents are set to hidden
27921          * addClass: `.ng-hide` - happens after the `ngHide` expression evaluates to a non truthy value and just before the contents are set to visible
27922          *
27923          * @element ANY
27924          * @param {expression} ngHide If the {@link guide/expression expression} is truthy then
27925          *     the element is shown or hidden respectively.
27926          *
27927          * @example
27928           <example module="ngAnimate" deps="angular-animate.js" animations="true">
27929             <file name="index.html">
27930               Click me: <input type="checkbox" ng-model="checked" aria-label="Toggle ngShow"><br/>
27931               <div>
27932                 Show:
27933                 <div class="check-element animate-hide" ng-show="checked">
27934                   <span class="glyphicon glyphicon-thumbs-up"></span> I show up when your checkbox is checked.
27935                 </div>
27936               </div>
27937               <div>
27938                 Hide:
27939                 <div class="check-element animate-hide" ng-hide="checked">
27940                   <span class="glyphicon glyphicon-thumbs-down"></span> I hide when your checkbox is checked.
27941                 </div>
27942               </div>
27943             </file>
27944             <file name="glyphicons.css">
27945               @import url(../../components/bootstrap-3.1.1/css/bootstrap.css);
27946             </file>
27947             <file name="animations.css">
27948               .animate-hide {
27949                 transition: all linear 0.5s;
27950                 line-height: 20px;
27951                 opacity: 1;
27952                 padding: 10px;
27953                 border: 1px solid black;
27954                 background: white;
27955               }
27956
27957               .animate-hide.ng-hide {
27958                 line-height: 0;
27959                 opacity: 0;
27960                 padding: 0 10px;
27961               }
27962
27963               .check-element {
27964                 padding: 10px;
27965                 border: 1px solid black;
27966                 background: white;
27967               }
27968             </file>
27969             <file name="protractor.js" type="protractor">
27970               var thumbsUp = element(by.css('span.glyphicon-thumbs-up'));
27971               var thumbsDown = element(by.css('span.glyphicon-thumbs-down'));
27972
27973               it('should check ng-show / ng-hide', function() {
27974                 expect(thumbsUp.isDisplayed()).toBeFalsy();
27975                 expect(thumbsDown.isDisplayed()).toBeTruthy();
27976
27977                 element(by.model('checked')).click();
27978
27979                 expect(thumbsUp.isDisplayed()).toBeTruthy();
27980                 expect(thumbsDown.isDisplayed()).toBeFalsy();
27981               });
27982             </file>
27983           </example>
27984          */
27985         var ngHideDirective = ['$animate', function($animate) {
27986           return {
27987             restrict: 'A',
27988             multiElement: true,
27989             link: function(scope, element, attr) {
27990               scope.$watch(attr.ngHide, function ngHideWatchAction(value) {
27991                 // The comment inside of the ngShowDirective explains why we add and
27992                 // remove a temporary class for the show/hide animation
27993                 $animate[value ? 'addClass' : 'removeClass'](element,NG_HIDE_CLASS, {
27994                   tempClasses: NG_HIDE_IN_PROGRESS_CLASS
27995                 });
27996               });
27997             }
27998           };
27999         }];
28000
28001         /**
28002          * @ngdoc directive
28003          * @name ngStyle
28004          * @restrict AC
28005          *
28006          * @description
28007          * The `ngStyle` directive allows you to set CSS style on an HTML element conditionally.
28008          *
28009          * @element ANY
28010          * @param {expression} ngStyle
28011          *
28012          * {@link guide/expression Expression} which evals to an
28013          * object whose keys are CSS style names and values are corresponding values for those CSS
28014          * keys.
28015          *
28016          * Since some CSS style names are not valid keys for an object, they must be quoted.
28017          * See the 'background-color' style in the example below.
28018          *
28019          * @example
28020            <example>
28021              <file name="index.html">
28022                 <input type="button" value="set color" ng-click="myStyle={color:'red'}">
28023                 <input type="button" value="set background" ng-click="myStyle={'background-color':'blue'}">
28024                 <input type="button" value="clear" ng-click="myStyle={}">
28025                 <br/>
28026                 <span ng-style="myStyle">Sample Text</span>
28027                 <pre>myStyle={{myStyle}}</pre>
28028              </file>
28029              <file name="style.css">
28030                span {
28031                  color: black;
28032                }
28033              </file>
28034              <file name="protractor.js" type="protractor">
28035                var colorSpan = element(by.css('span'));
28036
28037                it('should check ng-style', function() {
28038                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28039                  element(by.css('input[value=\'set color\']')).click();
28040                  expect(colorSpan.getCssValue('color')).toBe('rgba(255, 0, 0, 1)');
28041                  element(by.css('input[value=clear]')).click();
28042                  expect(colorSpan.getCssValue('color')).toBe('rgba(0, 0, 0, 1)');
28043                });
28044              </file>
28045            </example>
28046          */
28047         var ngStyleDirective = ngDirective(function(scope, element, attr) {
28048           scope.$watch(attr.ngStyle, function ngStyleWatchAction(newStyles, oldStyles) {
28049             if (oldStyles && (newStyles !== oldStyles)) {
28050               forEach(oldStyles, function(val, style) { element.css(style, '');});
28051             }
28052             if (newStyles) element.css(newStyles);
28053           }, true);
28054         });
28055
28056         /**
28057          * @ngdoc directive
28058          * @name ngSwitch
28059          * @restrict EA
28060          *
28061          * @description
28062          * The `ngSwitch` directive is used to conditionally swap DOM structure on your template based on a scope expression.
28063          * Elements within `ngSwitch` but without `ngSwitchWhen` or `ngSwitchDefault` directives will be preserved at the location
28064          * as specified in the template.
28065          *
28066          * The directive itself works similar to ngInclude, however, instead of downloading template code (or loading it
28067          * from the template cache), `ngSwitch` simply chooses one of the nested elements and makes it visible based on which element
28068          * matches the value obtained from the evaluated expression. In other words, you define a container element
28069          * (where you place the directive), place an expression on the **`on="..."` attribute**
28070          * (or the **`ng-switch="..."` attribute**), define any inner elements inside of the directive and place
28071          * a when attribute per element. The when attribute is used to inform ngSwitch which element to display when the on
28072          * expression is evaluated. If a matching expression is not found via a when attribute then an element with the default
28073          * attribute is displayed.
28074          *
28075          * <div class="alert alert-info">
28076          * Be aware that the attribute values to match against cannot be expressions. They are interpreted
28077          * as literal string values to match against.
28078          * For example, **`ng-switch-when="someVal"`** will match against the string `"someVal"` not against the
28079          * value of the expression `$scope.someVal`.
28080          * </div>
28081
28082          * @animations
28083          * enter - happens after the ngSwitch contents change and the matched child element is placed inside the container
28084          * leave - happens just after the ngSwitch contents change and just before the former contents are removed from the DOM
28085          *
28086          * @usage
28087          *
28088          * ```
28089          * <ANY ng-switch="expression">
28090          *   <ANY ng-switch-when="matchValue1">...</ANY>
28091          *   <ANY ng-switch-when="matchValue2">...</ANY>
28092          *   <ANY ng-switch-default>...</ANY>
28093          * </ANY>
28094          * ```
28095          *
28096          *
28097          * @scope
28098          * @priority 1200
28099          * @param {*} ngSwitch|on expression to match against <code>ng-switch-when</code>.
28100          * On child elements add:
28101          *
28102          * * `ngSwitchWhen`: the case statement to match against. If match then this
28103          *   case will be displayed. If the same match appears multiple times, all the
28104          *   elements will be displayed.
28105          * * `ngSwitchDefault`: the default case when no other case match. If there
28106          *   are multiple default cases, all of them will be displayed when no other
28107          *   case match.
28108          *
28109          *
28110          * @example
28111           <example module="switchExample" deps="angular-animate.js" animations="true">
28112             <file name="index.html">
28113               <div ng-controller="ExampleController">
28114                 <select ng-model="selection" ng-options="item for item in items">
28115                 </select>
28116                 <code>selection={{selection}}</code>
28117                 <hr/>
28118                 <div class="animate-switch-container"
28119                   ng-switch on="selection">
28120                     <div class="animate-switch" ng-switch-when="settings">Settings Div</div>
28121                     <div class="animate-switch" ng-switch-when="home">Home Span</div>
28122                     <div class="animate-switch" ng-switch-default>default</div>
28123                 </div>
28124               </div>
28125             </file>
28126             <file name="script.js">
28127               angular.module('switchExample', ['ngAnimate'])
28128                 .controller('ExampleController', ['$scope', function($scope) {
28129                   $scope.items = ['settings', 'home', 'other'];
28130                   $scope.selection = $scope.items[0];
28131                 }]);
28132             </file>
28133             <file name="animations.css">
28134               .animate-switch-container {
28135                 position:relative;
28136                 background:white;
28137                 border:1px solid black;
28138                 height:40px;
28139                 overflow:hidden;
28140               }
28141
28142               .animate-switch {
28143                 padding:10px;
28144               }
28145
28146               .animate-switch.ng-animate {
28147                 transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 0.5s;
28148
28149                 position:absolute;
28150                 top:0;
28151                 left:0;
28152                 right:0;
28153                 bottom:0;
28154               }
28155
28156               .animate-switch.ng-leave.ng-leave-active,
28157               .animate-switch.ng-enter {
28158                 top:-50px;
28159               }
28160               .animate-switch.ng-leave,
28161               .animate-switch.ng-enter.ng-enter-active {
28162                 top:0;
28163               }
28164             </file>
28165             <file name="protractor.js" type="protractor">
28166               var switchElem = element(by.css('[ng-switch]'));
28167               var select = element(by.model('selection'));
28168
28169               it('should start in settings', function() {
28170                 expect(switchElem.getText()).toMatch(/Settings Div/);
28171               });
28172               it('should change to home', function() {
28173                 select.all(by.css('option')).get(1).click();
28174                 expect(switchElem.getText()).toMatch(/Home Span/);
28175               });
28176               it('should select default', function() {
28177                 select.all(by.css('option')).get(2).click();
28178                 expect(switchElem.getText()).toMatch(/default/);
28179               });
28180             </file>
28181           </example>
28182          */
28183         var ngSwitchDirective = ['$animate', function($animate) {
28184           return {
28185             require: 'ngSwitch',
28186
28187             // asks for $scope to fool the BC controller module
28188             controller: ['$scope', function ngSwitchController() {
28189              this.cases = {};
28190             }],
28191             link: function(scope, element, attr, ngSwitchController) {
28192               var watchExpr = attr.ngSwitch || attr.on,
28193                   selectedTranscludes = [],
28194                   selectedElements = [],
28195                   previousLeaveAnimations = [],
28196                   selectedScopes = [];
28197
28198               var spliceFactory = function(array, index) {
28199                   return function() { array.splice(index, 1); };
28200               };
28201
28202               scope.$watch(watchExpr, function ngSwitchWatchAction(value) {
28203                 var i, ii;
28204                 for (i = 0, ii = previousLeaveAnimations.length; i < ii; ++i) {
28205                   $animate.cancel(previousLeaveAnimations[i]);
28206                 }
28207                 previousLeaveAnimations.length = 0;
28208
28209                 for (i = 0, ii = selectedScopes.length; i < ii; ++i) {
28210                   var selected = getBlockNodes(selectedElements[i].clone);
28211                   selectedScopes[i].$destroy();
28212                   var promise = previousLeaveAnimations[i] = $animate.leave(selected);
28213                   promise.then(spliceFactory(previousLeaveAnimations, i));
28214                 }
28215
28216                 selectedElements.length = 0;
28217                 selectedScopes.length = 0;
28218
28219                 if ((selectedTranscludes = ngSwitchController.cases['!' + value] || ngSwitchController.cases['?'])) {
28220                   forEach(selectedTranscludes, function(selectedTransclude) {
28221                     selectedTransclude.transclude(function(caseElement, selectedScope) {
28222                       selectedScopes.push(selectedScope);
28223                       var anchor = selectedTransclude.element;
28224                       caseElement[caseElement.length++] = document.createComment(' end ngSwitchWhen: ');
28225                       var block = { clone: caseElement };
28226
28227                       selectedElements.push(block);
28228                       $animate.enter(caseElement, anchor.parent(), anchor);
28229                     });
28230                   });
28231                 }
28232               });
28233             }
28234           };
28235         }];
28236
28237         var ngSwitchWhenDirective = ngDirective({
28238           transclude: 'element',
28239           priority: 1200,
28240           require: '^ngSwitch',
28241           multiElement: true,
28242           link: function(scope, element, attrs, ctrl, $transclude) {
28243             ctrl.cases['!' + attrs.ngSwitchWhen] = (ctrl.cases['!' + attrs.ngSwitchWhen] || []);
28244             ctrl.cases['!' + attrs.ngSwitchWhen].push({ transclude: $transclude, element: element });
28245           }
28246         });
28247
28248         var ngSwitchDefaultDirective = ngDirective({
28249           transclude: 'element',
28250           priority: 1200,
28251           require: '^ngSwitch',
28252           multiElement: true,
28253           link: function(scope, element, attr, ctrl, $transclude) {
28254             ctrl.cases['?'] = (ctrl.cases['?'] || []);
28255             ctrl.cases['?'].push({ transclude: $transclude, element: element });
28256            }
28257         });
28258
28259         /**
28260          * @ngdoc directive
28261          * @name ngTransclude
28262          * @restrict EAC
28263          *
28264          * @description
28265          * Directive that marks the insertion point for the transcluded DOM of the nearest parent directive that uses transclusion.
28266          *
28267          * Any existing content of the element that this directive is placed on will be removed before the transcluded content is inserted.
28268          *
28269          * @element ANY
28270          *
28271          * @example
28272            <example module="transcludeExample">
28273              <file name="index.html">
28274                <script>
28275                  angular.module('transcludeExample', [])
28276                   .directive('pane', function(){
28277                      return {
28278                        restrict: 'E',
28279                        transclude: true,
28280                        scope: { title:'@' },
28281                        template: '<div style="border: 1px solid black;">' +
28282                                    '<div style="background-color: gray">{{title}}</div>' +
28283                                    '<ng-transclude></ng-transclude>' +
28284                                  '</div>'
28285                      };
28286                  })
28287                  .controller('ExampleController', ['$scope', function($scope) {
28288                    $scope.title = 'Lorem Ipsum';
28289                    $scope.text = 'Neque porro quisquam est qui dolorem ipsum quia dolor...';
28290                  }]);
28291                </script>
28292                <div ng-controller="ExampleController">
28293                  <input ng-model="title" aria-label="title"> <br/>
28294                  <textarea ng-model="text" aria-label="text"></textarea> <br/>
28295                  <pane title="{{title}}">{{text}}</pane>
28296                </div>
28297              </file>
28298              <file name="protractor.js" type="protractor">
28299                 it('should have transcluded', function() {
28300                   var titleElement = element(by.model('title'));
28301                   titleElement.clear();
28302                   titleElement.sendKeys('TITLE');
28303                   var textElement = element(by.model('text'));
28304                   textElement.clear();
28305                   textElement.sendKeys('TEXT');
28306                   expect(element(by.binding('title')).getText()).toEqual('TITLE');
28307                   expect(element(by.binding('text')).getText()).toEqual('TEXT');
28308                 });
28309              </file>
28310            </example>
28311          *
28312          */
28313         var ngTranscludeDirective = ngDirective({
28314           restrict: 'EAC',
28315           link: function($scope, $element, $attrs, controller, $transclude) {
28316             if (!$transclude) {
28317               throw minErr('ngTransclude')('orphan',
28318                'Illegal use of ngTransclude directive in the template! ' +
28319                'No parent directive that requires a transclusion found. ' +
28320                'Element: {0}',
28321                startingTag($element));
28322             }
28323
28324             $transclude(function(clone) {
28325               $element.empty();
28326               $element.append(clone);
28327             });
28328           }
28329         });
28330
28331         /**
28332          * @ngdoc directive
28333          * @name script
28334          * @restrict E
28335          *
28336          * @description
28337          * Load the content of a `<script>` element into {@link ng.$templateCache `$templateCache`}, so that the
28338          * template can be used by {@link ng.directive:ngInclude `ngInclude`},
28339          * {@link ngRoute.directive:ngView `ngView`}, or {@link guide/directive directives}. The type of the
28340          * `<script>` element must be specified as `text/ng-template`, and a cache name for the template must be
28341          * assigned through the element's `id`, which can then be used as a directive's `templateUrl`.
28342          *
28343          * @param {string} type Must be set to `'text/ng-template'`.
28344          * @param {string} id Cache name of the template.
28345          *
28346          * @example
28347           <example>
28348             <file name="index.html">
28349               <script type="text/ng-template" id="/tpl.html">
28350                 Content of the template.
28351               </script>
28352
28353               <a ng-click="currentTpl='/tpl.html'" id="tpl-link">Load inlined template</a>
28354               <div id="tpl-content" ng-include src="currentTpl"></div>
28355             </file>
28356             <file name="protractor.js" type="protractor">
28357               it('should load template defined inside script tag', function() {
28358                 element(by.css('#tpl-link')).click();
28359                 expect(element(by.css('#tpl-content')).getText()).toMatch(/Content of the template/);
28360               });
28361             </file>
28362           </example>
28363          */
28364         var scriptDirective = ['$templateCache', function($templateCache) {
28365           return {
28366             restrict: 'E',
28367             terminal: true,
28368             compile: function(element, attr) {
28369               if (attr.type == 'text/ng-template') {
28370                 var templateUrl = attr.id,
28371                     text = element[0].text;
28372
28373                 $templateCache.put(templateUrl, text);
28374               }
28375             }
28376           };
28377         }];
28378
28379         var noopNgModelController = { $setViewValue: noop, $render: noop };
28380
28381         function chromeHack(optionElement) {
28382           // Workaround for https://code.google.com/p/chromium/issues/detail?id=381459
28383           // Adding an <option selected="selected"> element to a <select required="required"> should
28384           // automatically select the new element
28385           if (optionElement[0].hasAttribute('selected')) {
28386             optionElement[0].selected = true;
28387           }
28388         }
28389
28390         /**
28391          * @ngdoc type
28392          * @name  select.SelectController
28393          * @description
28394          * The controller for the `<select>` directive. This provides support for reading
28395          * and writing the selected value(s) of the control and also coordinates dynamically
28396          * added `<option>` elements, perhaps by an `ngRepeat` directive.
28397          */
28398         var SelectController =
28399                 ['$element', '$scope', '$attrs', function($element, $scope, $attrs) {
28400
28401           var self = this,
28402               optionsMap = new HashMap();
28403
28404           // If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
28405           self.ngModelCtrl = noopNgModelController;
28406
28407           // The "unknown" option is one that is prepended to the list if the viewValue
28408           // does not match any of the options. When it is rendered the value of the unknown
28409           // option is '? XXX ?' where XXX is the hashKey of the value that is not known.
28410           //
28411           // We can't just jqLite('<option>') since jqLite is not smart enough
28412           // to create it in <select> and IE barfs otherwise.
28413           self.unknownOption = jqLite(document.createElement('option'));
28414           self.renderUnknownOption = function(val) {
28415             var unknownVal = '? ' + hashKey(val) + ' ?';
28416             self.unknownOption.val(unknownVal);
28417             $element.prepend(self.unknownOption);
28418             $element.val(unknownVal);
28419           };
28420
28421           $scope.$on('$destroy', function() {
28422             // disable unknown option so that we don't do work when the whole select is being destroyed
28423             self.renderUnknownOption = noop;
28424           });
28425
28426           self.removeUnknownOption = function() {
28427             if (self.unknownOption.parent()) self.unknownOption.remove();
28428           };
28429
28430
28431           // Read the value of the select control, the implementation of this changes depending
28432           // upon whether the select can have multiple values and whether ngOptions is at work.
28433           self.readValue = function readSingleValue() {
28434             self.removeUnknownOption();
28435             return $element.val();
28436           };
28437
28438
28439           // Write the value to the select control, the implementation of this changes depending
28440           // upon whether the select can have multiple values and whether ngOptions is at work.
28441           self.writeValue = function writeSingleValue(value) {
28442             if (self.hasOption(value)) {
28443               self.removeUnknownOption();
28444               $element.val(value);
28445               if (value === '') self.emptyOption.prop('selected', true); // to make IE9 happy
28446             } else {
28447               if (value == null && self.emptyOption) {
28448                 self.removeUnknownOption();
28449                 $element.val('');
28450               } else {
28451                 self.renderUnknownOption(value);
28452               }
28453             }
28454           };
28455
28456
28457           // Tell the select control that an option, with the given value, has been added
28458           self.addOption = function(value, element) {
28459             assertNotHasOwnProperty(value, '"option value"');
28460             if (value === '') {
28461               self.emptyOption = element;
28462             }
28463             var count = optionsMap.get(value) || 0;
28464             optionsMap.put(value, count + 1);
28465             self.ngModelCtrl.$render();
28466             chromeHack(element);
28467           };
28468
28469           // Tell the select control that an option, with the given value, has been removed
28470           self.removeOption = function(value) {
28471             var count = optionsMap.get(value);
28472             if (count) {
28473               if (count === 1) {
28474                 optionsMap.remove(value);
28475                 if (value === '') {
28476                   self.emptyOption = undefined;
28477                 }
28478               } else {
28479                 optionsMap.put(value, count - 1);
28480               }
28481             }
28482           };
28483
28484           // Check whether the select control has an option matching the given value
28485           self.hasOption = function(value) {
28486             return !!optionsMap.get(value);
28487           };
28488
28489
28490           self.registerOption = function(optionScope, optionElement, optionAttrs, interpolateValueFn, interpolateTextFn) {
28491
28492             if (interpolateValueFn) {
28493               // The value attribute is interpolated
28494               var oldVal;
28495               optionAttrs.$observe('value', function valueAttributeObserveAction(newVal) {
28496                 if (isDefined(oldVal)) {
28497                   self.removeOption(oldVal);
28498                 }
28499                 oldVal = newVal;
28500                 self.addOption(newVal, optionElement);
28501               });
28502             } else if (interpolateTextFn) {
28503               // The text content is interpolated
28504               optionScope.$watch(interpolateTextFn, function interpolateWatchAction(newVal, oldVal) {
28505                 optionAttrs.$set('value', newVal);
28506                 if (oldVal !== newVal) {
28507                   self.removeOption(oldVal);
28508                 }
28509                 self.addOption(newVal, optionElement);
28510               });
28511             } else {
28512               // The value attribute is static
28513               self.addOption(optionAttrs.value, optionElement);
28514             }
28515
28516             optionElement.on('$destroy', function() {
28517               self.removeOption(optionAttrs.value);
28518               self.ngModelCtrl.$render();
28519             });
28520           };
28521         }];
28522
28523         /**
28524          * @ngdoc directive
28525          * @name select
28526          * @restrict E
28527          *
28528          * @description
28529          * HTML `SELECT` element with angular data-binding.
28530          *
28531          * The `select` directive is used together with {@link ngModel `ngModel`} to provide data-binding
28532          * between the scope and the `<select>` control (including setting default values).
28533          * Ìt also handles dynamic `<option>` elements, which can be added using the {@link ngRepeat `ngRepeat}` or
28534          * {@link ngOptions `ngOptions`} directives.
28535          *
28536          * When an item in the `<select>` menu is selected, the value of the selected option will be bound
28537          * to the model identified by the `ngModel` directive. With static or repeated options, this is
28538          * the content of the `value` attribute or the textContent of the `<option>`, if the value attribute is missing.
28539          * If you want dynamic value attributes, you can use interpolation inside the value attribute.
28540          *
28541          * <div class="alert alert-warning">
28542          * Note that the value of a `select` directive used without `ngOptions` is always a string.
28543          * When the model needs to be bound to a non-string value, you must either explictly convert it
28544          * using a directive (see example below) or use `ngOptions` to specify the set of options.
28545          * This is because an option element can only be bound to string values at present.
28546          * </div>
28547          *
28548          * If the viewValue of `ngModel` does not match any of the options, then the control
28549          * will automatically add an "unknown" option, which it then removes when the mismatch is resolved.
28550          *
28551          * Optionally, a single hard-coded `<option>` element, with the value set to an empty string, can
28552          * be nested into the `<select>` element. This element will then represent the `null` or "not selected"
28553          * option. See example below for demonstration.
28554          *
28555          * <div class="alert alert-info">
28556          * In many cases, `ngRepeat` can be used on `<option>` elements instead of {@link ng.directive:ngOptions
28557          * ngOptions} to achieve a similar result. However, `ngOptions` provides some benefits, such as
28558          * more flexibility in how the `<select>`'s model is assigned via the `select` **`as`** part of the
28559          * comprehension expression, and additionally in reducing memory and increasing speed by not creating
28560          * a new scope for each repeated instance.
28561          * </div>
28562          *
28563          *
28564          * @param {string} ngModel Assignable angular expression to data-bind to.
28565          * @param {string=} name Property name of the form under which the control is published.
28566          * @param {string=} multiple Allows multiple options to be selected. The selected values will be
28567          *     bound to the model as an array.
28568          * @param {string=} required Sets `required` validation error key if the value is not entered.
28569          * @param {string=} ngRequired Adds required attribute and required validation constraint to
28570          * the element when the ngRequired expression evaluates to true. Use ngRequired instead of required
28571          * when you want to data-bind to the required attribute.
28572          * @param {string=} ngChange Angular expression to be executed when selected option(s) changes due to user
28573          *    interaction with the select element.
28574          * @param {string=} ngOptions sets the options that the select is populated with and defines what is
28575          * set on the model on selection. See {@link ngOptions `ngOptions`}.
28576          *
28577          * @example
28578          * ### Simple `select` elements with static options
28579          *
28580          * <example name="static-select" module="staticSelect">
28581          * <file name="index.html">
28582          * <div ng-controller="ExampleController">
28583          *   <form name="myForm">
28584          *     <label for="singleSelect"> Single select: </label><br>
28585          *     <select name="singleSelect" ng-model="data.singleSelect">
28586          *       <option value="option-1">Option 1</option>
28587          *       <option value="option-2">Option 2</option>
28588          *     </select><br>
28589          *
28590          *     <label for="singleSelect"> Single select with "not selected" option and dynamic option values: </label><br>
28591          *     <select name="singleSelect" id="singleSelect" ng-model="data.singleSelect">
28592          *       <option value="">---Please select---</option> <!-- not selected / blank option -->
28593          *       <option value="{{data.option1}}">Option 1</option> <!-- interpolation -->
28594          *       <option value="option-2">Option 2</option>
28595          *     </select><br>
28596          *     <button ng-click="forceUnknownOption()">Force unknown option</button><br>
28597          *     <tt>singleSelect = {{data.singleSelect}}</tt>
28598          *
28599          *     <hr>
28600          *     <label for="multipleSelect"> Multiple select: </label><br>
28601          *     <select name="multipleSelect" id="multipleSelect" ng-model="data.multipleSelect" multiple>
28602          *       <option value="option-1">Option 1</option>
28603          *       <option value="option-2">Option 2</option>
28604          *       <option value="option-3">Option 3</option>
28605          *     </select><br>
28606          *     <tt>multipleSelect = {{data.multipleSelect}}</tt><br/>
28607          *   </form>
28608          * </div>
28609          * </file>
28610          * <file name="app.js">
28611          *  angular.module('staticSelect', [])
28612          *    .controller('ExampleController', ['$scope', function($scope) {
28613          *      $scope.data = {
28614          *       singleSelect: null,
28615          *       multipleSelect: [],
28616          *       option1: 'option-1',
28617          *      };
28618          *
28619          *      $scope.forceUnknownOption = function() {
28620          *        $scope.data.singleSelect = 'nonsense';
28621          *      };
28622          *   }]);
28623          * </file>
28624          *</example>
28625          *
28626          * ### Using `ngRepeat` to generate `select` options
28627          * <example name="ngrepeat-select" module="ngrepeatSelect">
28628          * <file name="index.html">
28629          * <div ng-controller="ExampleController">
28630          *   <form name="myForm">
28631          *     <label for="repeatSelect"> Repeat select: </label>
28632          *     <select name="repeatSelect" id="repeatSelect" ng-model="data.repeatSelect">
28633          *       <option ng-repeat="option in data.availableOptions" value="{{option.id}}">{{option.name}}</option>
28634          *     </select>
28635          *   </form>
28636          *   <hr>
28637          *   <tt>repeatSelect = {{data.repeatSelect}}</tt><br/>
28638          * </div>
28639          * </file>
28640          * <file name="app.js">
28641          *  angular.module('ngrepeatSelect', [])
28642          *    .controller('ExampleController', ['$scope', function($scope) {
28643          *      $scope.data = {
28644          *       repeatSelect: null,
28645          *       availableOptions: [
28646          *         {id: '1', name: 'Option A'},
28647          *         {id: '2', name: 'Option B'},
28648          *         {id: '3', name: 'Option C'}
28649          *       ],
28650          *      };
28651          *   }]);
28652          * </file>
28653          *</example>
28654          *
28655          *
28656          * ### Using `select` with `ngOptions` and setting a default value
28657          * See the {@link ngOptions ngOptions documentation} for more `ngOptions` usage examples.
28658          *
28659          * <example name="select-with-default-values" module="defaultValueSelect">
28660          * <file name="index.html">
28661          * <div ng-controller="ExampleController">
28662          *   <form name="myForm">
28663          *     <label for="mySelect">Make a choice:</label>
28664          *     <select name="mySelect" id="mySelect"
28665          *       ng-options="option.name for option in data.availableOptions track by option.id"
28666          *       ng-model="data.selectedOption"></select>
28667          *   </form>
28668          *   <hr>
28669          *   <tt>option = {{data.selectedOption}}</tt><br/>
28670          * </div>
28671          * </file>
28672          * <file name="app.js">
28673          *  angular.module('defaultValueSelect', [])
28674          *    .controller('ExampleController', ['$scope', function($scope) {
28675          *      $scope.data = {
28676          *       availableOptions: [
28677          *         {id: '1', name: 'Option A'},
28678          *         {id: '2', name: 'Option B'},
28679          *         {id: '3', name: 'Option C'}
28680          *       ],
28681          *       selectedOption: {id: '3', name: 'Option C'} //This sets the default value of the select in the ui
28682          *       };
28683          *   }]);
28684          * </file>
28685          *</example>
28686          *
28687          *
28688          * ### Binding `select` to a non-string value via `ngModel` parsing / formatting
28689          *
28690          * <example name="select-with-non-string-options" module="nonStringSelect">
28691          *   <file name="index.html">
28692          *     <select ng-model="model.id" convert-to-number>
28693          *       <option value="0">Zero</option>
28694          *       <option value="1">One</option>
28695          *       <option value="2">Two</option>
28696          *     </select>
28697          *     {{ model }}
28698          *   </file>
28699          *   <file name="app.js">
28700          *     angular.module('nonStringSelect', [])
28701          *       .run(function($rootScope) {
28702          *         $rootScope.model = { id: 2 };
28703          *       })
28704          *       .directive('convertToNumber', function() {
28705          *         return {
28706          *           require: 'ngModel',
28707          *           link: function(scope, element, attrs, ngModel) {
28708          *             ngModel.$parsers.push(function(val) {
28709          *               return parseInt(val, 10);
28710          *             });
28711          *             ngModel.$formatters.push(function(val) {
28712          *               return '' + val;
28713          *             });
28714          *           }
28715          *         };
28716          *       });
28717          *   </file>
28718          *   <file name="protractor.js" type="protractor">
28719          *     it('should initialize to model', function() {
28720          *       var select = element(by.css('select'));
28721          *       expect(element(by.model('model.id')).$('option:checked').getText()).toEqual('Two');
28722          *     });
28723          *   </file>
28724          * </example>
28725          *
28726          */
28727         var selectDirective = function() {
28728
28729           return {
28730             restrict: 'E',
28731             require: ['select', '?ngModel'],
28732             controller: SelectController,
28733             priority: 1,
28734             link: {
28735               pre: selectPreLink
28736             }
28737           };
28738
28739           function selectPreLink(scope, element, attr, ctrls) {
28740
28741               // if ngModel is not defined, we don't need to do anything
28742               var ngModelCtrl = ctrls[1];
28743               if (!ngModelCtrl) return;
28744
28745               var selectCtrl = ctrls[0];
28746
28747               selectCtrl.ngModelCtrl = ngModelCtrl;
28748
28749               // We delegate rendering to the `writeValue` method, which can be changed
28750               // if the select can have multiple selected values or if the options are being
28751               // generated by `ngOptions`
28752               ngModelCtrl.$render = function() {
28753                 selectCtrl.writeValue(ngModelCtrl.$viewValue);
28754               };
28755
28756               // When the selected item(s) changes we delegate getting the value of the select control
28757               // to the `readValue` method, which can be changed if the select can have multiple
28758               // selected values or if the options are being generated by `ngOptions`
28759               element.on('change', function() {
28760                 scope.$apply(function() {
28761                   ngModelCtrl.$setViewValue(selectCtrl.readValue());
28762                 });
28763               });
28764
28765               // If the select allows multiple values then we need to modify how we read and write
28766               // values from and to the control; also what it means for the value to be empty and
28767               // we have to add an extra watch since ngModel doesn't work well with arrays - it
28768               // doesn't trigger rendering if only an item in the array changes.
28769               if (attr.multiple) {
28770
28771                 // Read value now needs to check each option to see if it is selected
28772                 selectCtrl.readValue = function readMultipleValue() {
28773                   var array = [];
28774                   forEach(element.find('option'), function(option) {
28775                     if (option.selected) {
28776                       array.push(option.value);
28777                     }
28778                   });
28779                   return array;
28780                 };
28781
28782                 // Write value now needs to set the selected property of each matching option
28783                 selectCtrl.writeValue = function writeMultipleValue(value) {
28784                   var items = new HashMap(value);
28785                   forEach(element.find('option'), function(option) {
28786                     option.selected = isDefined(items.get(option.value));
28787                   });
28788                 };
28789
28790                 // we have to do it on each watch since ngModel watches reference, but
28791                 // we need to work of an array, so we need to see if anything was inserted/removed
28792                 var lastView, lastViewRef = NaN;
28793                 scope.$watch(function selectMultipleWatch() {
28794                   if (lastViewRef === ngModelCtrl.$viewValue && !equals(lastView, ngModelCtrl.$viewValue)) {
28795                     lastView = shallowCopy(ngModelCtrl.$viewValue);
28796                     ngModelCtrl.$render();
28797                   }
28798                   lastViewRef = ngModelCtrl.$viewValue;
28799                 });
28800
28801                 // If we are a multiple select then value is now a collection
28802                 // so the meaning of $isEmpty changes
28803                 ngModelCtrl.$isEmpty = function(value) {
28804                   return !value || value.length === 0;
28805                 };
28806
28807               }
28808             }
28809         };
28810
28811
28812         // The option directive is purely designed to communicate the existence (or lack of)
28813         // of dynamically created (and destroyed) option elements to their containing select
28814         // directive via its controller.
28815         var optionDirective = ['$interpolate', function($interpolate) {
28816           return {
28817             restrict: 'E',
28818             priority: 100,
28819             compile: function(element, attr) {
28820
28821               if (isDefined(attr.value)) {
28822                 // If the value attribute is defined, check if it contains an interpolation
28823                 var interpolateValueFn = $interpolate(attr.value, true);
28824               } else {
28825                 // If the value attribute is not defined then we fall back to the
28826                 // text content of the option element, which may be interpolated
28827                 var interpolateTextFn = $interpolate(element.text(), true);
28828                 if (!interpolateTextFn) {
28829                   attr.$set('value', element.text());
28830                 }
28831               }
28832
28833               return function(scope, element, attr) {
28834
28835                 // This is an optimization over using ^^ since we don't want to have to search
28836                 // all the way to the root of the DOM for every single option element
28837                 var selectCtrlName = '$selectController',
28838                     parent = element.parent(),
28839                     selectCtrl = parent.data(selectCtrlName) ||
28840                       parent.parent().data(selectCtrlName); // in case we are in optgroup
28841
28842                 if (selectCtrl) {
28843                   selectCtrl.registerOption(scope, element, attr, interpolateValueFn, interpolateTextFn);
28844                 }
28845               };
28846             }
28847           };
28848         }];
28849
28850         var styleDirective = valueFn({
28851           restrict: 'E',
28852           terminal: false
28853         });
28854
28855         var requiredDirective = function() {
28856           return {
28857             restrict: 'A',
28858             require: '?ngModel',
28859             link: function(scope, elm, attr, ctrl) {
28860               if (!ctrl) return;
28861               attr.required = true; // force truthy in case we are on non input element
28862
28863               ctrl.$validators.required = function(modelValue, viewValue) {
28864                 return !attr.required || !ctrl.$isEmpty(viewValue);
28865               };
28866
28867               attr.$observe('required', function() {
28868                 ctrl.$validate();
28869               });
28870             }
28871           };
28872         };
28873
28874
28875         var patternDirective = function() {
28876           return {
28877             restrict: 'A',
28878             require: '?ngModel',
28879             link: function(scope, elm, attr, ctrl) {
28880               if (!ctrl) return;
28881
28882               var regexp, patternExp = attr.ngPattern || attr.pattern;
28883               attr.$observe('pattern', function(regex) {
28884                 if (isString(regex) && regex.length > 0) {
28885                   regex = new RegExp('^' + regex + '$');
28886                 }
28887
28888                 if (regex && !regex.test) {
28889                   throw minErr('ngPattern')('noregexp',
28890                     'Expected {0} to be a RegExp but was {1}. Element: {2}', patternExp,
28891                     regex, startingTag(elm));
28892                 }
28893
28894                 regexp = regex || undefined;
28895                 ctrl.$validate();
28896               });
28897
28898               ctrl.$validators.pattern = function(modelValue, viewValue) {
28899                 // HTML5 pattern constraint validates the input value, so we validate the viewValue
28900                 return ctrl.$isEmpty(viewValue) || isUndefined(regexp) || regexp.test(viewValue);
28901               };
28902             }
28903           };
28904         };
28905
28906
28907         var maxlengthDirective = function() {
28908           return {
28909             restrict: 'A',
28910             require: '?ngModel',
28911             link: function(scope, elm, attr, ctrl) {
28912               if (!ctrl) return;
28913
28914               var maxlength = -1;
28915               attr.$observe('maxlength', function(value) {
28916                 var intVal = toInt(value);
28917                 maxlength = isNaN(intVal) ? -1 : intVal;
28918                 ctrl.$validate();
28919               });
28920               ctrl.$validators.maxlength = function(modelValue, viewValue) {
28921                 return (maxlength < 0) || ctrl.$isEmpty(viewValue) || (viewValue.length <= maxlength);
28922               };
28923             }
28924           };
28925         };
28926
28927         var minlengthDirective = function() {
28928           return {
28929             restrict: 'A',
28930             require: '?ngModel',
28931             link: function(scope, elm, attr, ctrl) {
28932               if (!ctrl) return;
28933
28934               var minlength = 0;
28935               attr.$observe('minlength', function(value) {
28936                 minlength = toInt(value) || 0;
28937                 ctrl.$validate();
28938               });
28939               ctrl.$validators.minlength = function(modelValue, viewValue) {
28940                 return ctrl.$isEmpty(viewValue) || viewValue.length >= minlength;
28941               };
28942             }
28943           };
28944         };
28945
28946         if (window.angular.bootstrap) {
28947           //AngularJS is already loaded, so we can return here...
28948           console.log('WARNING: Tried to load angular more than once.');
28949           return;
28950         }
28951
28952         //try to bind to jquery now so that one can write jqLite(document).ready()
28953         //but we will rebind on bootstrap again.
28954         bindJQuery();
28955
28956         publishExternalAPI(angular);
28957
28958         angular.module("ngLocale", [], ["$provide", function($provide) {
28959         var PLURAL_CATEGORY = {ZERO: "zero", ONE: "one", TWO: "two", FEW: "few", MANY: "many", OTHER: "other"};
28960         function getDecimals(n) {
28961           n = n + '';
28962           var i = n.indexOf('.');
28963           return (i == -1) ? 0 : n.length - i - 1;
28964         }
28965
28966         function getVF(n, opt_precision) {
28967           var v = opt_precision;
28968
28969           if (undefined === v) {
28970             v = Math.min(getDecimals(n), 3);
28971           }
28972
28973           var base = Math.pow(10, v);
28974           var f = ((n * base) | 0) % base;
28975           return {v: v, f: f};
28976         }
28977
28978         $provide.value("$locale", {
28979           "DATETIME_FORMATS": {
28980             "AMPMS": [
28981               "AM",
28982               "PM"
28983             ],
28984             "DAY": [
28985               "Sunday",
28986               "Monday",
28987               "Tuesday",
28988               "Wednesday",
28989               "Thursday",
28990               "Friday",
28991               "Saturday"
28992             ],
28993             "ERANAMES": [
28994               "Before Christ",
28995               "Anno Domini"
28996             ],
28997             "ERAS": [
28998               "BC",
28999               "AD"
29000             ],
29001             "FIRSTDAYOFWEEK": 6,
29002             "MONTH": [
29003               "January",
29004               "February",
29005               "March",
29006               "April",
29007               "May",
29008               "June",
29009               "July",
29010               "August",
29011               "September",
29012               "October",
29013               "November",
29014               "December"
29015             ],
29016             "SHORTDAY": [
29017               "Sun",
29018               "Mon",
29019               "Tue",
29020               "Wed",
29021               "Thu",
29022               "Fri",
29023               "Sat"
29024             ],
29025             "SHORTMONTH": [
29026               "Jan",
29027               "Feb",
29028               "Mar",
29029               "Apr",
29030               "May",
29031               "Jun",
29032               "Jul",
29033               "Aug",
29034               "Sep",
29035               "Oct",
29036               "Nov",
29037               "Dec"
29038             ],
29039             "WEEKENDRANGE": [
29040               5,
29041               6
29042             ],
29043             "fullDate": "EEEE, MMMM d, y",
29044             "longDate": "MMMM d, y",
29045             "medium": "MMM d, y h:mm:ss a",
29046             "mediumDate": "MMM d, y",
29047             "mediumTime": "h:mm:ss a",
29048             "short": "M/d/yy h:mm a",
29049             "shortDate": "M/d/yy",
29050             "shortTime": "h:mm a"
29051           },
29052           "NUMBER_FORMATS": {
29053             "CURRENCY_SYM": "$",
29054             "DECIMAL_SEP": ".",
29055             "GROUP_SEP": ",",
29056             "PATTERNS": [
29057               {
29058                 "gSize": 3,
29059                 "lgSize": 3,
29060                 "maxFrac": 3,
29061                 "minFrac": 0,
29062                 "minInt": 1,
29063                 "negPre": "-",
29064                 "negSuf": "",
29065                 "posPre": "",
29066                 "posSuf": ""
29067               },
29068               {
29069                 "gSize": 3,
29070                 "lgSize": 3,
29071                 "maxFrac": 2,
29072                 "minFrac": 2,
29073                 "minInt": 1,
29074                 "negPre": "-\u00a4",
29075                 "negSuf": "",
29076                 "posPre": "\u00a4",
29077                 "posSuf": ""
29078               }
29079             ]
29080           },
29081           "id": "en-us",
29082           "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;}
29083         });
29084         }]);
29085
29086           jqLite(document).ready(function() {
29087             angularInit(document, bootstrap);
29088           });
29089
29090         })(window, document);
29091
29092         !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>');
29093
29094 /***/ },
29095 /* 3 */
29096 /***/ function(module, exports) {
29097
29098         /**
29099          * State-based routing for AngularJS
29100          * @version v0.2.15
29101          * @link http://angular-ui.github.com/
29102          * @license MIT License, http://www.opensource.org/licenses/MIT
29103          */
29104
29105         /* commonjs package manager support (eg componentjs) */
29106         if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
29107           module.exports = 'ui.router';
29108         }
29109
29110         (function (window, angular, undefined) {
29111         /*jshint globalstrict:true*/
29112         /*global angular:false*/
29113         'use strict';
29114
29115         var isDefined = angular.isDefined,
29116             isFunction = angular.isFunction,
29117             isString = angular.isString,
29118             isObject = angular.isObject,
29119             isArray = angular.isArray,
29120             forEach = angular.forEach,
29121             extend = angular.extend,
29122             copy = angular.copy;
29123
29124         function inherit(parent, extra) {
29125           return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29126         }
29127
29128         function merge(dst) {
29129           forEach(arguments, function(obj) {
29130             if (obj !== dst) {
29131               forEach(obj, function(value, key) {
29132                 if (!dst.hasOwnProperty(key)) dst[key] = value;
29133               });
29134             }
29135           });
29136           return dst;
29137         }
29138
29139         /**
29140          * Finds the common ancestor path between two states.
29141          *
29142          * @param {Object} first The first state.
29143          * @param {Object} second The second state.
29144          * @return {Array} Returns an array of state names in descending order, not including the root.
29145          */
29146         function ancestors(first, second) {
29147           var path = [];
29148
29149           for (var n in first.path) {
29150             if (first.path[n] !== second.path[n]) break;
29151             path.push(first.path[n]);
29152           }
29153           return path;
29154         }
29155
29156         /**
29157          * IE8-safe wrapper for `Object.keys()`.
29158          *
29159          * @param {Object} object A JavaScript object.
29160          * @return {Array} Returns the keys of the object as an array.
29161          */
29162         function objectKeys(object) {
29163           if (Object.keys) {
29164             return Object.keys(object);
29165           }
29166           var result = [];
29167
29168           forEach(object, function(val, key) {
29169             result.push(key);
29170           });
29171           return result;
29172         }
29173
29174         /**
29175          * IE8-safe wrapper for `Array.prototype.indexOf()`.
29176          *
29177          * @param {Array} array A JavaScript array.
29178          * @param {*} value A value to search the array for.
29179          * @return {Number} Returns the array index value of `value`, or `-1` if not present.
29180          */
29181         function indexOf(array, value) {
29182           if (Array.prototype.indexOf) {
29183             return array.indexOf(value, Number(arguments[2]) || 0);
29184           }
29185           var len = array.length >>> 0, from = Number(arguments[2]) || 0;
29186           from = (from < 0) ? Math.ceil(from) : Math.floor(from);
29187
29188           if (from < 0) from += len;
29189
29190           for (; from < len; from++) {
29191             if (from in array && array[from] === value) return from;
29192           }
29193           return -1;
29194         }
29195
29196         /**
29197          * Merges a set of parameters with all parameters inherited between the common parents of the
29198          * current state and a given destination state.
29199          *
29200          * @param {Object} currentParams The value of the current state parameters ($stateParams).
29201          * @param {Object} newParams The set of parameters which will be composited with inherited params.
29202          * @param {Object} $current Internal definition of object representing the current state.
29203          * @param {Object} $to Internal definition of object representing state to transition to.
29204          */
29205         function inheritParams(currentParams, newParams, $current, $to) {
29206           var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
29207
29208           for (var i in parents) {
29209             if (!parents[i].params) continue;
29210             parentParams = objectKeys(parents[i].params);
29211             if (!parentParams.length) continue;
29212
29213             for (var j in parentParams) {
29214               if (indexOf(inheritList, parentParams[j]) >= 0) continue;
29215               inheritList.push(parentParams[j]);
29216               inherited[parentParams[j]] = currentParams[parentParams[j]];
29217             }
29218           }
29219           return extend({}, inherited, newParams);
29220         }
29221
29222         /**
29223          * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
29224          *
29225          * @param {Object} a The first object.
29226          * @param {Object} b The second object.
29227          * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
29228          *                     it defaults to the list of keys in `a`.
29229          * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
29230          */
29231         function equalForKeys(a, b, keys) {
29232           if (!keys) {
29233             keys = [];
29234             for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
29235           }
29236
29237           for (var i=0; i<keys.length; i++) {
29238             var k = keys[i];
29239             if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
29240           }
29241           return true;
29242         }
29243
29244         /**
29245          * Returns the subset of an object, based on a list of keys.
29246          *
29247          * @param {Array} keys
29248          * @param {Object} values
29249          * @return {Boolean} Returns a subset of `values`.
29250          */
29251         function filterByKeys(keys, values) {
29252           var filtered = {};
29253
29254           forEach(keys, function (name) {
29255             filtered[name] = values[name];
29256           });
29257           return filtered;
29258         }
29259
29260         // like _.indexBy
29261         // when you know that your index values will be unique, or you want last-one-in to win
29262         function indexBy(array, propName) {
29263           var result = {};
29264           forEach(array, function(item) {
29265             result[item[propName]] = item;
29266           });
29267           return result;
29268         }
29269
29270         // extracted from underscore.js
29271         // Return a copy of the object only containing the whitelisted properties.
29272         function pick(obj) {
29273           var copy = {};
29274           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29275           forEach(keys, function(key) {
29276             if (key in obj) copy[key] = obj[key];
29277           });
29278           return copy;
29279         }
29280
29281         // extracted from underscore.js
29282         // Return a copy of the object omitting the blacklisted properties.
29283         function omit(obj) {
29284           var copy = {};
29285           var keys = Array.prototype.concat.apply(Array.prototype, Array.prototype.slice.call(arguments, 1));
29286           for (var key in obj) {
29287             if (indexOf(keys, key) == -1) copy[key] = obj[key];
29288           }
29289           return copy;
29290         }
29291
29292         function pluck(collection, key) {
29293           var result = isArray(collection) ? [] : {};
29294
29295           forEach(collection, function(val, i) {
29296             result[i] = isFunction(key) ? key(val) : val[key];
29297           });
29298           return result;
29299         }
29300
29301         function filter(collection, callback) {
29302           var array = isArray(collection);
29303           var result = array ? [] : {};
29304           forEach(collection, function(val, i) {
29305             if (callback(val, i)) {
29306               result[array ? result.length : i] = val;
29307             }
29308           });
29309           return result;
29310         }
29311
29312         function map(collection, callback) {
29313           var result = isArray(collection) ? [] : {};
29314
29315           forEach(collection, function(val, i) {
29316             result[i] = callback(val, i);
29317           });
29318           return result;
29319         }
29320
29321         /**
29322          * @ngdoc overview
29323          * @name ui.router.util
29324          *
29325          * @description
29326          * # ui.router.util sub-module
29327          *
29328          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29329          * in your angular app (use {@link ui.router} module instead).
29330          *
29331          */
29332         angular.module('ui.router.util', ['ng']);
29333
29334         /**
29335          * @ngdoc overview
29336          * @name ui.router.router
29337          * 
29338          * @requires ui.router.util
29339          *
29340          * @description
29341          * # ui.router.router sub-module
29342          *
29343          * This module is a dependency of other sub-modules. Do not include this module as a dependency
29344          * in your angular app (use {@link ui.router} module instead).
29345          */
29346         angular.module('ui.router.router', ['ui.router.util']);
29347
29348         /**
29349          * @ngdoc overview
29350          * @name ui.router.state
29351          * 
29352          * @requires ui.router.router
29353          * @requires ui.router.util
29354          *
29355          * @description
29356          * # ui.router.state sub-module
29357          *
29358          * This module is a dependency of the main ui.router module. Do not include this module as a dependency
29359          * in your angular app (use {@link ui.router} module instead).
29360          * 
29361          */
29362         angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
29363
29364         /**
29365          * @ngdoc overview
29366          * @name ui.router
29367          *
29368          * @requires ui.router.state
29369          *
29370          * @description
29371          * # ui.router
29372          * 
29373          * ## The main module for ui.router 
29374          * There are several sub-modules included with the ui.router module, however only this module is needed
29375          * as a dependency within your angular app. The other modules are for organization purposes. 
29376          *
29377          * The modules are:
29378          * * ui.router - the main "umbrella" module
29379          * * ui.router.router - 
29380          * 
29381          * *You'll need to include **only** this module as the dependency within your angular app.*
29382          * 
29383          * <pre>
29384          * <!doctype html>
29385          * <html ng-app="myApp">
29386          * <head>
29387          *   <script src="js/angular.js"></script>
29388          *   <!-- Include the ui-router script -->
29389          *   <script src="js/angular-ui-router.min.js"></script>
29390          *   <script>
29391          *     // ...and add 'ui.router' as a dependency
29392          *     var myApp = angular.module('myApp', ['ui.router']);
29393          *   </script>
29394          * </head>
29395          * <body>
29396          * </body>
29397          * </html>
29398          * </pre>
29399          */
29400         angular.module('ui.router', ['ui.router.state']);
29401
29402         angular.module('ui.router.compat', ['ui.router']);
29403
29404         /**
29405          * @ngdoc object
29406          * @name ui.router.util.$resolve
29407          *
29408          * @requires $q
29409          * @requires $injector
29410          *
29411          * @description
29412          * Manages resolution of (acyclic) graphs of promises.
29413          */
29414         $Resolve.$inject = ['$q', '$injector'];
29415         function $Resolve(  $q,    $injector) {
29416           
29417           var VISIT_IN_PROGRESS = 1,
29418               VISIT_DONE = 2,
29419               NOTHING = {},
29420               NO_DEPENDENCIES = [],
29421               NO_LOCALS = NOTHING,
29422               NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
29423           
29424
29425           /**
29426            * @ngdoc function
29427            * @name ui.router.util.$resolve#study
29428            * @methodOf ui.router.util.$resolve
29429            *
29430            * @description
29431            * Studies a set of invocables that are likely to be used multiple times.
29432            * <pre>
29433            * $resolve.study(invocables)(locals, parent, self)
29434            * </pre>
29435            * is equivalent to
29436            * <pre>
29437            * $resolve.resolve(invocables, locals, parent, self)
29438            * </pre>
29439            * but the former is more efficient (in fact `resolve` just calls `study` 
29440            * internally).
29441            *
29442            * @param {object} invocables Invocable objects
29443            * @return {function} a function to pass in locals, parent and self
29444            */
29445           this.study = function (invocables) {
29446             if (!isObject(invocables)) throw new Error("'invocables' must be an object");
29447             var invocableKeys = objectKeys(invocables || {});
29448             
29449             // Perform a topological sort of invocables to build an ordered plan
29450             var plan = [], cycle = [], visited = {};
29451             function visit(value, key) {
29452               if (visited[key] === VISIT_DONE) return;
29453               
29454               cycle.push(key);
29455               if (visited[key] === VISIT_IN_PROGRESS) {
29456                 cycle.splice(0, indexOf(cycle, key));
29457                 throw new Error("Cyclic dependency: " + cycle.join(" -> "));
29458               }
29459               visited[key] = VISIT_IN_PROGRESS;
29460               
29461               if (isString(value)) {
29462                 plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
29463               } else {
29464                 var params = $injector.annotate(value);
29465                 forEach(params, function (param) {
29466                   if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
29467                 });
29468                 plan.push(key, value, params);
29469               }
29470               
29471               cycle.pop();
29472               visited[key] = VISIT_DONE;
29473             }
29474             forEach(invocables, visit);
29475             invocables = cycle = visited = null; // plan is all that's required
29476             
29477             function isResolve(value) {
29478               return isObject(value) && value.then && value.$$promises;
29479             }
29480             
29481             return function (locals, parent, self) {
29482               if (isResolve(locals) && self === undefined) {
29483                 self = parent; parent = locals; locals = null;
29484               }
29485               if (!locals) locals = NO_LOCALS;
29486               else if (!isObject(locals)) {
29487                 throw new Error("'locals' must be an object");
29488               }       
29489               if (!parent) parent = NO_PARENT;
29490               else if (!isResolve(parent)) {
29491                 throw new Error("'parent' must be a promise returned by $resolve.resolve()");
29492               }
29493               
29494               // To complete the overall resolution, we have to wait for the parent
29495               // promise and for the promise for each invokable in our plan.
29496               var resolution = $q.defer(),
29497                   result = resolution.promise,
29498                   promises = result.$$promises = {},
29499                   values = extend({}, locals),
29500                   wait = 1 + plan.length/3,
29501                   merged = false;
29502                   
29503               function done() {
29504                 // Merge parent values we haven't got yet and publish our own $$values
29505                 if (!--wait) {
29506                   if (!merged) merge(values, parent.$$values); 
29507                   result.$$values = values;
29508                   result.$$promises = result.$$promises || true; // keep for isResolve()
29509                   delete result.$$inheritedValues;
29510                   resolution.resolve(values);
29511                 }
29512               }
29513               
29514               function fail(reason) {
29515                 result.$$failure = reason;
29516                 resolution.reject(reason);
29517               }
29518
29519               // Short-circuit if parent has already failed
29520               if (isDefined(parent.$$failure)) {
29521                 fail(parent.$$failure);
29522                 return result;
29523               }
29524               
29525               if (parent.$$inheritedValues) {
29526                 merge(values, omit(parent.$$inheritedValues, invocableKeys));
29527               }
29528
29529               // Merge parent values if the parent has already resolved, or merge
29530               // parent promises and wait if the parent resolve is still in progress.
29531               extend(promises, parent.$$promises);
29532               if (parent.$$values) {
29533                 merged = merge(values, omit(parent.$$values, invocableKeys));
29534                 result.$$inheritedValues = omit(parent.$$values, invocableKeys);
29535                 done();
29536               } else {
29537                 if (parent.$$inheritedValues) {
29538                   result.$$inheritedValues = omit(parent.$$inheritedValues, invocableKeys);
29539                 }        
29540                 parent.then(done, fail);
29541               }
29542               
29543               // Process each invocable in the plan, but ignore any where a local of the same name exists.
29544               for (var i=0, ii=plan.length; i<ii; i+=3) {
29545                 if (locals.hasOwnProperty(plan[i])) done();
29546                 else invoke(plan[i], plan[i+1], plan[i+2]);
29547               }
29548               
29549               function invoke(key, invocable, params) {
29550                 // Create a deferred for this invocation. Failures will propagate to the resolution as well.
29551                 var invocation = $q.defer(), waitParams = 0;
29552                 function onfailure(reason) {
29553                   invocation.reject(reason);
29554                   fail(reason);
29555                 }
29556                 // Wait for any parameter that we have a promise for (either from parent or from this
29557                 // resolve; in that case study() will have made sure it's ordered before us in the plan).
29558                 forEach(params, function (dep) {
29559                   if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
29560                     waitParams++;
29561                     promises[dep].then(function (result) {
29562                       values[dep] = result;
29563                       if (!(--waitParams)) proceed();
29564                     }, onfailure);
29565                   }
29566                 });
29567                 if (!waitParams) proceed();
29568                 function proceed() {
29569                   if (isDefined(result.$$failure)) return;
29570                   try {
29571                     invocation.resolve($injector.invoke(invocable, self, values));
29572                     invocation.promise.then(function (result) {
29573                       values[key] = result;
29574                       done();
29575                     }, onfailure);
29576                   } catch (e) {
29577                     onfailure(e);
29578                   }
29579                 }
29580                 // Publish promise synchronously; invocations further down in the plan may depend on it.
29581                 promises[key] = invocation.promise;
29582               }
29583               
29584               return result;
29585             };
29586           };
29587           
29588           /**
29589            * @ngdoc function
29590            * @name ui.router.util.$resolve#resolve
29591            * @methodOf ui.router.util.$resolve
29592            *
29593            * @description
29594            * Resolves a set of invocables. An invocable is a function to be invoked via 
29595            * `$injector.invoke()`, and can have an arbitrary number of dependencies. 
29596            * An invocable can either return a value directly,
29597            * or a `$q` promise. If a promise is returned it will be resolved and the 
29598            * resulting value will be used instead. Dependencies of invocables are resolved 
29599            * (in this order of precedence)
29600            *
29601            * - from the specified `locals`
29602            * - from another invocable that is part of this `$resolve` call
29603            * - from an invocable that is inherited from a `parent` call to `$resolve` 
29604            *   (or recursively
29605            * - from any ancestor `$resolve` of that parent).
29606            *
29607            * The return value of `$resolve` is a promise for an object that contains 
29608            * (in this order of precedence)
29609            *
29610            * - any `locals` (if specified)
29611            * - the resolved return values of all injectables
29612            * - any values inherited from a `parent` call to `$resolve` (if specified)
29613            *
29614            * The promise will resolve after the `parent` promise (if any) and all promises 
29615            * returned by injectables have been resolved. If any invocable 
29616            * (or `$injector.invoke`) throws an exception, or if a promise returned by an 
29617            * invocable is rejected, the `$resolve` promise is immediately rejected with the 
29618            * same error. A rejection of a `parent` promise (if specified) will likewise be 
29619            * propagated immediately. Once the `$resolve` promise has been rejected, no 
29620            * further invocables will be called.
29621            * 
29622            * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
29623            * to throw an error. As a special case, an injectable can depend on a parameter 
29624            * with the same name as the injectable, which will be fulfilled from the `parent` 
29625            * injectable of the same name. This allows inherited values to be decorated. 
29626            * Note that in this case any other injectable in the same `$resolve` with the same
29627            * dependency would see the decorated value, not the inherited value.
29628            *
29629            * Note that missing dependencies -- unlike cyclic dependencies -- will cause an 
29630            * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous) 
29631            * exception.
29632            *
29633            * Invocables are invoked eagerly as soon as all dependencies are available. 
29634            * This is true even for dependencies inherited from a `parent` call to `$resolve`.
29635            *
29636            * As a special case, an invocable can be a string, in which case it is taken to 
29637            * be a service name to be passed to `$injector.get()`. This is supported primarily 
29638            * for backwards-compatibility with the `resolve` property of `$routeProvider` 
29639            * routes.
29640            *
29641            * @param {object} invocables functions to invoke or 
29642            * `$injector` services to fetch.
29643            * @param {object} locals  values to make available to the injectables
29644            * @param {object} parent  a promise returned by another call to `$resolve`.
29645            * @param {object} self  the `this` for the invoked methods
29646            * @return {object} Promise for an object that contains the resolved return value
29647            * of all invocables, as well as any inherited and local values.
29648            */
29649           this.resolve = function (invocables, locals, parent, self) {
29650             return this.study(invocables)(locals, parent, self);
29651           };
29652         }
29653
29654         angular.module('ui.router.util').service('$resolve', $Resolve);
29655
29656
29657         /**
29658          * @ngdoc object
29659          * @name ui.router.util.$templateFactory
29660          *
29661          * @requires $http
29662          * @requires $templateCache
29663          * @requires $injector
29664          *
29665          * @description
29666          * Service. Manages loading of templates.
29667          */
29668         $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
29669         function $TemplateFactory(  $http,   $templateCache,   $injector) {
29670
29671           /**
29672            * @ngdoc function
29673            * @name ui.router.util.$templateFactory#fromConfig
29674            * @methodOf ui.router.util.$templateFactory
29675            *
29676            * @description
29677            * Creates a template from a configuration object. 
29678            *
29679            * @param {object} config Configuration object for which to load a template. 
29680            * The following properties are search in the specified order, and the first one 
29681            * that is defined is used to create the template:
29682            *
29683            * @param {string|object} config.template html string template or function to 
29684            * load via {@link ui.router.util.$templateFactory#fromString fromString}.
29685            * @param {string|object} config.templateUrl url to load or a function returning 
29686            * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
29687            * @param {Function} config.templateProvider function to invoke via 
29688            * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
29689            * @param {object} params  Parameters to pass to the template function.
29690            * @param {object} locals Locals to pass to `invoke` if the template is loaded 
29691            * via a `templateProvider`. Defaults to `{ params: params }`.
29692            *
29693            * @return {string|object}  The template html as a string, or a promise for 
29694            * that string,or `null` if no template is configured.
29695            */
29696           this.fromConfig = function (config, params, locals) {
29697             return (
29698               isDefined(config.template) ? this.fromString(config.template, params) :
29699               isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
29700               isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
29701               null
29702             );
29703           };
29704
29705           /**
29706            * @ngdoc function
29707            * @name ui.router.util.$templateFactory#fromString
29708            * @methodOf ui.router.util.$templateFactory
29709            *
29710            * @description
29711            * Creates a template from a string or a function returning a string.
29712            *
29713            * @param {string|object} template html template as a string or function that 
29714            * returns an html template as a string.
29715            * @param {object} params Parameters to pass to the template function.
29716            *
29717            * @return {string|object} The template html as a string, or a promise for that 
29718            * string.
29719            */
29720           this.fromString = function (template, params) {
29721             return isFunction(template) ? template(params) : template;
29722           };
29723
29724           /**
29725            * @ngdoc function
29726            * @name ui.router.util.$templateFactory#fromUrl
29727            * @methodOf ui.router.util.$templateFactory
29728            * 
29729            * @description
29730            * Loads a template from the a URL via `$http` and `$templateCache`.
29731            *
29732            * @param {string|Function} url url of the template to load, or a function 
29733            * that returns a url.
29734            * @param {Object} params Parameters to pass to the url function.
29735            * @return {string|Promise.<string>} The template html as a string, or a promise 
29736            * for that string.
29737            */
29738           this.fromUrl = function (url, params) {
29739             if (isFunction(url)) url = url(params);
29740             if (url == null) return null;
29741             else return $http
29742                 .get(url, { cache: $templateCache, headers: { Accept: 'text/html' }})
29743                 .then(function(response) { return response.data; });
29744           };
29745
29746           /**
29747            * @ngdoc function
29748            * @name ui.router.util.$templateFactory#fromProvider
29749            * @methodOf ui.router.util.$templateFactory
29750            *
29751            * @description
29752            * Creates a template by invoking an injectable provider function.
29753            *
29754            * @param {Function} provider Function to invoke via `$injector.invoke`
29755            * @param {Object} params Parameters for the template.
29756            * @param {Object} locals Locals to pass to `invoke`. Defaults to 
29757            * `{ params: params }`.
29758            * @return {string|Promise.<string>} The template html as a string, or a promise 
29759            * for that string.
29760            */
29761           this.fromProvider = function (provider, params, locals) {
29762             return $injector.invoke(provider, null, locals || { params: params });
29763           };
29764         }
29765
29766         angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
29767
29768         var $$UMFP; // reference to $UrlMatcherFactoryProvider
29769
29770         /**
29771          * @ngdoc object
29772          * @name ui.router.util.type:UrlMatcher
29773          *
29774          * @description
29775          * Matches URLs against patterns and extracts named parameters from the path or the search
29776          * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
29777          * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
29778          * do not influence whether or not a URL is matched, but their values are passed through into
29779          * the matched parameters returned by {@link ui.router.util.type:UrlMatcher#methods_exec exec}.
29780          *
29781          * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
29782          * syntax, which optionally allows a regular expression for the parameter to be specified:
29783          *
29784          * * `':'` name - colon placeholder
29785          * * `'*'` name - catch-all placeholder
29786          * * `'{' name '}'` - curly placeholder
29787          * * `'{' name ':' regexp|type '}'` - curly placeholder with regexp or type name. Should the
29788          *   regexp itself contain curly braces, they must be in matched pairs or escaped with a backslash.
29789          *
29790          * Parameter names may contain only word characters (latin letters, digits, and underscore) and
29791          * must be unique within the pattern (across both path and search parameters). For colon
29792          * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
29793          * number of characters other than '/'. For catch-all placeholders the path parameter matches
29794          * any number of characters.
29795          *
29796          * Examples:
29797          *
29798          * * `'/hello/'` - Matches only if the path is exactly '/hello/'. There is no special treatment for
29799          *   trailing slashes, and patterns have to match the entire path, not just a prefix.
29800          * * `'/user/:id'` - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
29801          *   '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
29802          * * `'/user/{id}'` - Same as the previous example, but using curly brace syntax.
29803          * * `'/user/{id:[^/]*}'` - Same as the previous example.
29804          * * `'/user/{id:[0-9a-fA-F]{1,8}}'` - Similar to the previous example, but only matches if the id
29805          *   parameter consists of 1 to 8 hex digits.
29806          * * `'/files/{path:.*}'` - Matches any URL starting with '/files/' and captures the rest of the
29807          *   path into the parameter 'path'.
29808          * * `'/files/*path'` - ditto.
29809          * * `'/calendar/{start:date}'` - Matches "/calendar/2014-11-12" (because the pattern defined
29810          *   in the built-in  `date` Type matches `2014-11-12`) and provides a Date object in $stateParams.start
29811          *
29812          * @param {string} pattern  The pattern to compile into a matcher.
29813          * @param {Object} config  A configuration object hash:
29814          * @param {Object=} parentMatcher Used to concatenate the pattern/config onto
29815          *   an existing UrlMatcher
29816          *
29817          * * `caseInsensitive` - `true` if URL matching should be case insensitive, otherwise `false`, the default value (for backward compatibility) is `false`.
29818          * * `strict` - `false` if matching against a URL with a trailing slash should be treated as equivalent to a URL without a trailing slash, the default value is `true`.
29819          *
29820          * @property {string} prefix  A static prefix of this pattern. The matcher guarantees that any
29821          *   URL matching this matcher (i.e. any string for which {@link ui.router.util.type:UrlMatcher#methods_exec exec()} returns
29822          *   non-null) will start with this prefix.
29823          *
29824          * @property {string} source  The pattern that was passed into the constructor
29825          *
29826          * @property {string} sourcePath  The path portion of the source property
29827          *
29828          * @property {string} sourceSearch  The search portion of the source property
29829          *
29830          * @property {string} regex  The constructed regex that will be used to match against the url when
29831          *   it is time to determine which url will match.
29832          *
29833          * @returns {Object}  New `UrlMatcher` object
29834          */
29835         function UrlMatcher(pattern, config, parentMatcher) {
29836           config = extend({ params: {} }, isObject(config) ? config : {});
29837
29838           // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
29839           //   '*' name
29840           //   ':' name
29841           //   '{' name '}'
29842           //   '{' name ':' regexp '}'
29843           // The regular expression is somewhat complicated due to the need to allow curly braces
29844           // inside the regular expression. The placeholder regexp breaks down as follows:
29845           //    ([:*])([\w\[\]]+)              - classic placeholder ($1 / $2) (search version has - for snake-case)
29846           //    \{([\w\[\]]+)(?:\:( ... ))?\}  - curly brace placeholder ($3) with optional regexp/type ... ($4) (search version has - for snake-case
29847           //    (?: ... | ... | ... )+         - the regexp consists of any number of atoms, an atom being either
29848           //    [^{}\\]+                       - anything other than curly braces or backslash
29849           //    \\.                            - a backslash escape
29850           //    \{(?:[^{}\\]+|\\.)*\}          - a matched set of curly braces containing other atoms
29851           var placeholder       = /([:*])([\w\[\]]+)|\{([\w\[\]]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29852               searchPlaceholder = /([:]?)([\w\[\]-]+)|\{([\w\[\]-]+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
29853               compiled = '^', last = 0, m,
29854               segments = this.segments = [],
29855               parentParams = parentMatcher ? parentMatcher.params : {},
29856               params = this.params = parentMatcher ? parentMatcher.params.$$new() : new $$UMFP.ParamSet(),
29857               paramNames = [];
29858
29859           function addParameter(id, type, config, location) {
29860             paramNames.push(id);
29861             if (parentParams[id]) return parentParams[id];
29862             if (!/^\w+(-+\w+)*(?:\[\])?$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
29863             if (params[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
29864             params[id] = new $$UMFP.Param(id, type, config, location);
29865             return params[id];
29866           }
29867
29868           function quoteRegExp(string, pattern, squash, optional) {
29869             var surroundPattern = ['',''], result = string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
29870             if (!pattern) return result;
29871             switch(squash) {
29872               case false: surroundPattern = ['(', ')' + (optional ? "?" : "")]; break;
29873               case true:  surroundPattern = ['?(', ')?']; break;
29874               default:    surroundPattern = ['(' + squash + "|", ')?']; break;
29875             }
29876             return result + surroundPattern[0] + pattern + surroundPattern[1];
29877           }
29878
29879           this.source = pattern;
29880
29881           // Split into static segments separated by path parameter placeholders.
29882           // The number of segments is always 1 more than the number of parameters.
29883           function matchDetails(m, isSearch) {
29884             var id, regexp, segment, type, cfg, arrayMode;
29885             id          = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
29886             cfg         = config.params[id];
29887             segment     = pattern.substring(last, m.index);
29888             regexp      = isSearch ? m[4] : m[4] || (m[1] == '*' ? '.*' : null);
29889             type        = $$UMFP.type(regexp || "string") || inherit($$UMFP.type("string"), { pattern: new RegExp(regexp, config.caseInsensitive ? 'i' : undefined) });
29890             return {
29891               id: id, regexp: regexp, segment: segment, type: type, cfg: cfg
29892             };
29893           }
29894
29895           var p, param, segment;
29896           while ((m = placeholder.exec(pattern))) {
29897             p = matchDetails(m, false);
29898             if (p.segment.indexOf('?') >= 0) break; // we're into the search part
29899
29900             param = addParameter(p.id, p.type, p.cfg, "path");
29901             compiled += quoteRegExp(p.segment, param.type.pattern.source, param.squash, param.isOptional);
29902             segments.push(p.segment);
29903             last = placeholder.lastIndex;
29904           }
29905           segment = pattern.substring(last);
29906
29907           // Find any search parameter names and remove them from the last segment
29908           var i = segment.indexOf('?');
29909
29910           if (i >= 0) {
29911             var search = this.sourceSearch = segment.substring(i);
29912             segment = segment.substring(0, i);
29913             this.sourcePath = pattern.substring(0, last + i);
29914
29915             if (search.length > 0) {
29916               last = 0;
29917               while ((m = searchPlaceholder.exec(search))) {
29918                 p = matchDetails(m, true);
29919                 param = addParameter(p.id, p.type, p.cfg, "search");
29920                 last = placeholder.lastIndex;
29921                 // check if ?&
29922               }
29923             }
29924           } else {
29925             this.sourcePath = pattern;
29926             this.sourceSearch = '';
29927           }
29928
29929           compiled += quoteRegExp(segment) + (config.strict === false ? '\/?' : '') + '$';
29930           segments.push(segment);
29931
29932           this.regexp = new RegExp(compiled, config.caseInsensitive ? 'i' : undefined);
29933           this.prefix = segments[0];
29934           this.$$paramNames = paramNames;
29935         }
29936
29937         /**
29938          * @ngdoc function
29939          * @name ui.router.util.type:UrlMatcher#concat
29940          * @methodOf ui.router.util.type:UrlMatcher
29941          *
29942          * @description
29943          * Returns a new matcher for a pattern constructed by appending the path part and adding the
29944          * search parameters of the specified pattern to this pattern. The current pattern is not
29945          * modified. This can be understood as creating a pattern for URLs that are relative to (or
29946          * suffixes of) the current pattern.
29947          *
29948          * @example
29949          * The following two matchers are equivalent:
29950          * <pre>
29951          * new UrlMatcher('/user/{id}?q').concat('/details?date');
29952          * new UrlMatcher('/user/{id}/details?q&date');
29953          * </pre>
29954          *
29955          * @param {string} pattern  The pattern to append.
29956          * @param {Object} config  An object hash of the configuration for the matcher.
29957          * @returns {UrlMatcher}  A matcher for the concatenated pattern.
29958          */
29959         UrlMatcher.prototype.concat = function (pattern, config) {
29960           // Because order of search parameters is irrelevant, we can add our own search
29961           // parameters to the end of the new pattern. Parse the new pattern by itself
29962           // and then join the bits together, but it's much easier to do this on a string level.
29963           var defaultConfig = {
29964             caseInsensitive: $$UMFP.caseInsensitive(),
29965             strict: $$UMFP.strictMode(),
29966             squash: $$UMFP.defaultSquashPolicy()
29967           };
29968           return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch, extend(defaultConfig, config), this);
29969         };
29970
29971         UrlMatcher.prototype.toString = function () {
29972           return this.source;
29973         };
29974
29975         /**
29976          * @ngdoc function
29977          * @name ui.router.util.type:UrlMatcher#exec
29978          * @methodOf ui.router.util.type:UrlMatcher
29979          *
29980          * @description
29981          * Tests the specified path against this matcher, and returns an object containing the captured
29982          * parameter values, or null if the path does not match. The returned object contains the values
29983          * of any search parameters that are mentioned in the pattern, but their value may be null if
29984          * they are not present in `searchParams`. This means that search parameters are always treated
29985          * as optional.
29986          *
29987          * @example
29988          * <pre>
29989          * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', {
29990          *   x: '1', q: 'hello'
29991          * });
29992          * // returns { id: 'bob', q: 'hello', r: null }
29993          * </pre>
29994          *
29995          * @param {string} path  The URL path to match, e.g. `$location.path()`.
29996          * @param {Object} searchParams  URL search parameters, e.g. `$location.search()`.
29997          * @returns {Object}  The captured parameter values.
29998          */
29999         UrlMatcher.prototype.exec = function (path, searchParams) {
30000           var m = this.regexp.exec(path);
30001           if (!m) return null;
30002           searchParams = searchParams || {};
30003
30004           var paramNames = this.parameters(), nTotal = paramNames.length,
30005             nPath = this.segments.length - 1,
30006             values = {}, i, j, cfg, paramName;
30007
30008           if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
30009
30010           function decodePathArray(string) {
30011             function reverseString(str) { return str.split("").reverse().join(""); }
30012             function unquoteDashes(str) { return str.replace(/\\-/g, "-"); }
30013
30014             var split = reverseString(string).split(/-(?!\\)/);
30015             var allReversed = map(split, reverseString);
30016             return map(allReversed, unquoteDashes).reverse();
30017           }
30018
30019           for (i = 0; i < nPath; i++) {
30020             paramName = paramNames[i];
30021             var param = this.params[paramName];
30022             var paramVal = m[i+1];
30023             // if the param value matches a pre-replace pair, replace the value before decoding.
30024             for (j = 0; j < param.replace; j++) {
30025               if (param.replace[j].from === paramVal) paramVal = param.replace[j].to;
30026             }
30027             if (paramVal && param.array === true) paramVal = decodePathArray(paramVal);
30028             values[paramName] = param.value(paramVal);
30029           }
30030           for (/**/; i < nTotal; i++) {
30031             paramName = paramNames[i];
30032             values[paramName] = this.params[paramName].value(searchParams[paramName]);
30033           }
30034
30035           return values;
30036         };
30037
30038         /**
30039          * @ngdoc function
30040          * @name ui.router.util.type:UrlMatcher#parameters
30041          * @methodOf ui.router.util.type:UrlMatcher
30042          *
30043          * @description
30044          * Returns the names of all path and search parameters of this pattern in an unspecified order.
30045          *
30046          * @returns {Array.<string>}  An array of parameter names. Must be treated as read-only. If the
30047          *    pattern has no parameters, an empty array is returned.
30048          */
30049         UrlMatcher.prototype.parameters = function (param) {
30050           if (!isDefined(param)) return this.$$paramNames;
30051           return this.params[param] || null;
30052         };
30053
30054         /**
30055          * @ngdoc function
30056          * @name ui.router.util.type:UrlMatcher#validate
30057          * @methodOf ui.router.util.type:UrlMatcher
30058          *
30059          * @description
30060          * Checks an object hash of parameters to validate their correctness according to the parameter
30061          * types of this `UrlMatcher`.
30062          *
30063          * @param {Object} params The object hash of parameters to validate.
30064          * @returns {boolean} Returns `true` if `params` validates, otherwise `false`.
30065          */
30066         UrlMatcher.prototype.validates = function (params) {
30067           return this.params.$$validates(params);
30068         };
30069
30070         /**
30071          * @ngdoc function
30072          * @name ui.router.util.type:UrlMatcher#format
30073          * @methodOf ui.router.util.type:UrlMatcher
30074          *
30075          * @description
30076          * Creates a URL that matches this pattern by substituting the specified values
30077          * for the path and search parameters. Null values for path parameters are
30078          * treated as empty strings.
30079          *
30080          * @example
30081          * <pre>
30082          * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
30083          * // returns '/user/bob?q=yes'
30084          * </pre>
30085          *
30086          * @param {Object} values  the values to substitute for the parameters in this pattern.
30087          * @returns {string}  the formatted URL (path and optionally search part).
30088          */
30089         UrlMatcher.prototype.format = function (values) {
30090           values = values || {};
30091           var segments = this.segments, params = this.parameters(), paramset = this.params;
30092           if (!this.validates(values)) return null;
30093
30094           var i, search = false, nPath = segments.length - 1, nTotal = params.length, result = segments[0];
30095
30096           function encodeDashes(str) { // Replace dashes with encoded "\-"
30097             return encodeURIComponent(str).replace(/-/g, function(c) { return '%5C%' + c.charCodeAt(0).toString(16).toUpperCase(); });
30098           }
30099
30100           for (i = 0; i < nTotal; i++) {
30101             var isPathParam = i < nPath;
30102             var name = params[i], param = paramset[name], value = param.value(values[name]);
30103             var isDefaultValue = param.isOptional && param.type.equals(param.value(), value);
30104             var squash = isDefaultValue ? param.squash : false;
30105             var encoded = param.type.encode(value);
30106
30107             if (isPathParam) {
30108               var nextSegment = segments[i + 1];
30109               if (squash === false) {
30110                 if (encoded != null) {
30111                   if (isArray(encoded)) {
30112                     result += map(encoded, encodeDashes).join("-");
30113                   } else {
30114                     result += encodeURIComponent(encoded);
30115                   }
30116                 }
30117                 result += nextSegment;
30118               } else if (squash === true) {
30119                 var capture = result.match(/\/$/) ? /\/?(.*)/ : /(.*)/;
30120                 result += nextSegment.match(capture)[1];
30121               } else if (isString(squash)) {
30122                 result += squash + nextSegment;
30123               }
30124             } else {
30125               if (encoded == null || (isDefaultValue && squash !== false)) continue;
30126               if (!isArray(encoded)) encoded = [ encoded ];
30127               encoded = map(encoded, encodeURIComponent).join('&' + name + '=');
30128               result += (search ? '&' : '?') + (name + '=' + encoded);
30129               search = true;
30130             }
30131           }
30132
30133           return result;
30134         };
30135
30136         /**
30137          * @ngdoc object
30138          * @name ui.router.util.type:Type
30139          *
30140          * @description
30141          * Implements an interface to define custom parameter types that can be decoded from and encoded to
30142          * string parameters matched in a URL. Used by {@link ui.router.util.type:UrlMatcher `UrlMatcher`}
30143          * objects when matching or formatting URLs, or comparing or validating parameter values.
30144          *
30145          * See {@link ui.router.util.$urlMatcherFactory#methods_type `$urlMatcherFactory#type()`} for more
30146          * information on registering custom types.
30147          *
30148          * @param {Object} config  A configuration object which contains the custom type definition.  The object's
30149          *        properties will override the default methods and/or pattern in `Type`'s public interface.
30150          * @example
30151          * <pre>
30152          * {
30153          *   decode: function(val) { return parseInt(val, 10); },
30154          *   encode: function(val) { return val && val.toString(); },
30155          *   equals: function(a, b) { return this.is(a) && a === b; },
30156          *   is: function(val) { return angular.isNumber(val) isFinite(val) && val % 1 === 0; },
30157          *   pattern: /\d+/
30158          * }
30159          * </pre>
30160          *
30161          * @property {RegExp} pattern The regular expression pattern used to match values of this type when
30162          *           coming from a substring of a URL.
30163          *
30164          * @returns {Object}  Returns a new `Type` object.
30165          */
30166         function Type(config) {
30167           extend(this, config);
30168         }
30169
30170         /**
30171          * @ngdoc function
30172          * @name ui.router.util.type:Type#is
30173          * @methodOf ui.router.util.type:Type
30174          *
30175          * @description
30176          * Detects whether a value is of a particular type. Accepts a native (decoded) value
30177          * and determines whether it matches the current `Type` object.
30178          *
30179          * @param {*} val  The value to check.
30180          * @param {string} key  Optional. If the type check is happening in the context of a specific
30181          *        {@link ui.router.util.type:UrlMatcher `UrlMatcher`} object, this is the name of the
30182          *        parameter in which `val` is stored. Can be used for meta-programming of `Type` objects.
30183          * @returns {Boolean}  Returns `true` if the value matches the type, otherwise `false`.
30184          */
30185         Type.prototype.is = function(val, key) {
30186           return true;
30187         };
30188
30189         /**
30190          * @ngdoc function
30191          * @name ui.router.util.type:Type#encode
30192          * @methodOf ui.router.util.type:Type
30193          *
30194          * @description
30195          * Encodes a custom/native type value to a string that can be embedded in a URL. Note that the
30196          * return value does *not* need to be URL-safe (i.e. passed through `encodeURIComponent()`), it
30197          * only needs to be a representation of `val` that has been coerced to a string.
30198          *
30199          * @param {*} val  The value to encode.
30200          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30201          *        meta-programming of `Type` objects.
30202          * @returns {string}  Returns a string representation of `val` that can be encoded in a URL.
30203          */
30204         Type.prototype.encode = function(val, key) {
30205           return val;
30206         };
30207
30208         /**
30209          * @ngdoc function
30210          * @name ui.router.util.type:Type#decode
30211          * @methodOf ui.router.util.type:Type
30212          *
30213          * @description
30214          * Converts a parameter value (from URL string or transition param) to a custom/native value.
30215          *
30216          * @param {string} val  The URL parameter value to decode.
30217          * @param {string} key  The name of the parameter in which `val` is stored. Can be used for
30218          *        meta-programming of `Type` objects.
30219          * @returns {*}  Returns a custom representation of the URL parameter value.
30220          */
30221         Type.prototype.decode = function(val, key) {
30222           return val;
30223         };
30224
30225         /**
30226          * @ngdoc function
30227          * @name ui.router.util.type:Type#equals
30228          * @methodOf ui.router.util.type:Type
30229          *
30230          * @description
30231          * Determines whether two decoded values are equivalent.
30232          *
30233          * @param {*} a  A value to compare against.
30234          * @param {*} b  A value to compare against.
30235          * @returns {Boolean}  Returns `true` if the values are equivalent/equal, otherwise `false`.
30236          */
30237         Type.prototype.equals = function(a, b) {
30238           return a == b;
30239         };
30240
30241         Type.prototype.$subPattern = function() {
30242           var sub = this.pattern.toString();
30243           return sub.substr(1, sub.length - 2);
30244         };
30245
30246         Type.prototype.pattern = /.*/;
30247
30248         Type.prototype.toString = function() { return "{Type:" + this.name + "}"; };
30249
30250         /** Given an encoded string, or a decoded object, returns a decoded object */
30251         Type.prototype.$normalize = function(val) {
30252           return this.is(val) ? val : this.decode(val);
30253         };
30254
30255         /*
30256          * Wraps an existing custom Type as an array of Type, depending on 'mode'.
30257          * e.g.:
30258          * - urlmatcher pattern "/path?{queryParam[]:int}"
30259          * - url: "/path?queryParam=1&queryParam=2
30260          * - $stateParams.queryParam will be [1, 2]
30261          * if `mode` is "auto", then
30262          * - url: "/path?queryParam=1 will create $stateParams.queryParam: 1
30263          * - url: "/path?queryParam=1&queryParam=2 will create $stateParams.queryParam: [1, 2]
30264          */
30265         Type.prototype.$asArray = function(mode, isSearch) {
30266           if (!mode) return this;
30267           if (mode === "auto" && !isSearch) throw new Error("'auto' array mode is for query parameters only");
30268
30269           function ArrayType(type, mode) {
30270             function bindTo(type, callbackName) {
30271               return function() {
30272                 return type[callbackName].apply(type, arguments);
30273               };
30274             }
30275
30276             // Wrap non-array value as array
30277             function arrayWrap(val) { return isArray(val) ? val : (isDefined(val) ? [ val ] : []); }
30278             // Unwrap array value for "auto" mode. Return undefined for empty array.
30279             function arrayUnwrap(val) {
30280               switch(val.length) {
30281                 case 0: return undefined;
30282                 case 1: return mode === "auto" ? val[0] : val;
30283                 default: return val;
30284               }
30285             }
30286             function falsey(val) { return !val; }
30287
30288             // Wraps type (.is/.encode/.decode) functions to operate on each value of an array
30289             function arrayHandler(callback, allTruthyMode) {
30290               return function handleArray(val) {
30291                 val = arrayWrap(val);
30292                 var result = map(val, callback);
30293                 if (allTruthyMode === true)
30294                   return filter(result, falsey).length === 0;
30295                 return arrayUnwrap(result);
30296               };
30297             }
30298
30299             // Wraps type (.equals) functions to operate on each value of an array
30300             function arrayEqualsHandler(callback) {
30301               return function handleArray(val1, val2) {
30302                 var left = arrayWrap(val1), right = arrayWrap(val2);
30303                 if (left.length !== right.length) return false;
30304                 for (var i = 0; i < left.length; i++) {
30305                   if (!callback(left[i], right[i])) return false;
30306                 }
30307                 return true;
30308               };
30309             }
30310
30311             this.encode = arrayHandler(bindTo(type, 'encode'));
30312             this.decode = arrayHandler(bindTo(type, 'decode'));
30313             this.is     = arrayHandler(bindTo(type, 'is'), true);
30314             this.equals = arrayEqualsHandler(bindTo(type, 'equals'));
30315             this.pattern = type.pattern;
30316             this.$normalize = arrayHandler(bindTo(type, '$normalize'));
30317             this.name = type.name;
30318             this.$arrayMode = mode;
30319           }
30320
30321           return new ArrayType(this, mode);
30322         };
30323
30324
30325
30326         /**
30327          * @ngdoc object
30328          * @name ui.router.util.$urlMatcherFactory
30329          *
30330          * @description
30331          * Factory for {@link ui.router.util.type:UrlMatcher `UrlMatcher`} instances. The factory
30332          * is also available to providers under the name `$urlMatcherFactoryProvider`.
30333          */
30334         function $UrlMatcherFactory() {
30335           $$UMFP = this;
30336
30337           var isCaseInsensitive = false, isStrictMode = true, defaultSquashPolicy = false;
30338
30339           function valToString(val) { return val != null ? val.toString().replace(/\//g, "%2F") : val; }
30340           function valFromString(val) { return val != null ? val.toString().replace(/%2F/g, "/") : val; }
30341
30342           var $types = {}, enqueue = true, typeQueue = [], injector, defaultTypes = {
30343             string: {
30344               encode: valToString,
30345               decode: valFromString,
30346               // TODO: in 1.0, make string .is() return false if value is undefined/null by default.
30347               // In 0.2.x, string params are optional by default for backwards compat
30348               is: function(val) { return val == null || !isDefined(val) || typeof val === "string"; },
30349               pattern: /[^/]*/
30350             },
30351             int: {
30352               encode: valToString,
30353               decode: function(val) { return parseInt(val, 10); },
30354               is: function(val) { return isDefined(val) && this.decode(val.toString()) === val; },
30355               pattern: /\d+/
30356             },
30357             bool: {
30358               encode: function(val) { return val ? 1 : 0; },
30359               decode: function(val) { return parseInt(val, 10) !== 0; },
30360               is: function(val) { return val === true || val === false; },
30361               pattern: /0|1/
30362             },
30363             date: {
30364               encode: function (val) {
30365                 if (!this.is(val))
30366                   return undefined;
30367                 return [ val.getFullYear(),
30368                   ('0' + (val.getMonth() + 1)).slice(-2),
30369                   ('0' + val.getDate()).slice(-2)
30370                 ].join("-");
30371               },
30372               decode: function (val) {
30373                 if (this.is(val)) return val;
30374                 var match = this.capture.exec(val);
30375                 return match ? new Date(match[1], match[2] - 1, match[3]) : undefined;
30376               },
30377               is: function(val) { return val instanceof Date && !isNaN(val.valueOf()); },
30378               equals: function (a, b) { return this.is(a) && this.is(b) && a.toISOString() === b.toISOString(); },
30379               pattern: /[0-9]{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2][0-9]|3[0-1])/,
30380               capture: /([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])/
30381             },
30382             json: {
30383               encode: angular.toJson,
30384               decode: angular.fromJson,
30385               is: angular.isObject,
30386               equals: angular.equals,
30387               pattern: /[^/]*/
30388             },
30389             any: { // does not encode/decode
30390               encode: angular.identity,
30391               decode: angular.identity,
30392               equals: angular.equals,
30393               pattern: /.*/
30394             }
30395           };
30396
30397           function getDefaultConfig() {
30398             return {
30399               strict: isStrictMode,
30400               caseInsensitive: isCaseInsensitive
30401             };
30402           }
30403
30404           function isInjectable(value) {
30405             return (isFunction(value) || (isArray(value) && isFunction(value[value.length - 1])));
30406           }
30407
30408           /**
30409            * [Internal] Get the default value of a parameter, which may be an injectable function.
30410            */
30411           $UrlMatcherFactory.$$getDefaultValue = function(config) {
30412             if (!isInjectable(config.value)) return config.value;
30413             if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30414             return injector.invoke(config.value);
30415           };
30416
30417           /**
30418            * @ngdoc function
30419            * @name ui.router.util.$urlMatcherFactory#caseInsensitive
30420            * @methodOf ui.router.util.$urlMatcherFactory
30421            *
30422            * @description
30423            * Defines whether URL matching should be case sensitive (the default behavior), or not.
30424            *
30425            * @param {boolean} value `false` to match URL in a case sensitive manner; otherwise `true`;
30426            * @returns {boolean} the current value of caseInsensitive
30427            */
30428           this.caseInsensitive = function(value) {
30429             if (isDefined(value))
30430               isCaseInsensitive = value;
30431             return isCaseInsensitive;
30432           };
30433
30434           /**
30435            * @ngdoc function
30436            * @name ui.router.util.$urlMatcherFactory#strictMode
30437            * @methodOf ui.router.util.$urlMatcherFactory
30438            *
30439            * @description
30440            * Defines whether URLs should match trailing slashes, or not (the default behavior).
30441            *
30442            * @param {boolean=} value `false` to match trailing slashes in URLs, otherwise `true`.
30443            * @returns {boolean} the current value of strictMode
30444            */
30445           this.strictMode = function(value) {
30446             if (isDefined(value))
30447               isStrictMode = value;
30448             return isStrictMode;
30449           };
30450
30451           /**
30452            * @ngdoc function
30453            * @name ui.router.util.$urlMatcherFactory#defaultSquashPolicy
30454            * @methodOf ui.router.util.$urlMatcherFactory
30455            *
30456            * @description
30457            * Sets the default behavior when generating or matching URLs with default parameter values.
30458            *
30459            * @param {string} value A string that defines the default parameter URL squashing behavior.
30460            *    `nosquash`: When generating an href with a default parameter value, do not squash the parameter value from the URL
30461            *    `slash`: When generating an href with a default parameter value, squash (remove) the parameter value, and, if the
30462            *             parameter is surrounded by slashes, squash (remove) one slash from the URL
30463            *    any other string, e.g. "~": When generating an href with a default parameter value, squash (remove)
30464            *             the parameter value from the URL and replace it with this string.
30465            */
30466           this.defaultSquashPolicy = function(value) {
30467             if (!isDefined(value)) return defaultSquashPolicy;
30468             if (value !== true && value !== false && !isString(value))
30469               throw new Error("Invalid squash policy: " + value + ". Valid policies: false, true, arbitrary-string");
30470             defaultSquashPolicy = value;
30471             return value;
30472           };
30473
30474           /**
30475            * @ngdoc function
30476            * @name ui.router.util.$urlMatcherFactory#compile
30477            * @methodOf ui.router.util.$urlMatcherFactory
30478            *
30479            * @description
30480            * Creates a {@link ui.router.util.type:UrlMatcher `UrlMatcher`} for the specified pattern.
30481            *
30482            * @param {string} pattern  The URL pattern.
30483            * @param {Object} config  The config object hash.
30484            * @returns {UrlMatcher}  The UrlMatcher.
30485            */
30486           this.compile = function (pattern, config) {
30487             return new UrlMatcher(pattern, extend(getDefaultConfig(), config));
30488           };
30489
30490           /**
30491            * @ngdoc function
30492            * @name ui.router.util.$urlMatcherFactory#isMatcher
30493            * @methodOf ui.router.util.$urlMatcherFactory
30494            *
30495            * @description
30496            * Returns true if the specified object is a `UrlMatcher`, or false otherwise.
30497            *
30498            * @param {Object} object  The object to perform the type check against.
30499            * @returns {Boolean}  Returns `true` if the object matches the `UrlMatcher` interface, by
30500            *          implementing all the same methods.
30501            */
30502           this.isMatcher = function (o) {
30503             if (!isObject(o)) return false;
30504             var result = true;
30505
30506             forEach(UrlMatcher.prototype, function(val, name) {
30507               if (isFunction(val)) {
30508                 result = result && (isDefined(o[name]) && isFunction(o[name]));
30509               }
30510             });
30511             return result;
30512           };
30513
30514           /**
30515            * @ngdoc function
30516            * @name ui.router.util.$urlMatcherFactory#type
30517            * @methodOf ui.router.util.$urlMatcherFactory
30518            *
30519            * @description
30520            * Registers a custom {@link ui.router.util.type:Type `Type`} object that can be used to
30521            * generate URLs with typed parameters.
30522            *
30523            * @param {string} name  The type name.
30524            * @param {Object|Function} definition   The type definition. See
30525            *        {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30526            * @param {Object|Function} definitionFn (optional) A function that is injected before the app
30527            *        runtime starts.  The result of this function is merged into the existing `definition`.
30528            *        See {@link ui.router.util.type:Type `Type`} for information on the values accepted.
30529            *
30530            * @returns {Object}  Returns `$urlMatcherFactoryProvider`.
30531            *
30532            * @example
30533            * This is a simple example of a custom type that encodes and decodes items from an
30534            * array, using the array index as the URL-encoded value:
30535            *
30536            * <pre>
30537            * var list = ['John', 'Paul', 'George', 'Ringo'];
30538            *
30539            * $urlMatcherFactoryProvider.type('listItem', {
30540            *   encode: function(item) {
30541            *     // Represent the list item in the URL using its corresponding index
30542            *     return list.indexOf(item);
30543            *   },
30544            *   decode: function(item) {
30545            *     // Look up the list item by index
30546            *     return list[parseInt(item, 10)];
30547            *   },
30548            *   is: function(item) {
30549            *     // Ensure the item is valid by checking to see that it appears
30550            *     // in the list
30551            *     return list.indexOf(item) > -1;
30552            *   }
30553            * });
30554            *
30555            * $stateProvider.state('list', {
30556            *   url: "/list/{item:listItem}",
30557            *   controller: function($scope, $stateParams) {
30558            *     console.log($stateParams.item);
30559            *   }
30560            * });
30561            *
30562            * // ...
30563            *
30564            * // Changes URL to '/list/3', logs "Ringo" to the console
30565            * $state.go('list', { item: "Ringo" });
30566            * </pre>
30567            *
30568            * This is a more complex example of a type that relies on dependency injection to
30569            * interact with services, and uses the parameter name from the URL to infer how to
30570            * handle encoding and decoding parameter values:
30571            *
30572            * <pre>
30573            * // Defines a custom type that gets a value from a service,
30574            * // where each service gets different types of values from
30575            * // a backend API:
30576            * $urlMatcherFactoryProvider.type('dbObject', {}, function(Users, Posts) {
30577            *
30578            *   // Matches up services to URL parameter names
30579            *   var services = {
30580            *     user: Users,
30581            *     post: Posts
30582            *   };
30583            *
30584            *   return {
30585            *     encode: function(object) {
30586            *       // Represent the object in the URL using its unique ID
30587            *       return object.id;
30588            *     },
30589            *     decode: function(value, key) {
30590            *       // Look up the object by ID, using the parameter
30591            *       // name (key) to call the correct service
30592            *       return services[key].findById(value);
30593            *     },
30594            *     is: function(object, key) {
30595            *       // Check that object is a valid dbObject
30596            *       return angular.isObject(object) && object.id && services[key];
30597            *     }
30598            *     equals: function(a, b) {
30599            *       // Check the equality of decoded objects by comparing
30600            *       // their unique IDs
30601            *       return a.id === b.id;
30602            *     }
30603            *   };
30604            * });
30605            *
30606            * // In a config() block, you can then attach URLs with
30607            * // type-annotated parameters:
30608            * $stateProvider.state('users', {
30609            *   url: "/users",
30610            *   // ...
30611            * }).state('users.item', {
30612            *   url: "/{user:dbObject}",
30613            *   controller: function($scope, $stateParams) {
30614            *     // $stateParams.user will now be an object returned from
30615            *     // the Users service
30616            *   },
30617            *   // ...
30618            * });
30619            * </pre>
30620            */
30621           this.type = function (name, definition, definitionFn) {
30622             if (!isDefined(definition)) return $types[name];
30623             if ($types.hasOwnProperty(name)) throw new Error("A type named '" + name + "' has already been defined.");
30624
30625             $types[name] = new Type(extend({ name: name }, definition));
30626             if (definitionFn) {
30627               typeQueue.push({ name: name, def: definitionFn });
30628               if (!enqueue) flushTypeQueue();
30629             }
30630             return this;
30631           };
30632
30633           // `flushTypeQueue()` waits until `$urlMatcherFactory` is injected before invoking the queued `definitionFn`s
30634           function flushTypeQueue() {
30635             while(typeQueue.length) {
30636               var type = typeQueue.shift();
30637               if (type.pattern) throw new Error("You cannot override a type's .pattern at runtime.");
30638               angular.extend($types[type.name], injector.invoke(type.def));
30639             }
30640           }
30641
30642           // Register default types. Store them in the prototype of $types.
30643           forEach(defaultTypes, function(type, name) { $types[name] = new Type(extend({name: name}, type)); });
30644           $types = inherit($types, {});
30645
30646           /* No need to document $get, since it returns this */
30647           this.$get = ['$injector', function ($injector) {
30648             injector = $injector;
30649             enqueue = false;
30650             flushTypeQueue();
30651
30652             forEach(defaultTypes, function(type, name) {
30653               if (!$types[name]) $types[name] = new Type(type);
30654             });
30655             return this;
30656           }];
30657
30658           this.Param = function Param(id, type, config, location) {
30659             var self = this;
30660             config = unwrapShorthand(config);
30661             type = getType(config, type, location);
30662             var arrayMode = getArrayMode();
30663             type = arrayMode ? type.$asArray(arrayMode, location === "search") : type;
30664             if (type.name === "string" && !arrayMode && location === "path" && config.value === undefined)
30665               config.value = ""; // for 0.2.x; in 0.3.0+ do not automatically default to ""
30666             var isOptional = config.value !== undefined;
30667             var squash = getSquashPolicy(config, isOptional);
30668             var replace = getReplace(config, arrayMode, isOptional, squash);
30669
30670             function unwrapShorthand(config) {
30671               var keys = isObject(config) ? objectKeys(config) : [];
30672               var isShorthand = indexOf(keys, "value") === -1 && indexOf(keys, "type") === -1 &&
30673                                 indexOf(keys, "squash") === -1 && indexOf(keys, "array") === -1;
30674               if (isShorthand) config = { value: config };
30675               config.$$fn = isInjectable(config.value) ? config.value : function () { return config.value; };
30676               return config;
30677             }
30678
30679             function getType(config, urlType, location) {
30680               if (config.type && urlType) throw new Error("Param '"+id+"' has two type configurations.");
30681               if (urlType) return urlType;
30682               if (!config.type) return (location === "config" ? $types.any : $types.string);
30683               return config.type instanceof Type ? config.type : new Type(config.type);
30684             }
30685
30686             // array config: param name (param[]) overrides default settings.  explicit config overrides param name.
30687             function getArrayMode() {
30688               var arrayDefaults = { array: (location === "search" ? "auto" : false) };
30689               var arrayParamNomenclature = id.match(/\[\]$/) ? { array: true } : {};
30690               return extend(arrayDefaults, arrayParamNomenclature, config).array;
30691             }
30692
30693             /**
30694              * returns false, true, or the squash value to indicate the "default parameter url squash policy".
30695              */
30696             function getSquashPolicy(config, isOptional) {
30697               var squash = config.squash;
30698               if (!isOptional || squash === false) return false;
30699               if (!isDefined(squash) || squash == null) return defaultSquashPolicy;
30700               if (squash === true || isString(squash)) return squash;
30701               throw new Error("Invalid squash policy: '" + squash + "'. Valid policies: false, true, or arbitrary string");
30702             }
30703
30704             function getReplace(config, arrayMode, isOptional, squash) {
30705               var replace, configuredKeys, defaultPolicy = [
30706                 { from: "",   to: (isOptional || arrayMode ? undefined : "") },
30707                 { from: null, to: (isOptional || arrayMode ? undefined : "") }
30708               ];
30709               replace = isArray(config.replace) ? config.replace : [];
30710               if (isString(squash))
30711                 replace.push({ from: squash, to: undefined });
30712               configuredKeys = map(replace, function(item) { return item.from; } );
30713               return filter(defaultPolicy, function(item) { return indexOf(configuredKeys, item.from) === -1; }).concat(replace);
30714             }
30715
30716             /**
30717              * [Internal] Get the default value of a parameter, which may be an injectable function.
30718              */
30719             function $$getDefaultValue() {
30720               if (!injector) throw new Error("Injectable functions cannot be called at configuration time");
30721               var defaultValue = injector.invoke(config.$$fn);
30722               if (defaultValue !== null && defaultValue !== undefined && !self.type.is(defaultValue))
30723                 throw new Error("Default value (" + defaultValue + ") for parameter '" + self.id + "' is not an instance of Type (" + self.type.name + ")");
30724               return defaultValue;
30725             }
30726
30727             /**
30728              * [Internal] Gets the decoded representation of a value if the value is defined, otherwise, returns the
30729              * default value, which may be the result of an injectable function.
30730              */
30731             function $value(value) {
30732               function hasReplaceVal(val) { return function(obj) { return obj.from === val; }; }
30733               function $replace(value) {
30734                 var replacement = map(filter(self.replace, hasReplaceVal(value)), function(obj) { return obj.to; });
30735                 return replacement.length ? replacement[0] : value;
30736               }
30737               value = $replace(value);
30738               return !isDefined(value) ? $$getDefaultValue() : self.type.$normalize(value);
30739             }
30740
30741             function toString() { return "{Param:" + id + " " + type + " squash: '" + squash + "' optional: " + isOptional + "}"; }
30742
30743             extend(this, {
30744               id: id,
30745               type: type,
30746               location: location,
30747               array: arrayMode,
30748               squash: squash,
30749               replace: replace,
30750               isOptional: isOptional,
30751               value: $value,
30752               dynamic: undefined,
30753               config: config,
30754               toString: toString
30755             });
30756           };
30757
30758           function ParamSet(params) {
30759             extend(this, params || {});
30760           }
30761
30762           ParamSet.prototype = {
30763             $$new: function() {
30764               return inherit(this, extend(new ParamSet(), { $$parent: this}));
30765             },
30766             $$keys: function () {
30767               var keys = [], chain = [], parent = this,
30768                 ignore = objectKeys(ParamSet.prototype);
30769               while (parent) { chain.push(parent); parent = parent.$$parent; }
30770               chain.reverse();
30771               forEach(chain, function(paramset) {
30772                 forEach(objectKeys(paramset), function(key) {
30773                     if (indexOf(keys, key) === -1 && indexOf(ignore, key) === -1) keys.push(key);
30774                 });
30775               });
30776               return keys;
30777             },
30778             $$values: function(paramValues) {
30779               var values = {}, self = this;
30780               forEach(self.$$keys(), function(key) {
30781                 values[key] = self[key].value(paramValues && paramValues[key]);
30782               });
30783               return values;
30784             },
30785             $$equals: function(paramValues1, paramValues2) {
30786               var equal = true, self = this;
30787               forEach(self.$$keys(), function(key) {
30788                 var left = paramValues1 && paramValues1[key], right = paramValues2 && paramValues2[key];
30789                 if (!self[key].type.equals(left, right)) equal = false;
30790               });
30791               return equal;
30792             },
30793             $$validates: function $$validate(paramValues) {
30794               var keys = this.$$keys(), i, param, rawVal, normalized, encoded;
30795               for (i = 0; i < keys.length; i++) {
30796                 param = this[keys[i]];
30797                 rawVal = paramValues[keys[i]];
30798                 if ((rawVal === undefined || rawVal === null) && param.isOptional)
30799                   break; // There was no parameter value, but the param is optional
30800                 normalized = param.type.$normalize(rawVal);
30801                 if (!param.type.is(normalized))
30802                   return false; // The value was not of the correct Type, and could not be decoded to the correct Type
30803                 encoded = param.type.encode(normalized);
30804                 if (angular.isString(encoded) && !param.type.pattern.exec(encoded))
30805                   return false; // The value was of the correct type, but when encoded, did not match the Type's regexp
30806               }
30807               return true;
30808             },
30809             $$parent: undefined
30810           };
30811
30812           this.ParamSet = ParamSet;
30813         }
30814
30815         // Register as a provider so it's available to other providers
30816         angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
30817         angular.module('ui.router.util').run(['$urlMatcherFactory', function($urlMatcherFactory) { }]);
30818
30819         /**
30820          * @ngdoc object
30821          * @name ui.router.router.$urlRouterProvider
30822          *
30823          * @requires ui.router.util.$urlMatcherFactoryProvider
30824          * @requires $locationProvider
30825          *
30826          * @description
30827          * `$urlRouterProvider` has the responsibility of watching `$location`. 
30828          * When `$location` changes it runs through a list of rules one by one until a 
30829          * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify 
30830          * a url in a state configuration. All urls are compiled into a UrlMatcher object.
30831          *
30832          * There are several methods on `$urlRouterProvider` that make it useful to use directly
30833          * in your module config.
30834          */
30835         $UrlRouterProvider.$inject = ['$locationProvider', '$urlMatcherFactoryProvider'];
30836         function $UrlRouterProvider(   $locationProvider,   $urlMatcherFactory) {
30837           var rules = [], otherwise = null, interceptDeferred = false, listener;
30838
30839           // Returns a string that is a prefix of all strings matching the RegExp
30840           function regExpPrefix(re) {
30841             var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
30842             return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
30843           }
30844
30845           // Interpolates matched values into a String.replace()-style pattern
30846           function interpolate(pattern, match) {
30847             return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
30848               return match[what === '$' ? 0 : Number(what)];
30849             });
30850           }
30851
30852           /**
30853            * @ngdoc function
30854            * @name ui.router.router.$urlRouterProvider#rule
30855            * @methodOf ui.router.router.$urlRouterProvider
30856            *
30857            * @description
30858            * Defines rules that are used by `$urlRouterProvider` to find matches for
30859            * specific URLs.
30860            *
30861            * @example
30862            * <pre>
30863            * var app = angular.module('app', ['ui.router.router']);
30864            *
30865            * app.config(function ($urlRouterProvider) {
30866            *   // Here's an example of how you might allow case insensitive urls
30867            *   $urlRouterProvider.rule(function ($injector, $location) {
30868            *     var path = $location.path(),
30869            *         normalized = path.toLowerCase();
30870            *
30871            *     if (path !== normalized) {
30872            *       return normalized;
30873            *     }
30874            *   });
30875            * });
30876            * </pre>
30877            *
30878            * @param {object} rule Handler function that takes `$injector` and `$location`
30879            * services as arguments. You can use them to return a valid path as a string.
30880            *
30881            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30882            */
30883           this.rule = function (rule) {
30884             if (!isFunction(rule)) throw new Error("'rule' must be a function");
30885             rules.push(rule);
30886             return this;
30887           };
30888
30889           /**
30890            * @ngdoc object
30891            * @name ui.router.router.$urlRouterProvider#otherwise
30892            * @methodOf ui.router.router.$urlRouterProvider
30893            *
30894            * @description
30895            * Defines a path that is used when an invalid route is requested.
30896            *
30897            * @example
30898            * <pre>
30899            * var app = angular.module('app', ['ui.router.router']);
30900            *
30901            * app.config(function ($urlRouterProvider) {
30902            *   // if the path doesn't match any of the urls you configured
30903            *   // otherwise will take care of routing the user to the
30904            *   // specified url
30905            *   $urlRouterProvider.otherwise('/index');
30906            *
30907            *   // Example of using function rule as param
30908            *   $urlRouterProvider.otherwise(function ($injector, $location) {
30909            *     return '/a/valid/url';
30910            *   });
30911            * });
30912            * </pre>
30913            *
30914            * @param {string|object} rule The url path you want to redirect to or a function 
30915            * rule that returns the url path. The function version is passed two params: 
30916            * `$injector` and `$location` services, and must return a url string.
30917            *
30918            * @return {object} `$urlRouterProvider` - `$urlRouterProvider` instance
30919            */
30920           this.otherwise = function (rule) {
30921             if (isString(rule)) {
30922               var redirect = rule;
30923               rule = function () { return redirect; };
30924             }
30925             else if (!isFunction(rule)) throw new Error("'rule' must be a function");
30926             otherwise = rule;
30927             return this;
30928           };
30929
30930
30931           function handleIfMatch($injector, handler, match) {
30932             if (!match) return false;
30933             var result = $injector.invoke(handler, handler, { $match: match });
30934             return isDefined(result) ? result : true;
30935           }
30936
30937           /**
30938            * @ngdoc function
30939            * @name ui.router.router.$urlRouterProvider#when
30940            * @methodOf ui.router.router.$urlRouterProvider
30941            *
30942            * @description
30943            * Registers a handler for a given url matching. if handle is a string, it is
30944            * treated as a redirect, and is interpolated according to the syntax of match
30945            * (i.e. like `String.replace()` for `RegExp`, or like a `UrlMatcher` pattern otherwise).
30946            *
30947            * If the handler is a function, it is injectable. It gets invoked if `$location`
30948            * matches. You have the option of inject the match object as `$match`.
30949            *
30950            * The handler can return
30951            *
30952            * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
30953            *   will continue trying to find another one that matches.
30954            * - **string** which is treated as a redirect and passed to `$location.url()`
30955            * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
30956            *
30957            * @example
30958            * <pre>
30959            * var app = angular.module('app', ['ui.router.router']);
30960            *
30961            * app.config(function ($urlRouterProvider) {
30962            *   $urlRouterProvider.when($state.url, function ($match, $stateParams) {
30963            *     if ($state.$current.navigable !== state ||
30964            *         !equalForKeys($match, $stateParams) {
30965            *      $state.transitionTo(state, $match, false);
30966            *     }
30967            *   });
30968            * });
30969            * </pre>
30970            *
30971            * @param {string|object} what The incoming path that you want to redirect.
30972            * @param {string|object} handler The path you want to redirect your user to.
30973            */
30974           this.when = function (what, handler) {
30975             var redirect, handlerIsString = isString(handler);
30976             if (isString(what)) what = $urlMatcherFactory.compile(what);
30977
30978             if (!handlerIsString && !isFunction(handler) && !isArray(handler))
30979               throw new Error("invalid 'handler' in when()");
30980
30981             var strategies = {
30982               matcher: function (what, handler) {
30983                 if (handlerIsString) {
30984                   redirect = $urlMatcherFactory.compile(handler);
30985                   handler = ['$match', function ($match) { return redirect.format($match); }];
30986                 }
30987                 return extend(function ($injector, $location) {
30988                   return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
30989                 }, {
30990                   prefix: isString(what.prefix) ? what.prefix : ''
30991                 });
30992               },
30993               regex: function (what, handler) {
30994                 if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
30995
30996                 if (handlerIsString) {
30997                   redirect = handler;
30998                   handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
30999                 }
31000                 return extend(function ($injector, $location) {
31001                   return handleIfMatch($injector, handler, what.exec($location.path()));
31002                 }, {
31003                   prefix: regExpPrefix(what)
31004                 });
31005               }
31006             };
31007
31008             var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
31009
31010             for (var n in check) {
31011               if (check[n]) return this.rule(strategies[n](what, handler));
31012             }
31013
31014             throw new Error("invalid 'what' in when()");
31015           };
31016
31017           /**
31018            * @ngdoc function
31019            * @name ui.router.router.$urlRouterProvider#deferIntercept
31020            * @methodOf ui.router.router.$urlRouterProvider
31021            *
31022            * @description
31023            * Disables (or enables) deferring location change interception.
31024            *
31025            * If you wish to customize the behavior of syncing the URL (for example, if you wish to
31026            * defer a transition but maintain the current URL), call this method at configuration time.
31027            * Then, at run time, call `$urlRouter.listen()` after you have configured your own
31028            * `$locationChangeSuccess` event handler.
31029            *
31030            * @example
31031            * <pre>
31032            * var app = angular.module('app', ['ui.router.router']);
31033            *
31034            * app.config(function ($urlRouterProvider) {
31035            *
31036            *   // Prevent $urlRouter from automatically intercepting URL changes;
31037            *   // this allows you to configure custom behavior in between
31038            *   // location changes and route synchronization:
31039            *   $urlRouterProvider.deferIntercept();
31040            *
31041            * }).run(function ($rootScope, $urlRouter, UserService) {
31042            *
31043            *   $rootScope.$on('$locationChangeSuccess', function(e) {
31044            *     // UserService is an example service for managing user state
31045            *     if (UserService.isLoggedIn()) return;
31046            *
31047            *     // Prevent $urlRouter's default handler from firing
31048            *     e.preventDefault();
31049            *
31050            *     UserService.handleLogin().then(function() {
31051            *       // Once the user has logged in, sync the current URL
31052            *       // to the router:
31053            *       $urlRouter.sync();
31054            *     });
31055            *   });
31056            *
31057            *   // Configures $urlRouter's listener *after* your custom listener
31058            *   $urlRouter.listen();
31059            * });
31060            * </pre>
31061            *
31062            * @param {boolean} defer Indicates whether to defer location change interception. Passing
31063                     no parameter is equivalent to `true`.
31064            */
31065           this.deferIntercept = function (defer) {
31066             if (defer === undefined) defer = true;
31067             interceptDeferred = defer;
31068           };
31069
31070           /**
31071            * @ngdoc object
31072            * @name ui.router.router.$urlRouter
31073            *
31074            * @requires $location
31075            * @requires $rootScope
31076            * @requires $injector
31077            * @requires $browser
31078            *
31079            * @description
31080            *
31081            */
31082           this.$get = $get;
31083           $get.$inject = ['$location', '$rootScope', '$injector', '$browser'];
31084           function $get(   $location,   $rootScope,   $injector,   $browser) {
31085
31086             var baseHref = $browser.baseHref(), location = $location.url(), lastPushedUrl;
31087
31088             function appendBasePath(url, isHtml5, absolute) {
31089               if (baseHref === '/') return url;
31090               if (isHtml5) return baseHref.slice(0, -1) + url;
31091               if (absolute) return baseHref.slice(1) + url;
31092               return url;
31093             }
31094
31095             // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
31096             function update(evt) {
31097               if (evt && evt.defaultPrevented) return;
31098               var ignoreUpdate = lastPushedUrl && $location.url() === lastPushedUrl;
31099               lastPushedUrl = undefined;
31100               // TODO: Re-implement this in 1.0 for https://github.com/angular-ui/ui-router/issues/1573
31101               //if (ignoreUpdate) return true;
31102
31103               function check(rule) {
31104                 var handled = rule($injector, $location);
31105
31106                 if (!handled) return false;
31107                 if (isString(handled)) $location.replace().url(handled);
31108                 return true;
31109               }
31110               var n = rules.length, i;
31111
31112               for (i = 0; i < n; i++) {
31113                 if (check(rules[i])) return;
31114               }
31115               // always check otherwise last to allow dynamic updates to the set of rules
31116               if (otherwise) check(otherwise);
31117             }
31118
31119             function listen() {
31120               listener = listener || $rootScope.$on('$locationChangeSuccess', update);
31121               return listener;
31122             }
31123
31124             if (!interceptDeferred) listen();
31125
31126             return {
31127               /**
31128                * @ngdoc function
31129                * @name ui.router.router.$urlRouter#sync
31130                * @methodOf ui.router.router.$urlRouter
31131                *
31132                * @description
31133                * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
31134                * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
31135                * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
31136                * with the transition by calling `$urlRouter.sync()`.
31137                *
31138                * @example
31139                * <pre>
31140                * angular.module('app', ['ui.router'])
31141                *   .run(function($rootScope, $urlRouter) {
31142                *     $rootScope.$on('$locationChangeSuccess', function(evt) {
31143                *       // Halt state change from even starting
31144                *       evt.preventDefault();
31145                *       // Perform custom logic
31146                *       var meetsRequirement = ...
31147                *       // Continue with the update and state transition if logic allows
31148                *       if (meetsRequirement) $urlRouter.sync();
31149                *     });
31150                * });
31151                * </pre>
31152                */
31153               sync: function() {
31154                 update();
31155               },
31156
31157               listen: function() {
31158                 return listen();
31159               },
31160
31161               update: function(read) {
31162                 if (read) {
31163                   location = $location.url();
31164                   return;
31165                 }
31166                 if ($location.url() === location) return;
31167
31168                 $location.url(location);
31169                 $location.replace();
31170               },
31171
31172               push: function(urlMatcher, params, options) {
31173                  var url = urlMatcher.format(params || {});
31174
31175                 // Handle the special hash param, if needed
31176                 if (url !== null && params && params['#']) {
31177                     url += '#' + params['#'];
31178                 }
31179
31180                 $location.url(url);
31181                 lastPushedUrl = options && options.$$avoidResync ? $location.url() : undefined;
31182                 if (options && options.replace) $location.replace();
31183               },
31184
31185               /**
31186                * @ngdoc function
31187                * @name ui.router.router.$urlRouter#href
31188                * @methodOf ui.router.router.$urlRouter
31189                *
31190                * @description
31191                * A URL generation method that returns the compiled URL for a given
31192                * {@link ui.router.util.type:UrlMatcher `UrlMatcher`}, populated with the provided parameters.
31193                *
31194                * @example
31195                * <pre>
31196                * $bob = $urlRouter.href(new UrlMatcher("/about/:person"), {
31197                *   person: "bob"
31198                * });
31199                * // $bob == "/about/bob";
31200                * </pre>
31201                *
31202                * @param {UrlMatcher} urlMatcher The `UrlMatcher` object which is used as the template of the URL to generate.
31203                * @param {object=} params An object of parameter values to fill the matcher's required parameters.
31204                * @param {object=} options Options object. The options are:
31205                *
31206                * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
31207                *
31208                * @returns {string} Returns the fully compiled URL, or `null` if `params` fail validation against `urlMatcher`
31209                */
31210               href: function(urlMatcher, params, options) {
31211                 if (!urlMatcher.validates(params)) return null;
31212
31213                 var isHtml5 = $locationProvider.html5Mode();
31214                 if (angular.isObject(isHtml5)) {
31215                   isHtml5 = isHtml5.enabled;
31216                 }
31217                 
31218                 var url = urlMatcher.format(params);
31219                 options = options || {};
31220
31221                 if (!isHtml5 && url !== null) {
31222                   url = "#" + $locationProvider.hashPrefix() + url;
31223                 }
31224
31225                 // Handle special hash param, if needed
31226                 if (url !== null && params && params['#']) {
31227                   url += '#' + params['#'];
31228                 }
31229
31230                 url = appendBasePath(url, isHtml5, options.absolute);
31231
31232                 if (!options.absolute || !url) {
31233                   return url;
31234                 }
31235
31236                 var slash = (!isHtml5 && url ? '/' : ''), port = $location.port();
31237                 port = (port === 80 || port === 443 ? '' : ':' + port);
31238
31239                 return [$location.protocol(), '://', $location.host(), port, slash, url].join('');
31240               }
31241             };
31242           }
31243         }
31244
31245         angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
31246
31247         /**
31248          * @ngdoc object
31249          * @name ui.router.state.$stateProvider
31250          *
31251          * @requires ui.router.router.$urlRouterProvider
31252          * @requires ui.router.util.$urlMatcherFactoryProvider
31253          *
31254          * @description
31255          * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
31256          * on state.
31257          *
31258          * A state corresponds to a "place" in the application in terms of the overall UI and
31259          * navigation. A state describes (via the controller / template / view properties) what
31260          * the UI looks like and does at that place.
31261          *
31262          * States often have things in common, and the primary way of factoring out these
31263          * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
31264          * nested states.
31265          *
31266          * The `$stateProvider` provides interfaces to declare these states for your app.
31267          */
31268         $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
31269         function $StateProvider(   $urlRouterProvider,   $urlMatcherFactory) {
31270
31271           var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
31272
31273           // Builds state properties from definition passed to registerState()
31274           var stateBuilder = {
31275
31276             // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
31277             // state.children = [];
31278             // if (parent) parent.children.push(state);
31279             parent: function(state) {
31280               if (isDefined(state.parent) && state.parent) return findState(state.parent);
31281               // regex matches any valid composite state name
31282               // would match "contact.list" but not "contacts"
31283               var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
31284               return compositeName ? findState(compositeName[1]) : root;
31285             },
31286
31287             // inherit 'data' from parent and override by own values (if any)
31288             data: function(state) {
31289               if (state.parent && state.parent.data) {
31290                 state.data = state.self.data = extend({}, state.parent.data, state.data);
31291               }
31292               return state.data;
31293             },
31294
31295             // Build a URLMatcher if necessary, either via a relative or absolute URL
31296             url: function(state) {
31297               var url = state.url, config = { params: state.params || {} };
31298
31299               if (isString(url)) {
31300                 if (url.charAt(0) == '^') return $urlMatcherFactory.compile(url.substring(1), config);
31301                 return (state.parent.navigable || root).url.concat(url, config);
31302               }
31303
31304               if (!url || $urlMatcherFactory.isMatcher(url)) return url;
31305               throw new Error("Invalid url '" + url + "' in state '" + state + "'");
31306             },
31307
31308             // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
31309             navigable: function(state) {
31310               return state.url ? state : (state.parent ? state.parent.navigable : null);
31311             },
31312
31313             // Own parameters for this state. state.url.params is already built at this point. Create and add non-url params
31314             ownParams: function(state) {
31315               var params = state.url && state.url.params || new $$UMFP.ParamSet();
31316               forEach(state.params || {}, function(config, id) {
31317                 if (!params[id]) params[id] = new $$UMFP.Param(id, null, config, "config");
31318               });
31319               return params;
31320             },
31321
31322             // Derive parameters for this state and ensure they're a super-set of parent's parameters
31323             params: function(state) {
31324               return state.parent && state.parent.params ? extend(state.parent.params.$$new(), state.ownParams) : new $$UMFP.ParamSet();
31325             },
31326
31327             // If there is no explicit multi-view configuration, make one up so we don't have
31328             // to handle both cases in the view directive later. Note that having an explicit
31329             // 'views' property will mean the default unnamed view properties are ignored. This
31330             // is also a good time to resolve view names to absolute names, so everything is a
31331             // straight lookup at link time.
31332             views: function(state) {
31333               var views = {};
31334
31335               forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
31336                 if (name.indexOf('@') < 0) name += '@' + state.parent.name;
31337                 views[name] = view;
31338               });
31339               return views;
31340             },
31341
31342             // Keep a full path from the root down to this state as this is needed for state activation.
31343             path: function(state) {
31344               return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
31345             },
31346
31347             // Speed up $state.contains() as it's used a lot
31348             includes: function(state) {
31349               var includes = state.parent ? extend({}, state.parent.includes) : {};
31350               includes[state.name] = true;
31351               return includes;
31352             },
31353
31354             $delegates: {}
31355           };
31356
31357           function isRelative(stateName) {
31358             return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
31359           }
31360
31361           function findState(stateOrName, base) {
31362             if (!stateOrName) return undefined;
31363
31364             var isStr = isString(stateOrName),
31365                 name  = isStr ? stateOrName : stateOrName.name,
31366                 path  = isRelative(name);
31367
31368             if (path) {
31369               if (!base) throw new Error("No reference point given for path '"  + name + "'");
31370               base = findState(base);
31371               
31372               var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
31373
31374               for (; i < pathLength; i++) {
31375                 if (rel[i] === "" && i === 0) {
31376                   current = base;
31377                   continue;
31378                 }
31379                 if (rel[i] === "^") {
31380                   if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
31381                   current = current.parent;
31382                   continue;
31383                 }
31384                 break;
31385               }
31386               rel = rel.slice(i).join(".");
31387               name = current.name + (current.name && rel ? "." : "") + rel;
31388             }
31389             var state = states[name];
31390
31391             if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
31392               return state;
31393             }
31394             return undefined;
31395           }
31396
31397           function queueState(parentName, state) {
31398             if (!queue[parentName]) {
31399               queue[parentName] = [];
31400             }
31401             queue[parentName].push(state);
31402           }
31403
31404           function flushQueuedChildren(parentName) {
31405             var queued = queue[parentName] || [];
31406             while(queued.length) {
31407               registerState(queued.shift());
31408             }
31409           }
31410
31411           function registerState(state) {
31412             // Wrap a new object around the state so we can store our private details easily.
31413             state = inherit(state, {
31414               self: state,
31415               resolve: state.resolve || {},
31416               toString: function() { return this.name; }
31417             });
31418
31419             var name = state.name;
31420             if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
31421             if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
31422
31423             // Get parent name
31424             var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
31425                 : (isString(state.parent)) ? state.parent
31426                 : (isObject(state.parent) && isString(state.parent.name)) ? state.parent.name
31427                 : '';
31428
31429             // If parent is not registered yet, add state to queue and register later
31430             if (parentName && !states[parentName]) {
31431               return queueState(parentName, state.self);
31432             }
31433
31434             for (var key in stateBuilder) {
31435               if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
31436             }
31437             states[name] = state;
31438
31439             // Register the state in the global state list and with $urlRouter if necessary.
31440             if (!state[abstractKey] && state.url) {
31441               $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
31442                 if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
31443                   $state.transitionTo(state, $match, { inherit: true, location: false });
31444                 }
31445               }]);
31446             }
31447
31448             // Register any queued children
31449             flushQueuedChildren(name);
31450
31451             return state;
31452           }
31453
31454           // Checks text to see if it looks like a glob.
31455           function isGlob (text) {
31456             return text.indexOf('*') > -1;
31457           }
31458
31459           // Returns true if glob matches current $state name.
31460           function doesStateMatchGlob (glob) {
31461             var globSegments = glob.split('.'),
31462                 segments = $state.$current.name.split('.');
31463
31464             //match single stars
31465             for (var i = 0, l = globSegments.length; i < l; i++) {
31466               if (globSegments[i] === '*') {
31467                 segments[i] = '*';
31468               }
31469             }
31470
31471             //match greedy starts
31472             if (globSegments[0] === '**') {
31473                segments = segments.slice(indexOf(segments, globSegments[1]));
31474                segments.unshift('**');
31475             }
31476             //match greedy ends
31477             if (globSegments[globSegments.length - 1] === '**') {
31478                segments.splice(indexOf(segments, globSegments[globSegments.length - 2]) + 1, Number.MAX_VALUE);
31479                segments.push('**');
31480             }
31481
31482             if (globSegments.length != segments.length) {
31483               return false;
31484             }
31485
31486             return segments.join('') === globSegments.join('');
31487           }
31488
31489
31490           // Implicit root state that is always active
31491           root = registerState({
31492             name: '',
31493             url: '^',
31494             views: null,
31495             'abstract': true
31496           });
31497           root.navigable = null;
31498
31499
31500           /**
31501            * @ngdoc function
31502            * @name ui.router.state.$stateProvider#decorator
31503            * @methodOf ui.router.state.$stateProvider
31504            *
31505            * @description
31506            * Allows you to extend (carefully) or override (at your own peril) the 
31507            * `stateBuilder` object used internally by `$stateProvider`. This can be used 
31508            * to add custom functionality to ui-router, for example inferring templateUrl 
31509            * based on the state name.
31510            *
31511            * When passing only a name, it returns the current (original or decorated) builder
31512            * function that matches `name`.
31513            *
31514            * The builder functions that can be decorated are listed below. Though not all
31515            * necessarily have a good use case for decoration, that is up to you to decide.
31516            *
31517            * In addition, users can attach custom decorators, which will generate new 
31518            * properties within the state's internal definition. There is currently no clear 
31519            * use-case for this beyond accessing internal states (i.e. $state.$current), 
31520            * however, expect this to become increasingly relevant as we introduce additional 
31521            * meta-programming features.
31522            *
31523            * **Warning**: Decorators should not be interdependent because the order of 
31524            * execution of the builder functions in non-deterministic. Builder functions 
31525            * should only be dependent on the state definition object and super function.
31526            *
31527            *
31528            * Existing builder functions and current return values:
31529            *
31530            * - **parent** `{object}` - returns the parent state object.
31531            * - **data** `{object}` - returns state data, including any inherited data that is not
31532            *   overridden by own values (if any).
31533            * - **url** `{object}` - returns a {@link ui.router.util.type:UrlMatcher UrlMatcher}
31534            *   or `null`.
31535            * - **navigable** `{object}` - returns closest ancestor state that has a URL (aka is 
31536            *   navigable).
31537            * - **params** `{object}` - returns an array of state params that are ensured to 
31538            *   be a super-set of parent's params.
31539            * - **views** `{object}` - returns a views object where each key is an absolute view 
31540            *   name (i.e. "viewName@stateName") and each value is the config object 
31541            *   (template, controller) for the view. Even when you don't use the views object 
31542            *   explicitly on a state config, one is still created for you internally.
31543            *   So by decorating this builder function you have access to decorating template 
31544            *   and controller properties.
31545            * - **ownParams** `{object}` - returns an array of params that belong to the state, 
31546            *   not including any params defined by ancestor states.
31547            * - **path** `{string}` - returns the full path from the root down to this state. 
31548            *   Needed for state activation.
31549            * - **includes** `{object}` - returns an object that includes every state that 
31550            *   would pass a `$state.includes()` test.
31551            *
31552            * @example
31553            * <pre>
31554            * // Override the internal 'views' builder with a function that takes the state
31555            * // definition, and a reference to the internal function being overridden:
31556            * $stateProvider.decorator('views', function (state, parent) {
31557            *   var result = {},
31558            *       views = parent(state);
31559            *
31560            *   angular.forEach(views, function (config, name) {
31561            *     var autoName = (state.name + '.' + name).replace('.', '/');
31562            *     config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
31563            *     result[name] = config;
31564            *   });
31565            *   return result;
31566            * });
31567            *
31568            * $stateProvider.state('home', {
31569            *   views: {
31570            *     'contact.list': { controller: 'ListController' },
31571            *     'contact.item': { controller: 'ItemController' }
31572            *   }
31573            * });
31574            *
31575            * // ...
31576            *
31577            * $state.go('home');
31578            * // Auto-populates list and item views with /partials/home/contact/list.html,
31579            * // and /partials/home/contact/item.html, respectively.
31580            * </pre>
31581            *
31582            * @param {string} name The name of the builder function to decorate. 
31583            * @param {object} func A function that is responsible for decorating the original 
31584            * builder function. The function receives two parameters:
31585            *
31586            *   - `{object}` - state - The state config object.
31587            *   - `{object}` - super - The original builder function.
31588            *
31589            * @return {object} $stateProvider - $stateProvider instance
31590            */
31591           this.decorator = decorator;
31592           function decorator(name, func) {
31593             /*jshint validthis: true */
31594             if (isString(name) && !isDefined(func)) {
31595               return stateBuilder[name];
31596             }
31597             if (!isFunction(func) || !isString(name)) {
31598               return this;
31599             }
31600             if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
31601               stateBuilder.$delegates[name] = stateBuilder[name];
31602             }
31603             stateBuilder[name] = func;
31604             return this;
31605           }
31606
31607           /**
31608            * @ngdoc function
31609            * @name ui.router.state.$stateProvider#state
31610            * @methodOf ui.router.state.$stateProvider
31611            *
31612            * @description
31613            * Registers a state configuration under a given state name. The stateConfig object
31614            * has the following acceptable properties.
31615            *
31616            * @param {string} name A unique state name, e.g. "home", "about", "contacts".
31617            * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
31618            * @param {object} stateConfig State configuration object.
31619            * @param {string|function=} stateConfig.template
31620            * <a id='template'></a>
31621            *   html template as a string or a function that returns
31622            *   an html template as a string which should be used by the uiView directives. This property 
31623            *   takes precedence over templateUrl.
31624            *   
31625            *   If `template` is a function, it will be called with the following parameters:
31626            *
31627            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by
31628            *     applying the current state
31629            *
31630            * <pre>template:
31631            *   "<h1>inline template definition</h1>" +
31632            *   "<div ui-view></div>"</pre>
31633            * <pre>template: function(params) {
31634            *       return "<h1>generated template</h1>"; }</pre>
31635            * </div>
31636            *
31637            * @param {string|function=} stateConfig.templateUrl
31638            * <a id='templateUrl'></a>
31639            *
31640            *   path or function that returns a path to an html
31641            *   template that should be used by uiView.
31642            *   
31643            *   If `templateUrl` is a function, it will be called with the following parameters:
31644            *
31645            *   - {array.&lt;object&gt;} - state parameters extracted from the current $location.path() by 
31646            *     applying the current state
31647            *
31648            * <pre>templateUrl: "home.html"</pre>
31649            * <pre>templateUrl: function(params) {
31650            *     return myTemplates[params.pageId]; }</pre>
31651            *
31652            * @param {function=} stateConfig.templateProvider
31653            * <a id='templateProvider'></a>
31654            *    Provider function that returns HTML content string.
31655            * <pre> templateProvider:
31656            *       function(MyTemplateService, params) {
31657            *         return MyTemplateService.getTemplate(params.pageId);
31658            *       }</pre>
31659            *
31660            * @param {string|function=} stateConfig.controller
31661            * <a id='controller'></a>
31662            *
31663            *  Controller fn that should be associated with newly
31664            *   related scope or the name of a registered controller if passed as a string.
31665            *   Optionally, the ControllerAs may be declared here.
31666            * <pre>controller: "MyRegisteredController"</pre>
31667            * <pre>controller:
31668            *     "MyRegisteredController as fooCtrl"}</pre>
31669            * <pre>controller: function($scope, MyService) {
31670            *     $scope.data = MyService.getData(); }</pre>
31671            *
31672            * @param {function=} stateConfig.controllerProvider
31673            * <a id='controllerProvider'></a>
31674            *
31675            * Injectable provider function that returns the actual controller or string.
31676            * <pre>controllerProvider:
31677            *   function(MyResolveData) {
31678            *     if (MyResolveData.foo)
31679            *       return "FooCtrl"
31680            *     else if (MyResolveData.bar)
31681            *       return "BarCtrl";
31682            *     else return function($scope) {
31683            *       $scope.baz = "Qux";
31684            *     }
31685            *   }</pre>
31686            *
31687            * @param {string=} stateConfig.controllerAs
31688            * <a id='controllerAs'></a>
31689            * 
31690            * A controller alias name. If present the controller will be
31691            *   published to scope under the controllerAs name.
31692            * <pre>controllerAs: "myCtrl"</pre>
31693            *
31694            * @param {string|object=} stateConfig.parent
31695            * <a id='parent'></a>
31696            * Optionally specifies the parent state of this state.
31697            *
31698            * <pre>parent: 'parentState'</pre>
31699            * <pre>parent: parentState // JS variable</pre>
31700            *
31701            * @param {object=} stateConfig.resolve
31702            * <a id='resolve'></a>
31703            *
31704            * An optional map&lt;string, function&gt; of dependencies which
31705            *   should be injected into the controller. If any of these dependencies are promises, 
31706            *   the router will wait for them all to be resolved before the controller is instantiated.
31707            *   If all the promises are resolved successfully, the $stateChangeSuccess event is fired
31708            *   and the values of the resolved promises are injected into any controllers that reference them.
31709            *   If any  of the promises are rejected the $stateChangeError event is fired.
31710            *
31711            *   The map object is:
31712            *   
31713            *   - key - {string}: name of dependency to be injected into controller
31714            *   - factory - {string|function}: If string then it is alias for service. Otherwise if function, 
31715            *     it is injected and return value it treated as dependency. If result is a promise, it is 
31716            *     resolved before its value is injected into controller.
31717            *
31718            * <pre>resolve: {
31719            *     myResolve1:
31720            *       function($http, $stateParams) {
31721            *         return $http.get("/api/foos/"+stateParams.fooID);
31722            *       }
31723            *     }</pre>
31724            *
31725            * @param {string=} stateConfig.url
31726            * <a id='url'></a>
31727            *
31728            *   A url fragment with optional parameters. When a state is navigated or
31729            *   transitioned to, the `$stateParams` service will be populated with any 
31730            *   parameters that were passed.
31731            *
31732            *   (See {@link ui.router.util.type:UrlMatcher UrlMatcher} `UrlMatcher`} for
31733            *   more details on acceptable patterns )
31734            *
31735            * examples:
31736            * <pre>url: "/home"
31737            * url: "/users/:userid"
31738            * url: "/books/{bookid:[a-zA-Z_-]}"
31739            * url: "/books/{categoryid:int}"
31740            * url: "/books/{publishername:string}/{categoryid:int}"
31741            * url: "/messages?before&after"
31742            * url: "/messages?{before:date}&{after:date}"
31743            * url: "/messages/:mailboxid?{before:date}&{after:date}"
31744            * </pre>
31745            *
31746            * @param {object=} stateConfig.views
31747            * <a id='views'></a>
31748            * an optional map&lt;string, object&gt; which defined multiple views, or targets views
31749            * manually/explicitly.
31750            *
31751            * Examples:
31752            *
31753            * Targets three named `ui-view`s in the parent state's template
31754            * <pre>views: {
31755            *     header: {
31756            *       controller: "headerCtrl",
31757            *       templateUrl: "header.html"
31758            *     }, body: {
31759            *       controller: "bodyCtrl",
31760            *       templateUrl: "body.html"
31761            *     }, footer: {
31762            *       controller: "footCtrl",
31763            *       templateUrl: "footer.html"
31764            *     }
31765            *   }</pre>
31766            *
31767            * Targets named `ui-view="header"` from grandparent state 'top''s template, and named `ui-view="body" from parent state's template.
31768            * <pre>views: {
31769            *     'header@top': {
31770            *       controller: "msgHeaderCtrl",
31771            *       templateUrl: "msgHeader.html"
31772            *     }, 'body': {
31773            *       controller: "messagesCtrl",
31774            *       templateUrl: "messages.html"
31775            *     }
31776            *   }</pre>
31777            *
31778            * @param {boolean=} [stateConfig.abstract=false]
31779            * <a id='abstract'></a>
31780            * An abstract state will never be directly activated,
31781            *   but can provide inherited properties to its common children states.
31782            * <pre>abstract: true</pre>
31783            *
31784            * @param {function=} stateConfig.onEnter
31785            * <a id='onEnter'></a>
31786            *
31787            * Callback function for when a state is entered. Good way
31788            *   to trigger an action or dispatch an event, such as opening a dialog.
31789            * If minifying your scripts, make sure to explictly annotate this function,
31790            * because it won't be automatically annotated by your build tools.
31791            *
31792            * <pre>onEnter: function(MyService, $stateParams) {
31793            *     MyService.foo($stateParams.myParam);
31794            * }</pre>
31795            *
31796            * @param {function=} stateConfig.onExit
31797            * <a id='onExit'></a>
31798            *
31799            * Callback function for when a state is exited. Good way to
31800            *   trigger an action or dispatch an event, such as opening a dialog.
31801            * If minifying your scripts, make sure to explictly annotate this function,
31802            * because it won't be automatically annotated by your build tools.
31803            *
31804            * <pre>onExit: function(MyService, $stateParams) {
31805            *     MyService.cleanup($stateParams.myParam);
31806            * }</pre>
31807            *
31808            * @param {boolean=} [stateConfig.reloadOnSearch=true]
31809            * <a id='reloadOnSearch'></a>
31810            *
31811            * If `false`, will not retrigger the same state
31812            *   just because a search/query parameter has changed (via $location.search() or $location.hash()). 
31813            *   Useful for when you'd like to modify $location.search() without triggering a reload.
31814            * <pre>reloadOnSearch: false</pre>
31815            *
31816            * @param {object=} stateConfig.data
31817            * <a id='data'></a>
31818            *
31819            * Arbitrary data object, useful for custom configuration.  The parent state's `data` is
31820            *   prototypally inherited.  In other words, adding a data property to a state adds it to
31821            *   the entire subtree via prototypal inheritance.
31822            *
31823            * <pre>data: {
31824            *     requiredRole: 'foo'
31825            * } </pre>
31826            *
31827            * @param {object=} stateConfig.params
31828            * <a id='params'></a>
31829            *
31830            * A map which optionally configures parameters declared in the `url`, or
31831            *   defines additional non-url parameters.  For each parameter being
31832            *   configured, add a configuration object keyed to the name of the parameter.
31833            *
31834            *   Each parameter configuration object may contain the following properties:
31835            *
31836            *   - ** value ** - {object|function=}: specifies the default value for this
31837            *     parameter.  This implicitly sets this parameter as optional.
31838            *
31839            *     When UI-Router routes to a state and no value is
31840            *     specified for this parameter in the URL or transition, the
31841            *     default value will be used instead.  If `value` is a function,
31842            *     it will be injected and invoked, and the return value used.
31843            *
31844            *     *Note*: `undefined` is treated as "no default value" while `null`
31845            *     is treated as "the default value is `null`".
31846            *
31847            *     *Shorthand*: If you only need to configure the default value of the
31848            *     parameter, you may use a shorthand syntax.   In the **`params`**
31849            *     map, instead mapping the param name to a full parameter configuration
31850            *     object, simply set map it to the default parameter value, e.g.:
31851            *
31852            * <pre>// define a parameter's default value
31853            * params: {
31854            *     param1: { value: "defaultValue" }
31855            * }
31856            * // shorthand default values
31857            * params: {
31858            *     param1: "defaultValue",
31859            *     param2: "param2Default"
31860            * }</pre>
31861            *
31862            *   - ** array ** - {boolean=}: *(default: false)* If true, the param value will be
31863            *     treated as an array of values.  If you specified a Type, the value will be
31864            *     treated as an array of the specified Type.  Note: query parameter values
31865            *     default to a special `"auto"` mode.
31866            *
31867            *     For query parameters in `"auto"` mode, if multiple  values for a single parameter
31868            *     are present in the URL (e.g.: `/foo?bar=1&bar=2&bar=3`) then the values
31869            *     are mapped to an array (e.g.: `{ foo: [ '1', '2', '3' ] }`).  However, if
31870            *     only one value is present (e.g.: `/foo?bar=1`) then the value is treated as single
31871            *     value (e.g.: `{ foo: '1' }`).
31872            *
31873            * <pre>params: {
31874            *     param1: { array: true }
31875            * }</pre>
31876            *
31877            *   - ** squash ** - {bool|string=}: `squash` configures how a default parameter value is represented in the URL when
31878            *     the current parameter value is the same as the default value. If `squash` is not set, it uses the
31879            *     configured default squash policy.
31880            *     (See {@link ui.router.util.$urlMatcherFactory#methods_defaultSquashPolicy `defaultSquashPolicy()`})
31881            *
31882            *   There are three squash settings:
31883            *
31884            *     - false: The parameter's default value is not squashed.  It is encoded and included in the URL
31885            *     - true: The parameter's default value is omitted from the URL.  If the parameter is preceeded and followed
31886            *       by slashes in the state's `url` declaration, then one of those slashes are omitted.
31887            *       This can allow for cleaner looking URLs.
31888            *     - `"<arbitrary string>"`: The parameter's default value is replaced with an arbitrary placeholder of  your choice.
31889            *
31890            * <pre>params: {
31891            *     param1: {
31892            *       value: "defaultId",
31893            *       squash: true
31894            * } }
31895            * // squash "defaultValue" to "~"
31896            * params: {
31897            *     param1: {
31898            *       value: "defaultValue",
31899            *       squash: "~"
31900            * } }
31901            * </pre>
31902            *
31903            *
31904            * @example
31905            * <pre>
31906            * // Some state name examples
31907            *
31908            * // stateName can be a single top-level name (must be unique).
31909            * $stateProvider.state("home", {});
31910            *
31911            * // Or it can be a nested state name. This state is a child of the
31912            * // above "home" state.
31913            * $stateProvider.state("home.newest", {});
31914            *
31915            * // Nest states as deeply as needed.
31916            * $stateProvider.state("home.newest.abc.xyz.inception", {});
31917            *
31918            * // state() returns $stateProvider, so you can chain state declarations.
31919            * $stateProvider
31920            *   .state("home", {})
31921            *   .state("about", {})
31922            *   .state("contacts", {});
31923            * </pre>
31924            *
31925            */
31926           this.state = state;
31927           function state(name, definition) {
31928             /*jshint validthis: true */
31929             if (isObject(name)) definition = name;
31930             else definition.name = name;
31931             registerState(definition);
31932             return this;
31933           }
31934
31935           /**
31936            * @ngdoc object
31937            * @name ui.router.state.$state
31938            *
31939            * @requires $rootScope
31940            * @requires $q
31941            * @requires ui.router.state.$view
31942            * @requires $injector
31943            * @requires ui.router.util.$resolve
31944            * @requires ui.router.state.$stateParams
31945            * @requires ui.router.router.$urlRouter
31946            *
31947            * @property {object} params A param object, e.g. {sectionId: section.id)}, that 
31948            * you'd like to test against the current active state.
31949            * @property {object} current A reference to the state's config object. However 
31950            * you passed it in. Useful for accessing custom data.
31951            * @property {object} transition Currently pending transition. A promise that'll 
31952            * resolve or reject.
31953            *
31954            * @description
31955            * `$state` service is responsible for representing states as well as transitioning
31956            * between them. It also provides interfaces to ask for current state or even states
31957            * you're coming from.
31958            */
31959           this.$get = $get;
31960           $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$urlRouter', '$location', '$urlMatcherFactory'];
31961           function $get(   $rootScope,   $q,   $view,   $injector,   $resolve,   $stateParams,   $urlRouter,   $location,   $urlMatcherFactory) {
31962
31963             var TransitionSuperseded = $q.reject(new Error('transition superseded'));
31964             var TransitionPrevented = $q.reject(new Error('transition prevented'));
31965             var TransitionAborted = $q.reject(new Error('transition aborted'));
31966             var TransitionFailed = $q.reject(new Error('transition failed'));
31967
31968             // Handles the case where a state which is the target of a transition is not found, and the user
31969             // can optionally retry or defer the transition
31970             function handleRedirect(redirect, state, params, options) {
31971               /**
31972                * @ngdoc event
31973                * @name ui.router.state.$state#$stateNotFound
31974                * @eventOf ui.router.state.$state
31975                * @eventType broadcast on root scope
31976                * @description
31977                * Fired when a requested state **cannot be found** using the provided state name during transition.
31978                * The event is broadcast allowing any handlers a single chance to deal with the error (usually by
31979                * lazy-loading the unfound state). A special `unfoundState` object is passed to the listener handler,
31980                * you can see its three properties in the example. You can use `event.preventDefault()` to abort the
31981                * transition and the promise returned from `go` will be rejected with a `'transition aborted'` value.
31982                *
31983                * @param {Object} event Event object.
31984                * @param {Object} unfoundState Unfound State information. Contains: `to, toParams, options` properties.
31985                * @param {State} fromState Current state object.
31986                * @param {Object} fromParams Current state params.
31987                *
31988                * @example
31989                *
31990                * <pre>
31991                * // somewhere, assume lazy.state has not been defined
31992                * $state.go("lazy.state", {a:1, b:2}, {inherit:false});
31993                *
31994                * // somewhere else
31995                * $scope.$on('$stateNotFound',
31996                * function(event, unfoundState, fromState, fromParams){
31997                *     console.log(unfoundState.to); // "lazy.state"
31998                *     console.log(unfoundState.toParams); // {a:1, b:2}
31999                *     console.log(unfoundState.options); // {inherit:false} + default options
32000                * })
32001                * </pre>
32002                */
32003               var evt = $rootScope.$broadcast('$stateNotFound', redirect, state, params);
32004
32005               if (evt.defaultPrevented) {
32006                 $urlRouter.update();
32007                 return TransitionAborted;
32008               }
32009
32010               if (!evt.retry) {
32011                 return null;
32012               }
32013
32014               // Allow the handler to return a promise to defer state lookup retry
32015               if (options.$retry) {
32016                 $urlRouter.update();
32017                 return TransitionFailed;
32018               }
32019               var retryTransition = $state.transition = $q.when(evt.retry);
32020
32021               retryTransition.then(function() {
32022                 if (retryTransition !== $state.transition) return TransitionSuperseded;
32023                 redirect.options.$retry = true;
32024                 return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
32025               }, function() {
32026                 return TransitionAborted;
32027               });
32028               $urlRouter.update();
32029
32030               return retryTransition;
32031             }
32032
32033             root.locals = { resolve: null, globals: { $stateParams: {} } };
32034
32035             $state = {
32036               params: {},
32037               current: root.self,
32038               $current: root,
32039               transition: null
32040             };
32041
32042             /**
32043              * @ngdoc function
32044              * @name ui.router.state.$state#reload
32045              * @methodOf ui.router.state.$state
32046              *
32047              * @description
32048              * A method that force reloads the current state. All resolves are re-resolved,
32049              * controllers reinstantiated, and events re-fired.
32050              *
32051              * @example
32052              * <pre>
32053              * var app angular.module('app', ['ui.router']);
32054              *
32055              * app.controller('ctrl', function ($scope, $state) {
32056              *   $scope.reload = function(){
32057              *     $state.reload();
32058              *   }
32059              * });
32060              * </pre>
32061              *
32062              * `reload()` is just an alias for:
32063              * <pre>
32064              * $state.transitionTo($state.current, $stateParams, { 
32065              *   reload: true, inherit: false, notify: true
32066              * });
32067              * </pre>
32068              *
32069              * @param {string=|object=} state - A state name or a state object, which is the root of the resolves to be re-resolved.
32070              * @example
32071              * <pre>
32072              * //assuming app application consists of 3 states: 'contacts', 'contacts.detail', 'contacts.detail.item' 
32073              * //and current state is 'contacts.detail.item'
32074              * var app angular.module('app', ['ui.router']);
32075              *
32076              * app.controller('ctrl', function ($scope, $state) {
32077              *   $scope.reload = function(){
32078              *     //will reload 'contact.detail' and 'contact.detail.item' states
32079              *     $state.reload('contact.detail');
32080              *   }
32081              * });
32082              * </pre>
32083              *
32084              * `reload()` is just an alias for:
32085              * <pre>
32086              * $state.transitionTo($state.current, $stateParams, { 
32087              *   reload: true, inherit: false, notify: true
32088              * });
32089              * </pre>
32090
32091              * @returns {promise} A promise representing the state of the new transition. See
32092              * {@link ui.router.state.$state#methods_go $state.go}.
32093              */
32094             $state.reload = function reload(state) {
32095               return $state.transitionTo($state.current, $stateParams, { reload: state || true, inherit: false, notify: true});
32096             };
32097
32098             /**
32099              * @ngdoc function
32100              * @name ui.router.state.$state#go
32101              * @methodOf ui.router.state.$state
32102              *
32103              * @description
32104              * Convenience method for transitioning to a new state. `$state.go` calls 
32105              * `$state.transitionTo` internally but automatically sets options to 
32106              * `{ location: true, inherit: true, relative: $state.$current, notify: true }`. 
32107              * This allows you to easily use an absolute or relative to path and specify 
32108              * only the parameters you'd like to update (while letting unspecified parameters 
32109              * inherit from the currently active ancestor states).
32110              *
32111              * @example
32112              * <pre>
32113              * var app = angular.module('app', ['ui.router']);
32114              *
32115              * app.controller('ctrl', function ($scope, $state) {
32116              *   $scope.changeState = function () {
32117              *     $state.go('contact.detail');
32118              *   };
32119              * });
32120              * </pre>
32121              * <img src='../ngdoc_assets/StateGoExamples.png'/>
32122              *
32123              * @param {string} to Absolute state name or relative state path. Some examples:
32124              *
32125              * - `$state.go('contact.detail')` - will go to the `contact.detail` state
32126              * - `$state.go('^')` - will go to a parent state
32127              * - `$state.go('^.sibling')` - will go to a sibling state
32128              * - `$state.go('.child.grandchild')` - will go to grandchild state
32129              *
32130              * @param {object=} params A map of the parameters that will be sent to the state, 
32131              * will populate $stateParams. Any parameters that are not specified will be inherited from currently 
32132              * defined parameters. This allows, for example, going to a sibling state that shares parameters
32133              * specified in a parent state. Parameter inheritance only works between common ancestor states, I.e.
32134              * transitioning to a sibling will get you the parameters for all parents, transitioning to a child
32135              * will get you all current parameters, etc.
32136              * @param {object=} options Options object. The options are:
32137              *
32138              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32139              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32140              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32141              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32142              *    defines which state to be relative from.
32143              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32144              * - **`reload`** (v0.2.5) - {boolean=false}, If `true` will force transition even if the state or params 
32145              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32146              *    use this when you want to force a reload when *everything* is the same, including search params.
32147              *
32148              * @returns {promise} A promise representing the state of the new transition.
32149              *
32150              * Possible success values:
32151              *
32152              * - $state.current
32153              *
32154              * <br/>Possible rejection values:
32155              *
32156              * - 'transition superseded' - when a newer transition has been started after this one
32157              * - 'transition prevented' - when `event.preventDefault()` has been called in a `$stateChangeStart` listener
32158              * - 'transition aborted' - when `event.preventDefault()` has been called in a `$stateNotFound` listener or
32159              *   when a `$stateNotFound` `event.retry` promise errors.
32160              * - 'transition failed' - when a state has been unsuccessfully found after 2 tries.
32161              * - *resolve error* - when an error has occurred with a `resolve`
32162              *
32163              */
32164             $state.go = function go(to, params, options) {
32165               return $state.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
32166             };
32167
32168             /**
32169              * @ngdoc function
32170              * @name ui.router.state.$state#transitionTo
32171              * @methodOf ui.router.state.$state
32172              *
32173              * @description
32174              * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
32175              * uses `transitionTo` internally. `$state.go` is recommended in most situations.
32176              *
32177              * @example
32178              * <pre>
32179              * var app = angular.module('app', ['ui.router']);
32180              *
32181              * app.controller('ctrl', function ($scope, $state) {
32182              *   $scope.changeState = function () {
32183              *     $state.transitionTo('contact.detail');
32184              *   };
32185              * });
32186              * </pre>
32187              *
32188              * @param {string} to State name.
32189              * @param {object=} toParams A map of the parameters that will be sent to the state,
32190              * will populate $stateParams.
32191              * @param {object=} options Options object. The options are:
32192              *
32193              * - **`location`** - {boolean=true|string=} - If `true` will update the url in the location bar, if `false`
32194              *    will not. If string, must be `"replace"`, which will update url and also replace last history record.
32195              * - **`inherit`** - {boolean=false}, If `true` will inherit url parameters from current url.
32196              * - **`relative`** - {object=}, When transitioning with relative path (e.g '^'), 
32197              *    defines which state to be relative from.
32198              * - **`notify`** - {boolean=true}, If `true` will broadcast $stateChangeStart and $stateChangeSuccess events.
32199              * - **`reload`** (v0.2.5) - {boolean=false|string=|object=}, If `true` will force transition even if the state or params 
32200              *    have not changed, aka a reload of the same state. It differs from reloadOnSearch because you'd
32201              *    use this when you want to force a reload when *everything* is the same, including search params.
32202              *    if String, then will reload the state with the name given in reload, and any children.
32203              *    if Object, then a stateObj is expected, will reload the state found in stateObj, and any children.
32204              *
32205              * @returns {promise} A promise representing the state of the new transition. See
32206              * {@link ui.router.state.$state#methods_go $state.go}.
32207              */
32208             $state.transitionTo = function transitionTo(to, toParams, options) {
32209               toParams = toParams || {};
32210               options = extend({
32211                 location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
32212               }, options || {});
32213
32214               var from = $state.$current, fromParams = $state.params, fromPath = from.path;
32215               var evt, toState = findState(to, options.relative);
32216
32217               // Store the hash param for later (since it will be stripped out by various methods)
32218               var hash = toParams['#'];
32219
32220               if (!isDefined(toState)) {
32221                 var redirect = { to: to, toParams: toParams, options: options };
32222                 var redirectResult = handleRedirect(redirect, from.self, fromParams, options);
32223
32224                 if (redirectResult) {
32225                   return redirectResult;
32226                 }
32227
32228                 // Always retry once if the $stateNotFound was not prevented
32229                 // (handles either redirect changed or state lazy-definition)
32230                 to = redirect.to;
32231                 toParams = redirect.toParams;
32232                 options = redirect.options;
32233                 toState = findState(to, options.relative);
32234
32235                 if (!isDefined(toState)) {
32236                   if (!options.relative) throw new Error("No such state '" + to + "'");
32237                   throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
32238                 }
32239               }
32240               if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
32241               if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
32242               if (!toState.params.$$validates(toParams)) return TransitionFailed;
32243
32244               toParams = toState.params.$$values(toParams);
32245               to = toState;
32246
32247               var toPath = to.path;
32248
32249               // Starting from the root of the path, keep all levels that haven't changed
32250               var keep = 0, state = toPath[keep], locals = root.locals, toLocals = [];
32251
32252               if (!options.reload) {
32253                 while (state && state === fromPath[keep] && state.ownParams.$$equals(toParams, fromParams)) {
32254                   locals = toLocals[keep] = state.locals;
32255                   keep++;
32256                   state = toPath[keep];
32257                 }
32258               } else if (isString(options.reload) || isObject(options.reload)) {
32259                 if (isObject(options.reload) && !options.reload.name) {
32260                   throw new Error('Invalid reload state object');
32261                 }
32262                 
32263                 var reloadState = options.reload === true ? fromPath[0] : findState(options.reload);
32264                 if (options.reload && !reloadState) {
32265                   throw new Error("No such reload state '" + (isString(options.reload) ? options.reload : options.reload.name) + "'");
32266                 }
32267
32268                 while (state && state === fromPath[keep] && state !== reloadState) {
32269                   locals = toLocals[keep] = state.locals;
32270                   keep++;
32271                   state = toPath[keep];
32272                 }
32273               }
32274
32275               // If we're going to the same state and all locals are kept, we've got nothing to do.
32276               // But clear 'transition', as we still want to cancel any other pending transitions.
32277               // TODO: We may not want to bump 'transition' if we're called from a location change
32278               // that we've initiated ourselves, because we might accidentally abort a legitimate
32279               // transition initiated from code?
32280               if (shouldSkipReload(to, toParams, from, fromParams, locals, options)) {
32281                 if (hash) toParams['#'] = hash;
32282                 $state.params = toParams;
32283                 copy($state.params, $stateParams);
32284                 if (options.location && to.navigable && to.navigable.url) {
32285                   $urlRouter.push(to.navigable.url, toParams, {
32286                     $$avoidResync: true, replace: options.location === 'replace'
32287                   });
32288                   $urlRouter.update(true);
32289                 }
32290                 $state.transition = null;
32291                 return $q.when($state.current);
32292               }
32293
32294               // Filter parameters before we pass them to event handlers etc.
32295               toParams = filterByKeys(to.params.$$keys(), toParams || {});
32296
32297               // Broadcast start event and cancel the transition if requested
32298               if (options.notify) {
32299                 /**
32300                  * @ngdoc event
32301                  * @name ui.router.state.$state#$stateChangeStart
32302                  * @eventOf ui.router.state.$state
32303                  * @eventType broadcast on root scope
32304                  * @description
32305                  * Fired when the state transition **begins**. You can use `event.preventDefault()`
32306                  * to prevent the transition from happening and then the transition promise will be
32307                  * rejected with a `'transition prevented'` value.
32308                  *
32309                  * @param {Object} event Event object.
32310                  * @param {State} toState The state being transitioned to.
32311                  * @param {Object} toParams The params supplied to the `toState`.
32312                  * @param {State} fromState The current state, pre-transition.
32313                  * @param {Object} fromParams The params supplied to the `fromState`.
32314                  *
32315                  * @example
32316                  *
32317                  * <pre>
32318                  * $rootScope.$on('$stateChangeStart',
32319                  * function(event, toState, toParams, fromState, fromParams){
32320                  *     event.preventDefault();
32321                  *     // transitionTo() promise will be rejected with
32322                  *     // a 'transition prevented' error
32323                  * })
32324                  * </pre>
32325                  */
32326                 if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams).defaultPrevented) {
32327                   $rootScope.$broadcast('$stateChangeCancel', to.self, toParams, from.self, fromParams);
32328                   $urlRouter.update();
32329                   return TransitionPrevented;
32330                 }
32331               }
32332
32333               // Resolve locals for the remaining states, but don't update any global state just
32334               // yet -- if anything fails to resolve the current state needs to remain untouched.
32335               // We also set up an inheritance chain for the locals here. This allows the view directive
32336               // to quickly look up the correct definition for each view in the current state. Even
32337               // though we create the locals object itself outside resolveState(), it is initially
32338               // empty and gets filled asynchronously. We need to keep track of the promise for the
32339               // (fully resolved) current locals, and pass this down the chain.
32340               var resolved = $q.when(locals);
32341
32342               for (var l = keep; l < toPath.length; l++, state = toPath[l]) {
32343                 locals = toLocals[l] = inherit(locals);
32344                 resolved = resolveState(state, toParams, state === to, resolved, locals, options);
32345               }
32346
32347               // Once everything is resolved, we are ready to perform the actual transition
32348               // and return a promise for the new state. We also keep track of what the
32349               // current promise is, so that we can detect overlapping transitions and
32350               // keep only the outcome of the last transition.
32351               var transition = $state.transition = resolved.then(function () {
32352                 var l, entering, exiting;
32353
32354                 if ($state.transition !== transition) return TransitionSuperseded;
32355
32356                 // Exit 'from' states not kept
32357                 for (l = fromPath.length - 1; l >= keep; l--) {
32358                   exiting = fromPath[l];
32359                   if (exiting.self.onExit) {
32360                     $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
32361                   }
32362                   exiting.locals = null;
32363                 }
32364
32365                 // Enter 'to' states not kept
32366                 for (l = keep; l < toPath.length; l++) {
32367                   entering = toPath[l];
32368                   entering.locals = toLocals[l];
32369                   if (entering.self.onEnter) {
32370                     $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
32371                   }
32372                 }
32373
32374                 // Re-add the saved hash before we start returning things
32375                 if (hash) toParams['#'] = hash;
32376
32377                 // Run it again, to catch any transitions in callbacks
32378                 if ($state.transition !== transition) return TransitionSuperseded;
32379
32380                 // Update globals in $state
32381                 $state.$current = to;
32382                 $state.current = to.self;
32383                 $state.params = toParams;
32384                 copy($state.params, $stateParams);
32385                 $state.transition = null;
32386
32387                 if (options.location && to.navigable) {
32388                   $urlRouter.push(to.navigable.url, to.navigable.locals.globals.$stateParams, {
32389                     $$avoidResync: true, replace: options.location === 'replace'
32390                   });
32391                 }
32392
32393                 if (options.notify) {
32394                 /**
32395                  * @ngdoc event
32396                  * @name ui.router.state.$state#$stateChangeSuccess
32397                  * @eventOf ui.router.state.$state
32398                  * @eventType broadcast on root scope
32399                  * @description
32400                  * Fired once the state transition is **complete**.
32401                  *
32402                  * @param {Object} event Event object.
32403                  * @param {State} toState The state being transitioned to.
32404                  * @param {Object} toParams The params supplied to the `toState`.
32405                  * @param {State} fromState The current state, pre-transition.
32406                  * @param {Object} fromParams The params supplied to the `fromState`.
32407                  */
32408                   $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
32409                 }
32410                 $urlRouter.update(true);
32411
32412                 return $state.current;
32413               }, function (error) {
32414                 if ($state.transition !== transition) return TransitionSuperseded;
32415
32416                 $state.transition = null;
32417                 /**
32418                  * @ngdoc event
32419                  * @name ui.router.state.$state#$stateChangeError
32420                  * @eventOf ui.router.state.$state
32421                  * @eventType broadcast on root scope
32422                  * @description
32423                  * Fired when an **error occurs** during transition. It's important to note that if you
32424                  * have any errors in your resolve functions (javascript errors, non-existent services, etc)
32425                  * they will not throw traditionally. You must listen for this $stateChangeError event to
32426                  * catch **ALL** errors.
32427                  *
32428                  * @param {Object} event Event object.
32429                  * @param {State} toState The state being transitioned to.
32430                  * @param {Object} toParams The params supplied to the `toState`.
32431                  * @param {State} fromState The current state, pre-transition.
32432                  * @param {Object} fromParams The params supplied to the `fromState`.
32433                  * @param {Error} error The resolve error object.
32434                  */
32435                 evt = $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
32436
32437                 if (!evt.defaultPrevented) {
32438                     $urlRouter.update();
32439                 }
32440
32441                 return $q.reject(error);
32442               });
32443
32444               return transition;
32445             };
32446
32447             /**
32448              * @ngdoc function
32449              * @name ui.router.state.$state#is
32450              * @methodOf ui.router.state.$state
32451              *
32452              * @description
32453              * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
32454              * but only checks for the full state name. If params is supplied then it will be
32455              * tested for strict equality against the current active params object, so all params
32456              * must match with none missing and no extras.
32457              *
32458              * @example
32459              * <pre>
32460              * $state.$current.name = 'contacts.details.item';
32461              *
32462              * // absolute name
32463              * $state.is('contact.details.item'); // returns true
32464              * $state.is(contactDetailItemStateObject); // returns true
32465              *
32466              * // relative name (. and ^), typically from a template
32467              * // E.g. from the 'contacts.details' template
32468              * <div ng-class="{highlighted: $state.is('.item')}">Item</div>
32469              * </pre>
32470              *
32471              * @param {string|object} stateOrName The state name (absolute or relative) or state object you'd like to check.
32472              * @param {object=} params A param object, e.g. `{sectionId: section.id}`, that you'd like
32473              * to test against the current active state.
32474              * @param {object=} options An options object.  The options are:
32475              *
32476              * - **`relative`** - {string|object} -  If `stateOrName` is a relative state name and `options.relative` is set, .is will
32477              * test relative to `options.relative` state (or name).
32478              *
32479              * @returns {boolean} Returns true if it is the state.
32480              */
32481             $state.is = function is(stateOrName, params, options) {
32482               options = extend({ relative: $state.$current }, options || {});
32483               var state = findState(stateOrName, options.relative);
32484
32485               if (!isDefined(state)) { return undefined; }
32486               if ($state.$current !== state) { return false; }
32487               return params ? equalForKeys(state.params.$$values(params), $stateParams) : true;
32488             };
32489
32490             /**
32491              * @ngdoc function
32492              * @name ui.router.state.$state#includes
32493              * @methodOf ui.router.state.$state
32494              *
32495              * @description
32496              * A method to determine if the current active state is equal to or is the child of the
32497              * state stateName. If any params are passed then they will be tested for a match as well.
32498              * Not all the parameters need to be passed, just the ones you'd like to test for equality.
32499              *
32500              * @example
32501              * Partial and relative names
32502              * <pre>
32503              * $state.$current.name = 'contacts.details.item';
32504              *
32505              * // Using partial names
32506              * $state.includes("contacts"); // returns true
32507              * $state.includes("contacts.details"); // returns true
32508              * $state.includes("contacts.details.item"); // returns true
32509              * $state.includes("contacts.list"); // returns false
32510              * $state.includes("about"); // returns false
32511              *
32512              * // Using relative names (. and ^), typically from a template
32513              * // E.g. from the 'contacts.details' template
32514              * <div ng-class="{highlighted: $state.includes('.item')}">Item</div>
32515              * </pre>
32516              *
32517              * Basic globbing patterns
32518              * <pre>
32519              * $state.$current.name = 'contacts.details.item.url';
32520              *
32521              * $state.includes("*.details.*.*"); // returns true
32522              * $state.includes("*.details.**"); // returns true
32523              * $state.includes("**.item.**"); // returns true
32524              * $state.includes("*.details.item.url"); // returns true
32525              * $state.includes("*.details.*.url"); // returns true
32526              * $state.includes("*.details.*"); // returns false
32527              * $state.includes("item.**"); // returns false
32528              * </pre>
32529              *
32530              * @param {string} stateOrName A partial name, relative name, or glob pattern
32531              * to be searched for within the current state name.
32532              * @param {object=} params A param object, e.g. `{sectionId: section.id}`,
32533              * that you'd like to test against the current active state.
32534              * @param {object=} options An options object.  The options are:
32535              *
32536              * - **`relative`** - {string|object=} -  If `stateOrName` is a relative state reference and `options.relative` is set,
32537              * .includes will test relative to `options.relative` state (or name).
32538              *
32539              * @returns {boolean} Returns true if it does include the state
32540              */
32541             $state.includes = function includes(stateOrName, params, options) {
32542               options = extend({ relative: $state.$current }, options || {});
32543               if (isString(stateOrName) && isGlob(stateOrName)) {
32544                 if (!doesStateMatchGlob(stateOrName)) {
32545                   return false;
32546                 }
32547                 stateOrName = $state.$current.name;
32548               }
32549
32550               var state = findState(stateOrName, options.relative);
32551               if (!isDefined(state)) { return undefined; }
32552               if (!isDefined($state.$current.includes[state.name])) { return false; }
32553               return params ? equalForKeys(state.params.$$values(params), $stateParams, objectKeys(params)) : true;
32554             };
32555
32556
32557             /**
32558              * @ngdoc function
32559              * @name ui.router.state.$state#href
32560              * @methodOf ui.router.state.$state
32561              *
32562              * @description
32563              * A url generation method that returns the compiled url for the given state populated with the given params.
32564              *
32565              * @example
32566              * <pre>
32567              * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
32568              * </pre>
32569              *
32570              * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
32571              * @param {object=} params An object of parameter values to fill the state's required parameters.
32572              * @param {object=} options Options object. The options are:
32573              *
32574              * - **`lossy`** - {boolean=true} -  If true, and if there is no url associated with the state provided in the
32575              *    first parameter, then the constructed href url will be built from the first navigable ancestor (aka
32576              *    ancestor with a valid url).
32577              * - **`inherit`** - {boolean=true}, If `true` will inherit url parameters from current url.
32578              * - **`relative`** - {object=$state.$current}, When transitioning with relative path (e.g '^'), 
32579              *    defines which state to be relative from.
32580              * - **`absolute`** - {boolean=false},  If true will generate an absolute url, e.g. "http://www.example.com/fullurl".
32581              * 
32582              * @returns {string} compiled state url
32583              */
32584             $state.href = function href(stateOrName, params, options) {
32585               options = extend({
32586                 lossy:    true,
32587                 inherit:  true,
32588                 absolute: false,
32589                 relative: $state.$current
32590               }, options || {});
32591
32592               var state = findState(stateOrName, options.relative);
32593
32594               if (!isDefined(state)) return null;
32595               if (options.inherit) params = inheritParams($stateParams, params || {}, $state.$current, state);
32596               
32597               var nav = (state && options.lossy) ? state.navigable : state;
32598
32599               if (!nav || nav.url === undefined || nav.url === null) {
32600                 return null;
32601               }
32602               return $urlRouter.href(nav.url, filterByKeys(state.params.$$keys().concat('#'), params || {}), {
32603                 absolute: options.absolute
32604               });
32605             };
32606
32607             /**
32608              * @ngdoc function
32609              * @name ui.router.state.$state#get
32610              * @methodOf ui.router.state.$state
32611              *
32612              * @description
32613              * Returns the state configuration object for any specific state or all states.
32614              *
32615              * @param {string|object=} stateOrName (absolute or relative) If provided, will only get the config for
32616              * the requested state. If not provided, returns an array of ALL state configs.
32617              * @param {string|object=} context When stateOrName is a relative state reference, the state will be retrieved relative to context.
32618              * @returns {Object|Array} State configuration object or array of all objects.
32619              */
32620             $state.get = function (stateOrName, context) {
32621               if (arguments.length === 0) return map(objectKeys(states), function(name) { return states[name].self; });
32622               var state = findState(stateOrName, context || $state.$current);
32623               return (state && state.self) ? state.self : null;
32624             };
32625
32626             function resolveState(state, params, paramsAreFiltered, inherited, dst, options) {
32627               // Make a restricted $stateParams with only the parameters that apply to this state if
32628               // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
32629               // we also need $stateParams to be available for any $injector calls we make during the
32630               // dependency resolution process.
32631               var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params.$$keys(), params);
32632               var locals = { $stateParams: $stateParams };
32633
32634               // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
32635               // We're also including $stateParams in this; that way the parameters are restricted
32636               // to the set that should be visible to the state, and are independent of when we update
32637               // the global $state and $stateParams values.
32638               dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
32639               var promises = [dst.resolve.then(function (globals) {
32640                 dst.globals = globals;
32641               })];
32642               if (inherited) promises.push(inherited);
32643
32644               function resolveViews() {
32645                 var viewsPromises = [];
32646
32647                 // Resolve template and dependencies for all views.
32648                 forEach(state.views, function (view, name) {
32649                   var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
32650                   injectables.$template = [ function () {
32651                     return $view.load(name, { view: view, locals: dst.globals, params: $stateParams, notify: options.notify }) || '';
32652                   }];
32653
32654                   viewsPromises.push($resolve.resolve(injectables, dst.globals, dst.resolve, state).then(function (result) {
32655                     // References to the controller (only instantiated at link time)
32656                     if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
32657                       var injectLocals = angular.extend({}, injectables, dst.globals);
32658                       result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
32659                     } else {
32660                       result.$$controller = view.controller;
32661                     }
32662                     // Provide access to the state itself for internal use
32663                     result.$$state = state;
32664                     result.$$controllerAs = view.controllerAs;
32665                     dst[name] = result;
32666                   }));
32667                 });
32668
32669                 return $q.all(viewsPromises).then(function(){
32670                   return dst.globals;
32671                 });
32672               }
32673
32674               // Wait for all the promises and then return the activation object
32675               return $q.all(promises).then(resolveViews).then(function (values) {
32676                 return dst;
32677               });
32678             }
32679
32680             return $state;
32681           }
32682
32683           function shouldSkipReload(to, toParams, from, fromParams, locals, options) {
32684             // Return true if there are no differences in non-search (path/object) params, false if there are differences
32685             function nonSearchParamsEqual(fromAndToState, fromParams, toParams) {
32686               // Identify whether all the parameters that differ between `fromParams` and `toParams` were search params.
32687               function notSearchParam(key) {
32688                 return fromAndToState.params[key].location != "search";
32689               }
32690               var nonQueryParamKeys = fromAndToState.params.$$keys().filter(notSearchParam);
32691               var nonQueryParams = pick.apply({}, [fromAndToState.params].concat(nonQueryParamKeys));
32692               var nonQueryParamSet = new $$UMFP.ParamSet(nonQueryParams);
32693               return nonQueryParamSet.$$equals(fromParams, toParams);
32694             }
32695
32696             // If reload was not explicitly requested
32697             // and we're transitioning to the same state we're already in
32698             // and    the locals didn't change
32699             //     or they changed in a way that doesn't merit reloading
32700             //        (reloadOnParams:false, or reloadOnSearch.false and only search params changed)
32701             // Then return true.
32702             if (!options.reload && to === from &&
32703               (locals === from.locals || (to.self.reloadOnSearch === false && nonSearchParamsEqual(from, fromParams, toParams)))) {
32704               return true;
32705             }
32706           }
32707         }
32708
32709         angular.module('ui.router.state')
32710           .value('$stateParams', {})
32711           .provider('$state', $StateProvider);
32712
32713
32714         $ViewProvider.$inject = [];
32715         function $ViewProvider() {
32716
32717           this.$get = $get;
32718           /**
32719            * @ngdoc object
32720            * @name ui.router.state.$view
32721            *
32722            * @requires ui.router.util.$templateFactory
32723            * @requires $rootScope
32724            *
32725            * @description
32726            *
32727            */
32728           $get.$inject = ['$rootScope', '$templateFactory'];
32729           function $get(   $rootScope,   $templateFactory) {
32730             return {
32731               // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
32732               /**
32733                * @ngdoc function
32734                * @name ui.router.state.$view#load
32735                * @methodOf ui.router.state.$view
32736                *
32737                * @description
32738                *
32739                * @param {string} name name
32740                * @param {object} options option object.
32741                */
32742               load: function load(name, options) {
32743                 var result, defaults = {
32744                   template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
32745                 };
32746                 options = extend(defaults, options);
32747
32748                 if (options.view) {
32749                   result = $templateFactory.fromConfig(options.view, options.params, options.locals);
32750                 }
32751                 if (result && options.notify) {
32752                 /**
32753                  * @ngdoc event
32754                  * @name ui.router.state.$state#$viewContentLoading
32755                  * @eventOf ui.router.state.$view
32756                  * @eventType broadcast on root scope
32757                  * @description
32758                  *
32759                  * Fired once the view **begins loading**, *before* the DOM is rendered.
32760                  *
32761                  * @param {Object} event Event object.
32762                  * @param {Object} viewConfig The view config properties (template, controller, etc).
32763                  *
32764                  * @example
32765                  *
32766                  * <pre>
32767                  * $scope.$on('$viewContentLoading',
32768                  * function(event, viewConfig){
32769                  *     // Access to all the view config properties.
32770                  *     // and one special property 'targetView'
32771                  *     // viewConfig.targetView
32772                  * });
32773                  * </pre>
32774                  */
32775                   $rootScope.$broadcast('$viewContentLoading', options);
32776                 }
32777                 return result;
32778               }
32779             };
32780           }
32781         }
32782
32783         angular.module('ui.router.state').provider('$view', $ViewProvider);
32784
32785         /**
32786          * @ngdoc object
32787          * @name ui.router.state.$uiViewScrollProvider
32788          *
32789          * @description
32790          * Provider that returns the {@link ui.router.state.$uiViewScroll} service function.
32791          */
32792         function $ViewScrollProvider() {
32793
32794           var useAnchorScroll = false;
32795
32796           /**
32797            * @ngdoc function
32798            * @name ui.router.state.$uiViewScrollProvider#useAnchorScroll
32799            * @methodOf ui.router.state.$uiViewScrollProvider
32800            *
32801            * @description
32802            * Reverts back to using the core [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) service for
32803            * scrolling based on the url anchor.
32804            */
32805           this.useAnchorScroll = function () {
32806             useAnchorScroll = true;
32807           };
32808
32809           /**
32810            * @ngdoc object
32811            * @name ui.router.state.$uiViewScroll
32812            *
32813            * @requires $anchorScroll
32814            * @requires $timeout
32815            *
32816            * @description
32817            * When called with a jqLite element, it scrolls the element into view (after a
32818            * `$timeout` so the DOM has time to refresh).
32819            *
32820            * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
32821            * this can be enabled by calling {@link ui.router.state.$uiViewScrollProvider#methods_useAnchorScroll `$uiViewScrollProvider.useAnchorScroll()`}.
32822            */
32823           this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
32824             if (useAnchorScroll) {
32825               return $anchorScroll;
32826             }
32827
32828             return function ($element) {
32829               return $timeout(function () {
32830                 $element[0].scrollIntoView();
32831               }, 0, false);
32832             };
32833           }];
32834         }
32835
32836         angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
32837
32838         /**
32839          * @ngdoc directive
32840          * @name ui.router.state.directive:ui-view
32841          *
32842          * @requires ui.router.state.$state
32843          * @requires $compile
32844          * @requires $controller
32845          * @requires $injector
32846          * @requires ui.router.state.$uiViewScroll
32847          * @requires $document
32848          *
32849          * @restrict ECA
32850          *
32851          * @description
32852          * The ui-view directive tells $state where to place your templates.
32853          *
32854          * @param {string=} name A view name. The name should be unique amongst the other views in the
32855          * same state. You can have views of the same name that live in different states.
32856          *
32857          * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
32858          * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
32859          * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
32860          * scroll ui-view elements into view when they are populated during a state activation.
32861          *
32862          * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
32863          * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
32864          *
32865          * @param {string=} onload Expression to evaluate whenever the view updates.
32866          * 
32867          * @example
32868          * A view can be unnamed or named. 
32869          * <pre>
32870          * <!-- Unnamed -->
32871          * <div ui-view></div> 
32872          * 
32873          * <!-- Named -->
32874          * <div ui-view="viewName"></div>
32875          * </pre>
32876          *
32877          * You can only have one unnamed view within any template (or root html). If you are only using a 
32878          * single view and it is unnamed then you can populate it like so:
32879          * <pre>
32880          * <div ui-view></div> 
32881          * $stateProvider.state("home", {
32882          *   template: "<h1>HELLO!</h1>"
32883          * })
32884          * </pre>
32885          * 
32886          * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
32887          * config property, by name, in this case an empty name:
32888          * <pre>
32889          * $stateProvider.state("home", {
32890          *   views: {
32891          *     "": {
32892          *       template: "<h1>HELLO!</h1>"
32893          *     }
32894          *   }    
32895          * })
32896          * </pre>
32897          * 
32898          * But typically you'll only use the views property if you name your view or have more than one view 
32899          * in the same template. There's not really a compelling reason to name a view if its the only one, 
32900          * but you could if you wanted, like so:
32901          * <pre>
32902          * <div ui-view="main"></div>
32903          * </pre> 
32904          * <pre>
32905          * $stateProvider.state("home", {
32906          *   views: {
32907          *     "main": {
32908          *       template: "<h1>HELLO!</h1>"
32909          *     }
32910          *   }    
32911          * })
32912          * </pre>
32913          * 
32914          * Really though, you'll use views to set up multiple views:
32915          * <pre>
32916          * <div ui-view></div>
32917          * <div ui-view="chart"></div> 
32918          * <div ui-view="data"></div> 
32919          * </pre>
32920          * 
32921          * <pre>
32922          * $stateProvider.state("home", {
32923          *   views: {
32924          *     "": {
32925          *       template: "<h1>HELLO!</h1>"
32926          *     },
32927          *     "chart": {
32928          *       template: "<chart_thing/>"
32929          *     },
32930          *     "data": {
32931          *       template: "<data_thing/>"
32932          *     }
32933          *   }    
32934          * })
32935          * </pre>
32936          *
32937          * Examples for `autoscroll`:
32938          *
32939          * <pre>
32940          * <!-- If autoscroll present with no expression,
32941          *      then scroll ui-view into view -->
32942          * <ui-view autoscroll/>
32943          *
32944          * <!-- If autoscroll present with valid expression,
32945          *      then scroll ui-view into view if expression evaluates to true -->
32946          * <ui-view autoscroll='true'/>
32947          * <ui-view autoscroll='false'/>
32948          * <ui-view autoscroll='scopeVariable'/>
32949          * </pre>
32950          */
32951         $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
32952         function $ViewDirective(   $state,   $injector,   $uiViewScroll,   $interpolate) {
32953
32954           function getService() {
32955             return ($injector.has) ? function(service) {
32956               return $injector.has(service) ? $injector.get(service) : null;
32957             } : function(service) {
32958               try {
32959                 return $injector.get(service);
32960               } catch (e) {
32961                 return null;
32962               }
32963             };
32964           }
32965
32966           var service = getService(),
32967               $animator = service('$animator'),
32968               $animate = service('$animate');
32969
32970           // Returns a set of DOM manipulation functions based on which Angular version
32971           // it should use
32972           function getRenderer(attrs, scope) {
32973             var statics = function() {
32974               return {
32975                 enter: function (element, target, cb) { target.after(element); cb(); },
32976                 leave: function (element, cb) { element.remove(); cb(); }
32977               };
32978             };
32979
32980             if ($animate) {
32981               return {
32982                 enter: function(element, target, cb) {
32983                   var promise = $animate.enter(element, null, target, cb);
32984                   if (promise && promise.then) promise.then(cb);
32985                 },
32986                 leave: function(element, cb) {
32987                   var promise = $animate.leave(element, cb);
32988                   if (promise && promise.then) promise.then(cb);
32989                 }
32990               };
32991             }
32992
32993             if ($animator) {
32994               var animate = $animator && $animator(scope, attrs);
32995
32996               return {
32997                 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
32998                 leave: function(element, cb) { animate.leave(element); cb(); }
32999               };
33000             }
33001
33002             return statics();
33003           }
33004
33005           var directive = {
33006             restrict: 'ECA',
33007             terminal: true,
33008             priority: 400,
33009             transclude: 'element',
33010             compile: function (tElement, tAttrs, $transclude) {
33011               return function (scope, $element, attrs) {
33012                 var previousEl, currentEl, currentScope, latestLocals,
33013                     onloadExp     = attrs.onload || '',
33014                     autoScrollExp = attrs.autoscroll,
33015                     renderer      = getRenderer(attrs, scope);
33016
33017                 scope.$on('$stateChangeSuccess', function() {
33018                   updateView(false);
33019                 });
33020                 scope.$on('$viewContentLoading', function() {
33021                   updateView(false);
33022                 });
33023
33024                 updateView(true);
33025
33026                 function cleanupLastView() {
33027                   if (previousEl) {
33028                     previousEl.remove();
33029                     previousEl = null;
33030                   }
33031
33032                   if (currentScope) {
33033                     currentScope.$destroy();
33034                     currentScope = null;
33035                   }
33036
33037                   if (currentEl) {
33038                     renderer.leave(currentEl, function() {
33039                       previousEl = null;
33040                     });
33041
33042                     previousEl = currentEl;
33043                     currentEl = null;
33044                   }
33045                 }
33046
33047                 function updateView(firstTime) {
33048                   var newScope,
33049                       name            = getUiViewName(scope, attrs, $element, $interpolate),
33050                       previousLocals  = name && $state.$current && $state.$current.locals[name];
33051
33052                   if (!firstTime && previousLocals === latestLocals) return; // nothing to do
33053                   newScope = scope.$new();
33054                   latestLocals = $state.$current.locals[name];
33055
33056                   var clone = $transclude(newScope, function(clone) {
33057                     renderer.enter(clone, $element, function onUiViewEnter() {
33058                       if(currentScope) {
33059                         currentScope.$emit('$viewContentAnimationEnded');
33060                       }
33061
33062                       if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
33063                         $uiViewScroll(clone);
33064                       }
33065                     });
33066                     cleanupLastView();
33067                   });
33068
33069                   currentEl = clone;
33070                   currentScope = newScope;
33071                   /**
33072                    * @ngdoc event
33073                    * @name ui.router.state.directive:ui-view#$viewContentLoaded
33074                    * @eventOf ui.router.state.directive:ui-view
33075                    * @eventType emits on ui-view directive scope
33076                    * @description           *
33077                    * Fired once the view is **loaded**, *after* the DOM is rendered.
33078                    *
33079                    * @param {Object} event Event object.
33080                    */
33081                   currentScope.$emit('$viewContentLoaded');
33082                   currentScope.$eval(onloadExp);
33083                 }
33084               };
33085             }
33086           };
33087
33088           return directive;
33089         }
33090
33091         $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
33092         function $ViewDirectiveFill (  $compile,   $controller,   $state,   $interpolate) {
33093           return {
33094             restrict: 'ECA',
33095             priority: -400,
33096             compile: function (tElement) {
33097               var initial = tElement.html();
33098               return function (scope, $element, attrs) {
33099                 var current = $state.$current,
33100                     name = getUiViewName(scope, attrs, $element, $interpolate),
33101                     locals  = current && current.locals[name];
33102
33103                 if (! locals) {
33104                   return;
33105                 }
33106
33107                 $element.data('$uiView', { name: name, state: locals.$$state });
33108                 $element.html(locals.$template ? locals.$template : initial);
33109
33110                 var link = $compile($element.contents());
33111
33112                 if (locals.$$controller) {
33113                   locals.$scope = scope;
33114                   locals.$element = $element;
33115                   var controller = $controller(locals.$$controller, locals);
33116                   if (locals.$$controllerAs) {
33117                     scope[locals.$$controllerAs] = controller;
33118                   }
33119                   $element.data('$ngControllerController', controller);
33120                   $element.children().data('$ngControllerController', controller);
33121                 }
33122
33123                 link(scope);
33124               };
33125             }
33126           };
33127         }
33128
33129         /**
33130          * Shared ui-view code for both directives:
33131          * Given scope, element, and its attributes, return the view's name
33132          */
33133         function getUiViewName(scope, attrs, element, $interpolate) {
33134           var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
33135           var inherited = element.inheritedData('$uiView');
33136           return name.indexOf('@') >= 0 ?  name :  (name + '@' + (inherited ? inherited.state.name : ''));
33137         }
33138
33139         angular.module('ui.router.state').directive('uiView', $ViewDirective);
33140         angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
33141
33142         function parseStateRef(ref, current) {
33143           var preparsed = ref.match(/^\s*({[^}]*})\s*$/), parsed;
33144           if (preparsed) ref = current + '(' + preparsed[1] + ')';
33145           parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
33146           if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
33147           return { state: parsed[1], paramExpr: parsed[3] || null };
33148         }
33149
33150         function stateContext(el) {
33151           var stateData = el.parent().inheritedData('$uiView');
33152
33153           if (stateData && stateData.state && stateData.state.name) {
33154             return stateData.state;
33155           }
33156         }
33157
33158         /**
33159          * @ngdoc directive
33160          * @name ui.router.state.directive:ui-sref
33161          *
33162          * @requires ui.router.state.$state
33163          * @requires $timeout
33164          *
33165          * @restrict A
33166          *
33167          * @description
33168          * A directive that binds a link (`<a>` tag) to a state. If the state has an associated 
33169          * URL, the directive will automatically generate & update the `href` attribute via 
33170          * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking 
33171          * the link will trigger a state transition with optional parameters. 
33172          *
33173          * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be 
33174          * handled natively by the browser.
33175          *
33176          * You can also use relative state paths within ui-sref, just like the relative 
33177          * paths passed to `$state.go()`. You just need to be aware that the path is relative
33178          * to the state that the link lives in, in other words the state that loaded the 
33179          * template containing the link.
33180          *
33181          * You can specify options to pass to {@link ui.router.state.$state#go $state.go()}
33182          * using the `ui-sref-opts` attribute. Options are restricted to `location`, `inherit`,
33183          * and `reload`.
33184          *
33185          * @example
33186          * Here's an example of how you'd use ui-sref and how it would compile. If you have the 
33187          * following template:
33188          * <pre>
33189          * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a> | <a ui-sref="{page: 2}">Next page</a>
33190          * 
33191          * <ul>
33192          *     <li ng-repeat="contact in contacts">
33193          *         <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
33194          *     </li>
33195          * </ul>
33196          * </pre>
33197          * 
33198          * Then the compiled html would be (assuming Html5Mode is off and current state is contacts):
33199          * <pre>
33200          * <a href="#/home" ui-sref="home">Home</a> | <a href="#/about" ui-sref="about">About</a> | <a href="#/contacts?page=2" ui-sref="{page: 2}">Next page</a>
33201          * 
33202          * <ul>
33203          *     <li ng-repeat="contact in contacts">
33204          *         <a href="#/contacts/1" ui-sref="contacts.detail({ id: contact.id })">Joe</a>
33205          *     </li>
33206          *     <li ng-repeat="contact in contacts">
33207          *         <a href="#/contacts/2" ui-sref="contacts.detail({ id: contact.id })">Alice</a>
33208          *     </li>
33209          *     <li ng-repeat="contact in contacts">
33210          *         <a href="#/contacts/3" ui-sref="contacts.detail({ id: contact.id })">Bob</a>
33211          *     </li>
33212          * </ul>
33213          *
33214          * <a ui-sref="home" ui-sref-opts="{reload: true}">Home</a>
33215          * </pre>
33216          *
33217          * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
33218          * @param {Object} ui-sref-opts options to pass to {@link ui.router.state.$state#go $state.go()}
33219          */
33220         $StateRefDirective.$inject = ['$state', '$timeout'];
33221         function $StateRefDirective($state, $timeout) {
33222           var allowedOptions = ['location', 'inherit', 'reload', 'absolute'];
33223
33224           return {
33225             restrict: 'A',
33226             require: ['?^uiSrefActive', '?^uiSrefActiveEq'],
33227             link: function(scope, element, attrs, uiSrefActive) {
33228               var ref = parseStateRef(attrs.uiSref, $state.current.name);
33229               var params = null, url = null, base = stateContext(element) || $state.$current;
33230               // SVGAElement does not use the href attribute, but rather the 'xlinkHref' attribute.
33231               var hrefKind = Object.prototype.toString.call(element.prop('href')) === '[object SVGAnimatedString]' ?
33232                          'xlink:href' : 'href';
33233               var newHref = null, isAnchor = element.prop("tagName").toUpperCase() === "A";
33234               var isForm = element[0].nodeName === "FORM";
33235               var attr = isForm ? "action" : hrefKind, nav = true;
33236
33237               var options = { relative: base, inherit: true };
33238               var optionsOverride = scope.$eval(attrs.uiSrefOpts) || {};
33239
33240               angular.forEach(allowedOptions, function(option) {
33241                 if (option in optionsOverride) {
33242                   options[option] = optionsOverride[option];
33243                 }
33244               });
33245
33246               var update = function(newVal) {
33247                 if (newVal) params = angular.copy(newVal);
33248                 if (!nav) return;
33249
33250                 newHref = $state.href(ref.state, params, options);
33251
33252                 var activeDirective = uiSrefActive[1] || uiSrefActive[0];
33253                 if (activeDirective) {
33254                   activeDirective.$$addStateInfo(ref.state, params);
33255                 }
33256                 if (newHref === null) {
33257                   nav = false;
33258                   return false;
33259                 }
33260                 attrs.$set(attr, newHref);
33261               };
33262
33263               if (ref.paramExpr) {
33264                 scope.$watch(ref.paramExpr, function(newVal, oldVal) {
33265                   if (newVal !== params) update(newVal);
33266                 }, true);
33267                 params = angular.copy(scope.$eval(ref.paramExpr));
33268               }
33269               update();
33270
33271               if (isForm) return;
33272
33273               element.bind("click", function(e) {
33274                 var button = e.which || e.button;
33275                 if ( !(button > 1 || e.ctrlKey || e.metaKey || e.shiftKey || element.attr('target')) ) {
33276                   // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
33277                   var transition = $timeout(function() {
33278                     $state.go(ref.state, params, options);
33279                   });
33280                   e.preventDefault();
33281
33282                   // if the state has no URL, ignore one preventDefault from the <a> directive.
33283                   var ignorePreventDefaultCount = isAnchor && !newHref ? 1: 0;
33284                   e.preventDefault = function() {
33285                     if (ignorePreventDefaultCount-- <= 0)
33286                       $timeout.cancel(transition);
33287                   };
33288                 }
33289               });
33290             }
33291           };
33292         }
33293
33294         /**
33295          * @ngdoc directive
33296          * @name ui.router.state.directive:ui-sref-active
33297          *
33298          * @requires ui.router.state.$state
33299          * @requires ui.router.state.$stateParams
33300          * @requires $interpolate
33301          *
33302          * @restrict A
33303          *
33304          * @description
33305          * A directive working alongside ui-sref to add classes to an element when the
33306          * related ui-sref directive's state is active, and removing them when it is inactive.
33307          * The primary use-case is to simplify the special appearance of navigation menus
33308          * relying on `ui-sref`, by having the "active" state's menu button appear different,
33309          * distinguishing it from the inactive menu items.
33310          *
33311          * ui-sref-active can live on the same element as ui-sref or on a parent element. The first
33312          * ui-sref-active found at the same level or above the ui-sref will be used.
33313          *
33314          * Will activate when the ui-sref's target state or any child state is active. If you
33315          * need to activate only when the ui-sref target state is active and *not* any of
33316          * it's children, then you will use
33317          * {@link ui.router.state.directive:ui-sref-active-eq ui-sref-active-eq}
33318          *
33319          * @example
33320          * Given the following template:
33321          * <pre>
33322          * <ul>
33323          *   <li ui-sref-active="active" class="item">
33324          *     <a href ui-sref="app.user({user: 'bilbobaggins'})">@bilbobaggins</a>
33325          *   </li>
33326          * </ul>
33327          * </pre>
33328          *
33329          *
33330          * When the app state is "app.user" (or any children states), and contains the state parameter "user" with value "bilbobaggins",
33331          * the resulting HTML will appear as (note the 'active' class):
33332          * <pre>
33333          * <ul>
33334          *   <li ui-sref-active="active" class="item active">
33335          *     <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
33336          *   </li>
33337          * </ul>
33338          * </pre>
33339          *
33340          * The class name is interpolated **once** during the directives link time (any further changes to the
33341          * interpolated value are ignored).
33342          *
33343          * Multiple classes may be specified in a space-separated format:
33344          * <pre>
33345          * <ul>
33346          *   <li ui-sref-active='class1 class2 class3'>
33347          *     <a ui-sref="app.user">link</a>
33348          *   </li>
33349          * </ul>
33350          * </pre>
33351          */
33352
33353         /**
33354          * @ngdoc directive
33355          * @name ui.router.state.directive:ui-sref-active-eq
33356          *
33357          * @requires ui.router.state.$state
33358          * @requires ui.router.state.$stateParams
33359          * @requires $interpolate
33360          *
33361          * @restrict A
33362          *
33363          * @description
33364          * The same as {@link ui.router.state.directive:ui-sref-active ui-sref-active} but will only activate
33365          * when the exact target state used in the `ui-sref` is active; no child states.
33366          *
33367          */
33368         $StateRefActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
33369         function $StateRefActiveDirective($state, $stateParams, $interpolate) {
33370           return  {
33371             restrict: "A",
33372             controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
33373               var states = [], activeClass;
33374
33375               // There probably isn't much point in $observing this
33376               // uiSrefActive and uiSrefActiveEq share the same directive object with some
33377               // slight difference in logic routing
33378               activeClass = $interpolate($attrs.uiSrefActiveEq || $attrs.uiSrefActive || '', false)($scope);
33379
33380               // Allow uiSref to communicate with uiSrefActive[Equals]
33381               this.$$addStateInfo = function (newState, newParams) {
33382                 var state = $state.get(newState, stateContext($element));
33383
33384                 states.push({
33385                   state: state || { name: newState },
33386                   params: newParams
33387                 });
33388
33389                 update();
33390               };
33391
33392               $scope.$on('$stateChangeSuccess', update);
33393
33394               // Update route state
33395               function update() {
33396                 if (anyMatch()) {
33397                   $element.addClass(activeClass);
33398                 } else {
33399                   $element.removeClass(activeClass);
33400                 }
33401               }
33402
33403               function anyMatch() {
33404                 for (var i = 0; i < states.length; i++) {
33405                   if (isMatch(states[i].state, states[i].params)) {
33406                     return true;
33407                   }
33408                 }
33409                 return false;
33410               }
33411
33412               function isMatch(state, params) {
33413                 if (typeof $attrs.uiSrefActiveEq !== 'undefined') {
33414                   return $state.is(state.name, params);
33415                 } else {
33416                   return $state.includes(state.name, params);
33417                 }
33418               }
33419             }]
33420           };
33421         }
33422
33423         angular.module('ui.router.state')
33424           .directive('uiSref', $StateRefDirective)
33425           .directive('uiSrefActive', $StateRefActiveDirective)
33426           .directive('uiSrefActiveEq', $StateRefActiveDirective);
33427
33428         /**
33429          * @ngdoc filter
33430          * @name ui.router.state.filter:isState
33431          *
33432          * @requires ui.router.state.$state
33433          *
33434          * @description
33435          * Translates to {@link ui.router.state.$state#methods_is $state.is("stateName")}.
33436          */
33437         $IsStateFilter.$inject = ['$state'];
33438         function $IsStateFilter($state) {
33439           var isFilter = function (state) {
33440             return $state.is(state);
33441           };
33442           isFilter.$stateful = true;
33443           return isFilter;
33444         }
33445
33446         /**
33447          * @ngdoc filter
33448          * @name ui.router.state.filter:includedByState
33449          *
33450          * @requires ui.router.state.$state
33451          *
33452          * @description
33453          * Translates to {@link ui.router.state.$state#methods_includes $state.includes('fullOrPartialStateName')}.
33454          */
33455         $IncludedByStateFilter.$inject = ['$state'];
33456         function $IncludedByStateFilter($state) {
33457           var includesFilter = function (state) {
33458             return $state.includes(state);
33459           };
33460           includesFilter.$stateful = true;
33461           return  includesFilter;
33462         }
33463
33464         angular.module('ui.router.state')
33465           .filter('isState', $IsStateFilter)
33466           .filter('includedByState', $IncludedByStateFilter);
33467         })(window, window.angular);
33468
33469 /***/ },
33470 /* 4 */
33471 /***/ function(module, exports, __webpack_require__) {
33472
33473         __webpack_require__(5);
33474         module.exports = 'ngResource';
33475
33476
33477 /***/ },
33478 /* 5 */
33479 /***/ function(module, exports) {
33480
33481         /**
33482          * @license AngularJS v1.4.8
33483          * (c) 2010-2015 Google, Inc. http://angularjs.org
33484          * License: MIT
33485          */
33486         (function(window, angular, undefined) {'use strict';
33487
33488         var $resourceMinErr = angular.$$minErr('$resource');
33489
33490         // Helper functions and regex to lookup a dotted path on an object
33491         // stopping at undefined/null.  The path must be composed of ASCII
33492         // identifiers (just like $parse)
33493         var MEMBER_NAME_REGEX = /^(\.[a-zA-Z_$@][0-9a-zA-Z_$@]*)+$/;
33494
33495         function isValidDottedPath(path) {
33496           return (path != null && path !== '' && path !== 'hasOwnProperty' &&
33497               MEMBER_NAME_REGEX.test('.' + path));
33498         }
33499
33500         function lookupDottedPath(obj, path) {
33501           if (!isValidDottedPath(path)) {
33502             throw $resourceMinErr('badmember', 'Dotted member path "@{0}" is invalid.', path);
33503           }
33504           var keys = path.split('.');
33505           for (var i = 0, ii = keys.length; i < ii && angular.isDefined(obj); i++) {
33506             var key = keys[i];
33507             obj = (obj !== null) ? obj[key] : undefined;
33508           }
33509           return obj;
33510         }
33511
33512         /**
33513          * Create a shallow copy of an object and clear other fields from the destination
33514          */
33515         function shallowClearAndCopy(src, dst) {
33516           dst = dst || {};
33517
33518           angular.forEach(dst, function(value, key) {
33519             delete dst[key];
33520           });
33521
33522           for (var key in src) {
33523             if (src.hasOwnProperty(key) && !(key.charAt(0) === '$' && key.charAt(1) === '$')) {
33524               dst[key] = src[key];
33525             }
33526           }
33527
33528           return dst;
33529         }
33530
33531         /**
33532          * @ngdoc module
33533          * @name ngResource
33534          * @description
33535          *
33536          * # ngResource
33537          *
33538          * The `ngResource` module provides interaction support with RESTful services
33539          * via the $resource service.
33540          *
33541          *
33542          * <div doc-module-components="ngResource"></div>
33543          *
33544          * See {@link ngResource.$resource `$resource`} for usage.
33545          */
33546
33547         /**
33548          * @ngdoc service
33549          * @name $resource
33550          * @requires $http
33551          *
33552          * @description
33553          * A factory which creates a resource object that lets you interact with
33554          * [RESTful](http://en.wikipedia.org/wiki/Representational_State_Transfer) server-side data sources.
33555          *
33556          * The returned resource object has action methods which provide high-level behaviors without
33557          * the need to interact with the low level {@link ng.$http $http} service.
33558          *
33559          * Requires the {@link ngResource `ngResource`} module to be installed.
33560          *
33561          * By default, trailing slashes will be stripped from the calculated URLs,
33562          * which can pose problems with server backends that do not expect that
33563          * behavior.  This can be disabled by configuring the `$resourceProvider` like
33564          * this:
33565          *
33566          * ```js
33567              app.config(['$resourceProvider', function($resourceProvider) {
33568                // Don't strip trailing slashes from calculated URLs
33569                $resourceProvider.defaults.stripTrailingSlashes = false;
33570              }]);
33571          * ```
33572          *
33573          * @param {string} url A parameterized URL template with parameters prefixed by `:` as in
33574          *   `/user/:username`. If you are using a URL with a port number (e.g.
33575          *   `http://example.com:8080/api`), it will be respected.
33576          *
33577          *   If you are using a url with a suffix, just add the suffix, like this:
33578          *   `$resource('http://example.com/resource.json')` or `$resource('http://example.com/:id.json')`
33579          *   or even `$resource('http://example.com/resource/:resource_id.:format')`
33580          *   If the parameter before the suffix is empty, :resource_id in this case, then the `/.` will be
33581          *   collapsed down to a single `.`.  If you need this sequence to appear and not collapse then you
33582          *   can escape it with `/\.`.
33583          *
33584          * @param {Object=} paramDefaults Default values for `url` parameters. These can be overridden in
33585          *   `actions` methods. If any of the parameter value is a function, it will be executed every time
33586          *   when a param value needs to be obtained for a request (unless the param was overridden).
33587          *
33588          *   Each key value in the parameter object is first bound to url template if present and then any
33589          *   excess keys are appended to the url search query after the `?`.
33590          *
33591          *   Given a template `/path/:verb` and parameter `{verb:'greet', salutation:'Hello'}` results in
33592          *   URL `/path/greet?salutation=Hello`.
33593          *
33594          *   If the parameter value is prefixed with `@` then the value for that parameter will be extracted
33595          *   from the corresponding property on the `data` object (provided when calling an action method).  For
33596          *   example, if the `defaultParam` object is `{someParam: '@someProp'}` then the value of `someParam`
33597          *   will be `data.someProp`.
33598          *
33599          * @param {Object.<Object>=} actions Hash with declaration of custom actions that should extend
33600          *   the default set of resource actions. The declaration should be created in the format of {@link
33601          *   ng.$http#usage $http.config}:
33602          *
33603          *       {action1: {method:?, params:?, isArray:?, headers:?, ...},
33604          *        action2: {method:?, params:?, isArray:?, headers:?, ...},
33605          *        ...}
33606          *
33607          *   Where:
33608          *
33609          *   - **`action`** – {string} – The name of action. This name becomes the name of the method on
33610          *     your resource object.
33611          *   - **`method`** – {string} – Case insensitive HTTP method (e.g. `GET`, `POST`, `PUT`,
33612          *     `DELETE`, `JSONP`, etc).
33613          *   - **`params`** – {Object=} – Optional set of pre-bound parameters for this action. If any of
33614          *     the parameter value is a function, it will be executed every time when a param value needs to
33615          *     be obtained for a request (unless the param was overridden).
33616          *   - **`url`** – {string} – action specific `url` override. The url templating is supported just
33617          *     like for the resource-level urls.
33618          *   - **`isArray`** – {boolean=} – If true then the returned object for this action is an array,
33619          *     see `returns` section.
33620          *   - **`transformRequest`** –
33621          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33622          *     transform function or an array of such functions. The transform function takes the http
33623          *     request body and headers and returns its transformed (typically serialized) version.
33624          *     By default, transformRequest will contain one function that checks if the request data is
33625          *     an object and serializes to using `angular.toJson`. To prevent this behavior, set
33626          *     `transformRequest` to an empty array: `transformRequest: []`
33627          *   - **`transformResponse`** –
33628          *     `{function(data, headersGetter)|Array.<function(data, headersGetter)>}` –
33629          *     transform function or an array of such functions. The transform function takes the http
33630          *     response body and headers and returns its transformed (typically deserialized) version.
33631          *     By default, transformResponse will contain one function that checks if the response looks like
33632          *     a JSON string and deserializes it using `angular.fromJson`. To prevent this behavior, set
33633          *     `transformResponse` to an empty array: `transformResponse: []`
33634          *   - **`cache`** – `{boolean|Cache}` – If true, a default $http cache will be used to cache the
33635          *     GET request, otherwise if a cache instance built with
33636          *     {@link ng.$cacheFactory $cacheFactory}, this cache will be used for
33637          *     caching.
33638          *   - **`timeout`** – `{number|Promise}` – timeout in milliseconds, or {@link ng.$q promise} that
33639          *     should abort the request when resolved.
33640          *   - **`withCredentials`** - `{boolean}` - whether to set the `withCredentials` flag on the
33641          *     XHR object. See
33642          *     [requests with credentials](https://developer.mozilla.org/en/http_access_control#section_5)
33643          *     for more information.
33644          *   - **`responseType`** - `{string}` - see
33645          *     [requestType](https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#responseType).
33646          *   - **`interceptor`** - `{Object=}` - The interceptor object has two optional methods -
33647          *     `response` and `responseError`. Both `response` and `responseError` interceptors get called
33648          *     with `http response` object. See {@link ng.$http $http interceptors}.
33649          *
33650          * @param {Object} options Hash with custom settings that should extend the
33651          *   default `$resourceProvider` behavior.  The only supported option is
33652          *
33653          *   Where:
33654          *
33655          *   - **`stripTrailingSlashes`** – {boolean} – If true then the trailing
33656          *   slashes from any calculated URL will be stripped. (Defaults to true.)
33657          *
33658          * @returns {Object} A resource "class" object with methods for the default set of resource actions
33659          *   optionally extended with custom `actions`. The default set contains these actions:
33660          *   ```js
33661          *   { 'get':    {method:'GET'},
33662          *     'save':   {method:'POST'},
33663          *     'query':  {method:'GET', isArray:true},
33664          *     'remove': {method:'DELETE'},
33665          *     'delete': {method:'DELETE'} };
33666          *   ```
33667          *
33668          *   Calling these methods invoke an {@link ng.$http} with the specified http method,
33669          *   destination and parameters. When the data is returned from the server then the object is an
33670          *   instance of the resource class. The actions `save`, `remove` and `delete` are available on it
33671          *   as  methods with the `$` prefix. This allows you to easily perform CRUD operations (create,
33672          *   read, update, delete) on server-side data like this:
33673          *   ```js
33674          *   var User = $resource('/user/:userId', {userId:'@id'});
33675          *   var user = User.get({userId:123}, function() {
33676          *     user.abc = true;
33677          *     user.$save();
33678          *   });
33679          *   ```
33680          *
33681          *   It is important to realize that invoking a $resource object method immediately returns an
33682          *   empty reference (object or array depending on `isArray`). Once the data is returned from the
33683          *   server the existing reference is populated with the actual data. This is a useful trick since
33684          *   usually the resource is assigned to a model which is then rendered by the view. Having an empty
33685          *   object results in no rendering, once the data arrives from the server then the object is
33686          *   populated with the data and the view automatically re-renders itself showing the new data. This
33687          *   means that in most cases one never has to write a callback function for the action methods.
33688          *
33689          *   The action methods on the class object or instance object can be invoked with the following
33690          *   parameters:
33691          *
33692          *   - HTTP GET "class" actions: `Resource.action([parameters], [success], [error])`
33693          *   - non-GET "class" actions: `Resource.action([parameters], postData, [success], [error])`
33694          *   - non-GET instance actions:  `instance.$action([parameters], [success], [error])`
33695          *
33696          *
33697          *   Success callback is called with (value, responseHeaders) arguments, where the value is
33698          *   the populated resource instance or collection object. The error callback is called
33699          *   with (httpResponse) argument.
33700          *
33701          *   Class actions return empty instance (with additional properties below).
33702          *   Instance actions return promise of the action.
33703          *
33704          *   The Resource instances and collection have these additional properties:
33705          *
33706          *   - `$promise`: the {@link ng.$q promise} of the original server interaction that created this
33707          *     instance or collection.
33708          *
33709          *     On success, the promise is resolved with the same resource instance or collection object,
33710          *     updated with data from server. This makes it easy to use in
33711          *     {@link ngRoute.$routeProvider resolve section of $routeProvider.when()} to defer view
33712          *     rendering until the resource(s) are loaded.
33713          *
33714          *     On failure, the promise is resolved with the {@link ng.$http http response} object, without
33715          *     the `resource` property.
33716          *
33717          *     If an interceptor object was provided, the promise will instead be resolved with the value
33718          *     returned by the interceptor.
33719          *
33720          *   - `$resolved`: `true` after first server interaction is completed (either with success or
33721          *      rejection), `false` before that. Knowing if the Resource has been resolved is useful in
33722          *      data-binding.
33723          *
33724          * @example
33725          *
33726          * # Credit card resource
33727          *
33728          * ```js
33729              // Define CreditCard class
33730              var CreditCard = $resource('/user/:userId/card/:cardId',
33731               {userId:123, cardId:'@id'}, {
33732                charge: {method:'POST', params:{charge:true}}
33733               });
33734
33735              // We can retrieve a collection from the server
33736              var cards = CreditCard.query(function() {
33737                // GET: /user/123/card
33738                // server returns: [ {id:456, number:'1234', name:'Smith'} ];
33739
33740                var card = cards[0];
33741                // each item is an instance of CreditCard
33742                expect(card instanceof CreditCard).toEqual(true);
33743                card.name = "J. Smith";
33744                // non GET methods are mapped onto the instances
33745                card.$save();
33746                // POST: /user/123/card/456 {id:456, number:'1234', name:'J. Smith'}
33747                // server returns: {id:456, number:'1234', name: 'J. Smith'};
33748
33749                // our custom method is mapped as well.
33750                card.$charge({amount:9.99});
33751                // POST: /user/123/card/456?amount=9.99&charge=true {id:456, number:'1234', name:'J. Smith'}
33752              });
33753
33754              // we can create an instance as well
33755              var newCard = new CreditCard({number:'0123'});
33756              newCard.name = "Mike Smith";
33757              newCard.$save();
33758              // POST: /user/123/card {number:'0123', name:'Mike Smith'}
33759              // server returns: {id:789, number:'0123', name: 'Mike Smith'};
33760              expect(newCard.id).toEqual(789);
33761          * ```
33762          *
33763          * The object returned from this function execution is a resource "class" which has "static" method
33764          * for each action in the definition.
33765          *
33766          * Calling these methods invoke `$http` on the `url` template with the given `method`, `params` and
33767          * `headers`.
33768          * When the data is returned from the server then the object is an instance of the resource type and
33769          * all of the non-GET methods are available with `$` prefix. This allows you to easily support CRUD
33770          * operations (create, read, update, delete) on server-side data.
33771
33772            ```js
33773              var User = $resource('/user/:userId', {userId:'@id'});
33774              User.get({userId:123}, function(user) {
33775                user.abc = true;
33776                user.$save();
33777              });
33778            ```
33779          *
33780          * It's worth noting that the success callback for `get`, `query` and other methods gets passed
33781          * in the response that came from the server as well as $http header getter function, so one
33782          * could rewrite the above example and get access to http headers as:
33783          *
33784            ```js
33785              var User = $resource('/user/:userId', {userId:'@id'});
33786              User.get({userId:123}, function(u, getResponseHeaders){
33787                u.abc = true;
33788                u.$save(function(u, putResponseHeaders) {
33789                  //u => saved user object
33790                  //putResponseHeaders => $http header getter
33791                });
33792              });
33793            ```
33794          *
33795          * You can also access the raw `$http` promise via the `$promise` property on the object returned
33796          *
33797            ```
33798              var User = $resource('/user/:userId', {userId:'@id'});
33799              User.get({userId:123})
33800                  .$promise.then(function(user) {
33801                    $scope.user = user;
33802                  });
33803            ```
33804
33805          * # Creating a custom 'PUT' request
33806          * In this example we create a custom method on our resource to make a PUT request
33807          * ```js
33808          *    var app = angular.module('app', ['ngResource', 'ngRoute']);
33809          *
33810          *    // Some APIs expect a PUT request in the format URL/object/ID
33811          *    // Here we are creating an 'update' method
33812          *    app.factory('Notes', ['$resource', function($resource) {
33813          *    return $resource('/notes/:id', null,
33814          *        {
33815          *            'update': { method:'PUT' }
33816          *        });
33817          *    }]);
33818          *
33819          *    // In our controller we get the ID from the URL using ngRoute and $routeParams
33820          *    // We pass in $routeParams and our Notes factory along with $scope
33821          *    app.controller('NotesCtrl', ['$scope', '$routeParams', 'Notes',
33822                                               function($scope, $routeParams, Notes) {
33823          *    // First get a note object from the factory
33824          *    var note = Notes.get({ id:$routeParams.id });
33825          *    $id = note.id;
33826          *
33827          *    // Now call update passing in the ID first then the object you are updating
33828          *    Notes.update({ id:$id }, note);
33829          *
33830          *    // This will PUT /notes/ID with the note object in the request payload
33831          *    }]);
33832          * ```
33833          */
33834         angular.module('ngResource', ['ng']).
33835           provider('$resource', function() {
33836             var PROTOCOL_AND_DOMAIN_REGEX = /^https?:\/\/[^\/]*/;
33837             var provider = this;
33838
33839             this.defaults = {
33840               // Strip slashes by default
33841               stripTrailingSlashes: true,
33842
33843               // Default actions configuration
33844               actions: {
33845                 'get': {method: 'GET'},
33846                 'save': {method: 'POST'},
33847                 'query': {method: 'GET', isArray: true},
33848                 'remove': {method: 'DELETE'},
33849                 'delete': {method: 'DELETE'}
33850               }
33851             };
33852
33853             this.$get = ['$http', '$q', function($http, $q) {
33854
33855               var noop = angular.noop,
33856                 forEach = angular.forEach,
33857                 extend = angular.extend,
33858                 copy = angular.copy,
33859                 isFunction = angular.isFunction;
33860
33861               /**
33862                * We need our custom method because encodeURIComponent is too aggressive and doesn't follow
33863                * http://www.ietf.org/rfc/rfc3986.txt with regards to the character set
33864                * (pchar) allowed in path segments:
33865                *    segment       = *pchar
33866                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33867                *    pct-encoded   = "%" HEXDIG HEXDIG
33868                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33869                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33870                *                     / "*" / "+" / "," / ";" / "="
33871                */
33872               function encodeUriSegment(val) {
33873                 return encodeUriQuery(val, true).
33874                   replace(/%26/gi, '&').
33875                   replace(/%3D/gi, '=').
33876                   replace(/%2B/gi, '+');
33877               }
33878
33879
33880               /**
33881                * This method is intended for encoding *key* or *value* parts of query component. We need a
33882                * custom method because encodeURIComponent is too aggressive and encodes stuff that doesn't
33883                * have to be encoded per http://tools.ietf.org/html/rfc3986:
33884                *    query       = *( pchar / "/" / "?" )
33885                *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
33886                *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
33887                *    pct-encoded   = "%" HEXDIG HEXDIG
33888                *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
33889                *                     / "*" / "+" / "," / ";" / "="
33890                */
33891               function encodeUriQuery(val, pctEncodeSpaces) {
33892                 return encodeURIComponent(val).
33893                   replace(/%40/gi, '@').
33894                   replace(/%3A/gi, ':').
33895                   replace(/%24/g, '$').
33896                   replace(/%2C/gi, ',').
33897                   replace(/%20/g, (pctEncodeSpaces ? '%20' : '+'));
33898               }
33899
33900               function Route(template, defaults) {
33901                 this.template = template;
33902                 this.defaults = extend({}, provider.defaults, defaults);
33903                 this.urlParams = {};
33904               }
33905
33906               Route.prototype = {
33907                 setUrlParams: function(config, params, actionUrl) {
33908                   var self = this,
33909                     url = actionUrl || self.template,
33910                     val,
33911                     encodedVal,
33912                     protocolAndDomain = '';
33913
33914                   var urlParams = self.urlParams = {};
33915                   forEach(url.split(/\W/), function(param) {
33916                     if (param === 'hasOwnProperty') {
33917                       throw $resourceMinErr('badname', "hasOwnProperty is not a valid parameter name.");
33918                     }
33919                     if (!(new RegExp("^\\d+$").test(param)) && param &&
33920                       (new RegExp("(^|[^\\\\]):" + param + "(\\W|$)").test(url))) {
33921                       urlParams[param] = true;
33922                     }
33923                   });
33924                   url = url.replace(/\\:/g, ':');
33925                   url = url.replace(PROTOCOL_AND_DOMAIN_REGEX, function(match) {
33926                     protocolAndDomain = match;
33927                     return '';
33928                   });
33929
33930                   params = params || {};
33931                   forEach(self.urlParams, function(_, urlParam) {
33932                     val = params.hasOwnProperty(urlParam) ? params[urlParam] : self.defaults[urlParam];
33933                     if (angular.isDefined(val) && val !== null) {
33934                       encodedVal = encodeUriSegment(val);
33935                       url = url.replace(new RegExp(":" + urlParam + "(\\W|$)", "g"), function(match, p1) {
33936                         return encodedVal + p1;
33937                       });
33938                     } else {
33939                       url = url.replace(new RegExp("(\/?):" + urlParam + "(\\W|$)", "g"), function(match,
33940                           leadingSlashes, tail) {
33941                         if (tail.charAt(0) == '/') {
33942                           return tail;
33943                         } else {
33944                           return leadingSlashes + tail;
33945                         }
33946                       });
33947                     }
33948                   });
33949
33950                   // strip trailing slashes and set the url (unless this behavior is specifically disabled)
33951                   if (self.defaults.stripTrailingSlashes) {
33952                     url = url.replace(/\/+$/, '') || '/';
33953                   }
33954
33955                   // then replace collapse `/.` if found in the last URL path segment before the query
33956                   // E.g. `http://url.com/id./format?q=x` becomes `http://url.com/id.format?q=x`
33957                   url = url.replace(/\/\.(?=\w+($|\?))/, '.');
33958                   // replace escaped `/\.` with `/.`
33959                   config.url = protocolAndDomain + url.replace(/\/\\\./, '/.');
33960
33961
33962                   // set params - delegate param encoding to $http
33963                   forEach(params, function(value, key) {
33964                     if (!self.urlParams[key]) {
33965                       config.params = config.params || {};
33966                       config.params[key] = value;
33967                     }
33968                   });
33969                 }
33970               };
33971
33972
33973               function resourceFactory(url, paramDefaults, actions, options) {
33974                 var route = new Route(url, options);
33975
33976                 actions = extend({}, provider.defaults.actions, actions);
33977
33978                 function extractParams(data, actionParams) {
33979                   var ids = {};
33980                   actionParams = extend({}, paramDefaults, actionParams);
33981                   forEach(actionParams, function(value, key) {
33982                     if (isFunction(value)) { value = value(); }
33983                     ids[key] = value && value.charAt && value.charAt(0) == '@' ?
33984                       lookupDottedPath(data, value.substr(1)) : value;
33985                   });
33986                   return ids;
33987                 }
33988
33989                 function defaultResponseInterceptor(response) {
33990                   return response.resource;
33991                 }
33992
33993                 function Resource(value) {
33994                   shallowClearAndCopy(value || {}, this);
33995                 }
33996
33997                 Resource.prototype.toJSON = function() {
33998                   var data = extend({}, this);
33999                   delete data.$promise;
34000                   delete data.$resolved;
34001                   return data;
34002                 };
34003
34004                 forEach(actions, function(action, name) {
34005                   var hasBody = /^(POST|PUT|PATCH)$/i.test(action.method);
34006
34007                   Resource[name] = function(a1, a2, a3, a4) {
34008                     var params = {}, data, success, error;
34009
34010                     /* jshint -W086 */ /* (purposefully fall through case statements) */
34011                     switch (arguments.length) {
34012                       case 4:
34013                         error = a4;
34014                         success = a3;
34015                       //fallthrough
34016                       case 3:
34017                       case 2:
34018                         if (isFunction(a2)) {
34019                           if (isFunction(a1)) {
34020                             success = a1;
34021                             error = a2;
34022                             break;
34023                           }
34024
34025                           success = a2;
34026                           error = a3;
34027                           //fallthrough
34028                         } else {
34029                           params = a1;
34030                           data = a2;
34031                           success = a3;
34032                           break;
34033                         }
34034                       case 1:
34035                         if (isFunction(a1)) success = a1;
34036                         else if (hasBody) data = a1;
34037                         else params = a1;
34038                         break;
34039                       case 0: break;
34040                       default:
34041                         throw $resourceMinErr('badargs',
34042                           "Expected up to 4 arguments [params, data, success, error], got {0} arguments",
34043                           arguments.length);
34044                     }
34045                     /* jshint +W086 */ /* (purposefully fall through case statements) */
34046
34047                     var isInstanceCall = this instanceof Resource;
34048                     var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
34049                     var httpConfig = {};
34050                     var responseInterceptor = action.interceptor && action.interceptor.response ||
34051                       defaultResponseInterceptor;
34052                     var responseErrorInterceptor = action.interceptor && action.interceptor.responseError ||
34053                       undefined;
34054
34055                     forEach(action, function(value, key) {
34056                       switch (key) {
34057                         default:
34058                           httpConfig[key] = copy(value);
34059                           break;
34060                         case 'params':
34061                         case 'isArray':
34062                         case 'interceptor':
34063                           break;
34064                         case 'timeout':
34065                           httpConfig[key] = value;
34066                           break;
34067                       }
34068                     });
34069
34070                     if (hasBody) httpConfig.data = data;
34071                     route.setUrlParams(httpConfig,
34072                       extend({}, extractParams(data, action.params || {}), params),
34073                       action.url);
34074
34075                     var promise = $http(httpConfig).then(function(response) {
34076                       var data = response.data,
34077                         promise = value.$promise;
34078
34079                       if (data) {
34080                         // Need to convert action.isArray to boolean in case it is undefined
34081                         // jshint -W018
34082                         if (angular.isArray(data) !== (!!action.isArray)) {
34083                           throw $resourceMinErr('badcfg',
34084                               'Error in resource configuration for action `{0}`. Expected response to ' +
34085                               'contain an {1} but got an {2} (Request: {3} {4})', name, action.isArray ? 'array' : 'object',
34086                             angular.isArray(data) ? 'array' : 'object', httpConfig.method, httpConfig.url);
34087                         }
34088                         // jshint +W018
34089                         if (action.isArray) {
34090                           value.length = 0;
34091                           forEach(data, function(item) {
34092                             if (typeof item === "object") {
34093                               value.push(new Resource(item));
34094                             } else {
34095                               // Valid JSON values may be string literals, and these should not be converted
34096                               // into objects. These items will not have access to the Resource prototype
34097                               // methods, but unfortunately there
34098                               value.push(item);
34099                             }
34100                           });
34101                         } else {
34102                           shallowClearAndCopy(data, value);
34103                           value.$promise = promise;
34104                         }
34105                       }
34106
34107                       value.$resolved = true;
34108
34109                       response.resource = value;
34110
34111                       return response;
34112                     }, function(response) {
34113                       value.$resolved = true;
34114
34115                       (error || noop)(response);
34116
34117                       return $q.reject(response);
34118                     });
34119
34120                     promise = promise.then(
34121                       function(response) {
34122                         var value = responseInterceptor(response);
34123                         (success || noop)(value, response.headers);
34124                         return value;
34125                       },
34126                       responseErrorInterceptor);
34127
34128                     if (!isInstanceCall) {
34129                       // we are creating instance / collection
34130                       // - set the initial promise
34131                       // - return the instance / collection
34132                       value.$promise = promise;
34133                       value.$resolved = false;
34134
34135                       return value;
34136                     }
34137
34138                     // instance call
34139                     return promise;
34140                   };
34141
34142
34143                   Resource.prototype['$' + name] = function(params, success, error) {
34144                     if (isFunction(params)) {
34145                       error = success; success = params; params = {};
34146                     }
34147                     var result = Resource[name].call(this, params, this, success, error);
34148                     return result.$promise || result;
34149                   };
34150                 });
34151
34152                 Resource.bind = function(additionalParamDefaults) {
34153                   return resourceFactory(url, extend({}, paramDefaults, additionalParamDefaults), actions);
34154                 };
34155
34156                 return Resource;
34157               }
34158
34159               return resourceFactory;
34160             }];
34161           });
34162
34163
34164         })(window, window.angular);
34165
34166
34167 /***/ },
34168 /* 6 */
34169 /***/ function(module, exports, __webpack_require__) {
34170
34171         __webpack_require__(7);
34172
34173         module.exports = 'ui.bootstrap';
34174
34175
34176 /***/ },
34177 /* 7 */
34178 /***/ function(module, exports) {
34179
34180         /*
34181          * angular-ui-bootstrap
34182          * http://angular-ui.github.io/bootstrap/
34183
34184          * Version: 1.0.0 - 2016-01-08
34185          * License: MIT
34186          */
34187         angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.isClass","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.debounce","ui.bootstrap.dropdown","ui.bootstrap.stackedMap","ui.bootstrap.modal","ui.bootstrap.paging","ui.bootstrap.pager","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]);
34188         angular.module("ui.bootstrap.tpls", ["uib/template/accordion/accordion-group.html","uib/template/accordion/accordion.html","uib/template/alert/alert.html","uib/template/carousel/carousel.html","uib/template/carousel/slide.html","uib/template/datepicker/datepicker.html","uib/template/datepicker/day.html","uib/template/datepicker/month.html","uib/template/datepicker/popup.html","uib/template/datepicker/year.html","uib/template/modal/backdrop.html","uib/template/modal/window.html","uib/template/pager/pager.html","uib/template/pagination/pagination.html","uib/template/tooltip/tooltip-html-popup.html","uib/template/tooltip/tooltip-popup.html","uib/template/tooltip/tooltip-template-popup.html","uib/template/popover/popover-html.html","uib/template/popover/popover-template.html","uib/template/popover/popover.html","uib/template/progressbar/bar.html","uib/template/progressbar/progress.html","uib/template/progressbar/progressbar.html","uib/template/rating/rating.html","uib/template/tabs/tab.html","uib/template/tabs/tabset.html","uib/template/timepicker/timepicker.html","uib/template/typeahead/typeahead-match.html","uib/template/typeahead/typeahead-popup.html"]);
34189         angular.module('ui.bootstrap.collapse', [])
34190
34191           .directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) {
34192             var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null;
34193             return {
34194               link: function(scope, element, attrs) {
34195                 if (!scope.$eval(attrs.uibCollapse)) {
34196                   element.addClass('in')
34197                     .addClass('collapse')
34198                     .css({height: 'auto'});
34199                 }
34200
34201                 function expand() {
34202                   element.removeClass('collapse')
34203                     .addClass('collapsing')
34204                     .attr('aria-expanded', true)
34205                     .attr('aria-hidden', false);
34206
34207                   if ($animateCss) {
34208                     $animateCss(element, {
34209                       addClass: 'in',
34210                       easing: 'ease',
34211                       to: { height: element[0].scrollHeight + 'px' }
34212                     }).start()['finally'](expandDone);
34213                   } else {
34214                     $animate.addClass(element, 'in', {
34215                       to: { height: element[0].scrollHeight + 'px' }
34216                     }).then(expandDone);
34217                   }
34218                 }
34219
34220                 function expandDone() {
34221                   element.removeClass('collapsing')
34222                     .addClass('collapse')
34223                     .css({height: 'auto'});
34224                 }
34225
34226                 function collapse() {
34227                   if (!element.hasClass('collapse') && !element.hasClass('in')) {
34228                     return collapseDone();
34229                   }
34230
34231                   element
34232                     // IMPORTANT: The height must be set before adding "collapsing" class.
34233                     // Otherwise, the browser attempts to animate from height 0 (in
34234                     // collapsing class) to the given height here.
34235                     .css({height: element[0].scrollHeight + 'px'})
34236                     // initially all panel collapse have the collapse class, this removal
34237                     // prevents the animation from jumping to collapsed state
34238                     .removeClass('collapse')
34239                     .addClass('collapsing')
34240                     .attr('aria-expanded', false)
34241                     .attr('aria-hidden', true);
34242
34243                   if ($animateCss) {
34244                     $animateCss(element, {
34245                       removeClass: 'in',
34246                       to: {height: '0'}
34247                     }).start()['finally'](collapseDone);
34248                   } else {
34249                     $animate.removeClass(element, 'in', {
34250                       to: {height: '0'}
34251                     }).then(collapseDone);
34252                   }
34253                 }
34254
34255                 function collapseDone() {
34256                   element.css({height: '0'}); // Required so that collapse works when animation is disabled
34257                   element.removeClass('collapsing')
34258                     .addClass('collapse');
34259                 }
34260
34261                 scope.$watch(attrs.uibCollapse, function(shouldCollapse) {
34262                   if (shouldCollapse) {
34263                     collapse();
34264                   } else {
34265                     expand();
34266                   }
34267                 });
34268               }
34269             };
34270           }]);
34271
34272         angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
34273
34274         .constant('uibAccordionConfig', {
34275           closeOthers: true
34276         })
34277
34278         .controller('UibAccordionController', ['$scope', '$attrs', 'uibAccordionConfig', function($scope, $attrs, accordionConfig) {
34279           // This array keeps track of the accordion groups
34280           this.groups = [];
34281
34282           // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
34283           this.closeOthers = function(openGroup) {
34284             var closeOthers = angular.isDefined($attrs.closeOthers) ?
34285               $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
34286             if (closeOthers) {
34287               angular.forEach(this.groups, function(group) {
34288                 if (group !== openGroup) {
34289                   group.isOpen = false;
34290                 }
34291               });
34292             }
34293           };
34294
34295           // This is called from the accordion-group directive to add itself to the accordion
34296           this.addGroup = function(groupScope) {
34297             var that = this;
34298             this.groups.push(groupScope);
34299
34300             groupScope.$on('$destroy', function(event) {
34301               that.removeGroup(groupScope);
34302             });
34303           };
34304
34305           // This is called from the accordion-group directive when to remove itself
34306           this.removeGroup = function(group) {
34307             var index = this.groups.indexOf(group);
34308             if (index !== -1) {
34309               this.groups.splice(index, 1);
34310             }
34311           };
34312         }])
34313
34314         // The accordion directive simply sets up the directive controller
34315         // and adds an accordion CSS class to itself element.
34316         .directive('uibAccordion', function() {
34317           return {
34318             controller: 'UibAccordionController',
34319             controllerAs: 'accordion',
34320             transclude: true,
34321             templateUrl: function(element, attrs) {
34322               return attrs.templateUrl || 'uib/template/accordion/accordion.html';
34323             }
34324           };
34325         })
34326
34327         // The accordion-group directive indicates a block of html that will expand and collapse in an accordion
34328         .directive('uibAccordionGroup', function() {
34329           return {
34330             require: '^uibAccordion',         // We need this directive to be inside an accordion
34331             transclude: true,              // It transcludes the contents of the directive into the template
34332             replace: true,                // The element containing the directive will be replaced with the template
34333             templateUrl: function(element, attrs) {
34334               return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
34335             },
34336             scope: {
34337               heading: '@',               // Interpolate the heading attribute onto this scope
34338               isOpen: '=?',
34339               isDisabled: '=?'
34340             },
34341             controller: function() {
34342               this.setHeading = function(element) {
34343                 this.heading = element;
34344               };
34345             },
34346             link: function(scope, element, attrs, accordionCtrl) {
34347               accordionCtrl.addGroup(scope);
34348
34349               scope.openClass = attrs.openClass || 'panel-open';
34350               scope.panelClass = attrs.panelClass || 'panel-default';
34351               scope.$watch('isOpen', function(value) {
34352                 element.toggleClass(scope.openClass, !!value);
34353                 if (value) {
34354                   accordionCtrl.closeOthers(scope);
34355                 }
34356               });
34357
34358               scope.toggleOpen = function($event) {
34359                 if (!scope.isDisabled) {
34360                   if (!$event || $event.which === 32) {
34361                     scope.isOpen = !scope.isOpen;
34362                   }
34363                 }
34364               };
34365             }
34366           };
34367         })
34368
34369         // Use accordion-heading below an accordion-group to provide a heading containing HTML
34370         .directive('uibAccordionHeading', function() {
34371           return {
34372             transclude: true,   // Grab the contents to be used as the heading
34373             template: '',       // In effect remove this element!
34374             replace: true,
34375             require: '^uibAccordionGroup',
34376             link: function(scope, element, attrs, accordionGroupCtrl, transclude) {
34377               // Pass the heading to the accordion-group controller
34378               // so that it can be transcluded into the right place in the template
34379               // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
34380               accordionGroupCtrl.setHeading(transclude(scope, angular.noop));
34381             }
34382           };
34383         })
34384
34385         // Use in the accordion-group template to indicate where you want the heading to be transcluded
34386         // You must provide the property on the accordion-group controller that will hold the transcluded element
34387         .directive('uibAccordionTransclude', function() {
34388           return {
34389             require: '^uibAccordionGroup',
34390             link: function(scope, element, attrs, controller) {
34391               scope.$watch(function() { return controller[attrs.uibAccordionTransclude]; }, function(heading) {
34392                 if (heading) {
34393                   element.find('span').html('');
34394                   element.find('span').append(heading);
34395                 }
34396               });
34397             }
34398           };
34399         });
34400
34401         angular.module('ui.bootstrap.alert', [])
34402
34403         .controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
34404           $scope.closeable = !!$attrs.close;
34405
34406           var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
34407             $interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
34408
34409           if (dismissOnTimeout) {
34410             $timeout(function() {
34411               $scope.close();
34412             }, parseInt(dismissOnTimeout, 10));
34413           }
34414         }])
34415
34416         .directive('uibAlert', function() {
34417           return {
34418             controller: 'UibAlertController',
34419             controllerAs: 'alert',
34420             templateUrl: function(element, attrs) {
34421               return attrs.templateUrl || 'uib/template/alert/alert.html';
34422             },
34423             transclude: true,
34424             replace: true,
34425             scope: {
34426               type: '@',
34427               close: '&'
34428             }
34429           };
34430         });
34431
34432         angular.module('ui.bootstrap.buttons', [])
34433
34434         .constant('uibButtonConfig', {
34435           activeClass: 'active',
34436           toggleEvent: 'click'
34437         })
34438
34439         .controller('UibButtonsController', ['uibButtonConfig', function(buttonConfig) {
34440           this.activeClass = buttonConfig.activeClass || 'active';
34441           this.toggleEvent = buttonConfig.toggleEvent || 'click';
34442         }])
34443
34444         .directive('uibBtnRadio', ['$parse', function($parse) {
34445           return {
34446             require: ['uibBtnRadio', 'ngModel'],
34447             controller: 'UibButtonsController',
34448             controllerAs: 'buttons',
34449             link: function(scope, element, attrs, ctrls) {
34450               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34451               var uncheckableExpr = $parse(attrs.uibUncheckable);
34452
34453               element.find('input').css({display: 'none'});
34454
34455               //model -> UI
34456               ngModelCtrl.$render = function() {
34457                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.uibBtnRadio)));
34458               };
34459
34460               //ui->model
34461               element.on(buttonsCtrl.toggleEvent, function() {
34462                 if (attrs.disabled) {
34463                   return;
34464                 }
34465
34466                 var isActive = element.hasClass(buttonsCtrl.activeClass);
34467
34468                 if (!isActive || angular.isDefined(attrs.uncheckable)) {
34469                   scope.$apply(function() {
34470                     ngModelCtrl.$setViewValue(isActive ? null : scope.$eval(attrs.uibBtnRadio));
34471                     ngModelCtrl.$render();
34472                   });
34473                 }
34474               });
34475
34476               if (attrs.uibUncheckable) {
34477                 scope.$watch(uncheckableExpr, function(uncheckable) {
34478                   attrs.$set('uncheckable', uncheckable ? '' : null);
34479                 });
34480               }
34481             }
34482           };
34483         }])
34484
34485         .directive('uibBtnCheckbox', function() {
34486           return {
34487             require: ['uibBtnCheckbox', 'ngModel'],
34488             controller: 'UibButtonsController',
34489             controllerAs: 'button',
34490             link: function(scope, element, attrs, ctrls) {
34491               var buttonsCtrl = ctrls[0], ngModelCtrl = ctrls[1];
34492
34493               element.find('input').css({display: 'none'});
34494
34495               function getTrueValue() {
34496                 return getCheckboxValue(attrs.btnCheckboxTrue, true);
34497               }
34498
34499               function getFalseValue() {
34500                 return getCheckboxValue(attrs.btnCheckboxFalse, false);
34501               }
34502
34503               function getCheckboxValue(attribute, defaultValue) {
34504                 return angular.isDefined(attribute) ? scope.$eval(attribute) : defaultValue;
34505               }
34506
34507               //model -> UI
34508               ngModelCtrl.$render = function() {
34509                 element.toggleClass(buttonsCtrl.activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
34510               };
34511
34512               //ui->model
34513               element.on(buttonsCtrl.toggleEvent, function() {
34514                 if (attrs.disabled) {
34515                   return;
34516                 }
34517
34518                 scope.$apply(function() {
34519                   ngModelCtrl.$setViewValue(element.hasClass(buttonsCtrl.activeClass) ? getFalseValue() : getTrueValue());
34520                   ngModelCtrl.$render();
34521                 });
34522               });
34523             }
34524           };
34525         });
34526
34527         angular.module('ui.bootstrap.carousel', [])
34528
34529         .controller('UibCarouselController', ['$scope', '$element', '$interval', '$timeout', '$animate', function($scope, $element, $interval, $timeout, $animate) {
34530           var self = this,
34531             slides = self.slides = $scope.slides = [],
34532             SLIDE_DIRECTION = 'uib-slideDirection',
34533             currentIndex = -1,
34534             currentInterval, isPlaying, bufferedTransitions = [];
34535           self.currentSlide = null;
34536
34537           var destroyed = false;
34538
34539           self.addSlide = function(slide, element) {
34540             slide.$element = element;
34541             slides.push(slide);
34542             //if this is the first slide or the slide is set to active, select it
34543             if (slides.length === 1 || slide.active) {
34544               if ($scope.$currentTransition) {
34545                 $scope.$currentTransition = null;
34546               }
34547
34548               self.select(slides[slides.length - 1]);
34549               if (slides.length === 1) {
34550                 $scope.play();
34551               }
34552             } else {
34553               slide.active = false;
34554             }
34555           };
34556
34557           self.getCurrentIndex = function() {
34558             if (self.currentSlide && angular.isDefined(self.currentSlide.index)) {
34559               return +self.currentSlide.index;
34560             }
34561             return currentIndex;
34562           };
34563
34564           self.next = $scope.next = function() {
34565             var newIndex = (self.getCurrentIndex() + 1) % slides.length;
34566
34567             if (newIndex === 0 && $scope.noWrap()) {
34568               $scope.pause();
34569               return;
34570             }
34571
34572             return self.select(getSlideByIndex(newIndex), 'next');
34573           };
34574
34575           self.prev = $scope.prev = function() {
34576             var newIndex = self.getCurrentIndex() - 1 < 0 ? slides.length - 1 : self.getCurrentIndex() - 1;
34577
34578             if ($scope.noWrap() && newIndex === slides.length - 1) {
34579               $scope.pause();
34580               return;
34581             }
34582
34583             return self.select(getSlideByIndex(newIndex), 'prev');
34584           };
34585
34586           self.removeSlide = function(slide) {
34587             if (angular.isDefined(slide.index)) {
34588               slides.sort(function(a, b) {
34589                 return +a.index > +b.index;
34590               });
34591             }
34592
34593             var bufferedIndex = bufferedTransitions.indexOf(slide);
34594             if (bufferedIndex !== -1) {
34595               bufferedTransitions.splice(bufferedIndex, 1);
34596             }
34597             //get the index of the slide inside the carousel
34598             var index = slides.indexOf(slide);
34599             slides.splice(index, 1);
34600             $timeout(function() {
34601               if (slides.length > 0 && slide.active) {
34602                 if (index >= slides.length) {
34603                   self.select(slides[index - 1]);
34604                 } else {
34605                   self.select(slides[index]);
34606                 }
34607               } else if (currentIndex > index) {
34608                 currentIndex--;
34609               }
34610             });
34611
34612             //clean the currentSlide when no more slide
34613             if (slides.length === 0) {
34614               self.currentSlide = null;
34615               clearBufferedTransitions();
34616             }
34617           };
34618
34619           /* direction: "prev" or "next" */
34620           self.select = $scope.select = function(nextSlide, direction) {
34621             var nextIndex = $scope.indexOfSlide(nextSlide);
34622             //Decide direction if it's not given
34623             if (direction === undefined) {
34624               direction = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34625             }
34626             //Prevent this user-triggered transition from occurring if there is already one in progress
34627             if (nextSlide && nextSlide !== self.currentSlide && !$scope.$currentTransition) {
34628               goNext(nextSlide, nextIndex, direction);
34629             } else if (nextSlide && nextSlide !== self.currentSlide && $scope.$currentTransition) {
34630               bufferedTransitions.push(nextSlide);
34631             }
34632           };
34633
34634           /* Allow outside people to call indexOf on slides array */
34635           $scope.indexOfSlide = function(slide) {
34636             return angular.isDefined(slide.index) ? +slide.index : slides.indexOf(slide);
34637           };
34638
34639           $scope.isActive = function(slide) {
34640             return self.currentSlide === slide;
34641           };
34642
34643           $scope.pause = function() {
34644             if (!$scope.noPause) {
34645               isPlaying = false;
34646               resetTimer();
34647             }
34648           };
34649
34650           $scope.play = function() {
34651             if (!isPlaying) {
34652               isPlaying = true;
34653               restartTimer();
34654             }
34655           };
34656
34657           $scope.$on('$destroy', function() {
34658             destroyed = true;
34659             resetTimer();
34660           });
34661
34662           $scope.$watch('noTransition', function(noTransition) {
34663             $animate.enabled($element, !noTransition);
34664           });
34665
34666           $scope.$watch('interval', restartTimer);
34667
34668           $scope.$watchCollection('slides', resetTransition);
34669
34670           function clearBufferedTransitions() {
34671             while (bufferedTransitions.length) {
34672               bufferedTransitions.shift();
34673             }
34674           }
34675
34676           function getSlideByIndex(index) {
34677             if (angular.isUndefined(slides[index].index)) {
34678               return slides[index];
34679             }
34680             for (var i = 0, l = slides.length; i < l; ++i) {
34681               if (slides[i].index === index) {
34682                 return slides[i];
34683               }
34684             }
34685           }
34686
34687           function goNext(slide, index, direction) {
34688             if (destroyed) { return; }
34689
34690             angular.extend(slide, {direction: direction, active: true});
34691             angular.extend(self.currentSlide || {}, {direction: direction, active: false});
34692             if ($animate.enabled($element) && !$scope.$currentTransition &&
34693               slide.$element && self.slides.length > 1) {
34694               slide.$element.data(SLIDE_DIRECTION, slide.direction);
34695               if (self.currentSlide && self.currentSlide.$element) {
34696                 self.currentSlide.$element.data(SLIDE_DIRECTION, slide.direction);
34697               }
34698
34699               $scope.$currentTransition = true;
34700               $animate.on('addClass', slide.$element, function(element, phase) {
34701                 if (phase === 'close') {
34702                   $scope.$currentTransition = null;
34703                   $animate.off('addClass', element);
34704                   if (bufferedTransitions.length) {
34705                     var nextSlide = bufferedTransitions.pop();
34706                     var nextIndex = $scope.indexOfSlide(nextSlide);
34707                     var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
34708                     clearBufferedTransitions();
34709
34710                     goNext(nextSlide, nextIndex, nextDirection);
34711                   }
34712                 }
34713               });
34714             }
34715
34716             self.currentSlide = slide;
34717             currentIndex = index;
34718
34719             //every time you change slides, reset the timer
34720             restartTimer();
34721           }
34722
34723           function resetTimer() {
34724             if (currentInterval) {
34725               $interval.cancel(currentInterval);
34726               currentInterval = null;
34727             }
34728           }
34729
34730           function resetTransition(slides) {
34731             if (!slides.length) {
34732               $scope.$currentTransition = null;
34733               clearBufferedTransitions();
34734             }
34735           }
34736
34737           function restartTimer() {
34738             resetTimer();
34739             var interval = +$scope.interval;
34740             if (!isNaN(interval) && interval > 0) {
34741               currentInterval = $interval(timerFn, interval);
34742             }
34743           }
34744
34745           function timerFn() {
34746             var interval = +$scope.interval;
34747             if (isPlaying && !isNaN(interval) && interval > 0 && slides.length) {
34748               $scope.next();
34749             } else {
34750               $scope.pause();
34751             }
34752           }
34753         }])
34754
34755         .directive('uibCarousel', function() {
34756           return {
34757             transclude: true,
34758             replace: true,
34759             controller: 'UibCarouselController',
34760             controllerAs: 'carousel',
34761             templateUrl: function(element, attrs) {
34762               return attrs.templateUrl || 'uib/template/carousel/carousel.html';
34763             },
34764             scope: {
34765               interval: '=',
34766               noTransition: '=',
34767               noPause: '=',
34768               noWrap: '&'
34769             }
34770           };
34771         })
34772
34773         .directive('uibSlide', function() {
34774           return {
34775             require: '^uibCarousel',
34776             transclude: true,
34777             replace: true,
34778             templateUrl: function(element, attrs) {
34779               return attrs.templateUrl || 'uib/template/carousel/slide.html';
34780             },
34781             scope: {
34782               active: '=?',
34783               actual: '=?',
34784               index: '=?'
34785             },
34786             link: function (scope, element, attrs, carouselCtrl) {
34787               carouselCtrl.addSlide(scope, element);
34788               //when the scope is destroyed then remove the slide from the current slides array
34789               scope.$on('$destroy', function() {
34790                 carouselCtrl.removeSlide(scope);
34791               });
34792
34793               scope.$watch('active', function(active) {
34794                 if (active) {
34795                   carouselCtrl.select(scope);
34796                 }
34797               });
34798             }
34799           };
34800         })
34801
34802         .animation('.item', ['$animateCss',
34803         function($animateCss) {
34804           var SLIDE_DIRECTION = 'uib-slideDirection';
34805
34806           function removeClass(element, className, callback) {
34807             element.removeClass(className);
34808             if (callback) {
34809               callback();
34810             }
34811           }
34812
34813           return {
34814             beforeAddClass: function(element, className, done) {
34815               if (className === 'active') {
34816                 var stopped = false;
34817                 var direction = element.data(SLIDE_DIRECTION);
34818                 var directionClass = direction === 'next' ? 'left' : 'right';
34819                 var removeClassFn = removeClass.bind(this, element,
34820                   directionClass + ' ' + direction, done);
34821                 element.addClass(direction);
34822
34823                 $animateCss(element, {addClass: directionClass})
34824                   .start()
34825                   .done(removeClassFn);
34826
34827                 return function() {
34828                   stopped = true;
34829                 };
34830               }
34831               done();
34832             },
34833             beforeRemoveClass: function (element, className, done) {
34834               if (className === 'active') {
34835                 var stopped = false;
34836                 var direction = element.data(SLIDE_DIRECTION);
34837                 var directionClass = direction === 'next' ? 'left' : 'right';
34838                 var removeClassFn = removeClass.bind(this, element, directionClass, done);
34839
34840                 $animateCss(element, {addClass: directionClass})
34841                   .start()
34842                   .done(removeClassFn);
34843
34844                 return function() {
34845                   stopped = true;
34846                 };
34847               }
34848               done();
34849             }
34850           };
34851         }]);
34852
34853         angular.module('ui.bootstrap.dateparser', [])
34854
34855         .service('uibDateParser', ['$log', '$locale', 'orderByFilter', function($log, $locale, orderByFilter) {
34856           // Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
34857           var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
34858
34859           var localeId;
34860           var formatCodeToRegex;
34861
34862           this.init = function() {
34863             localeId = $locale.id;
34864
34865             this.parsers = {};
34866
34867             formatCodeToRegex = [
34868               {
34869                 key: 'yyyy',
34870                 regex: '\\d{4}',
34871                 apply: function(value) { this.year = +value; }
34872               },
34873               {
34874                 key: 'yy',
34875                 regex: '\\d{2}',
34876                 apply: function(value) { this.year = +value + 2000; }
34877               },
34878               {
34879                 key: 'y',
34880                 regex: '\\d{1,4}',
34881                 apply: function(value) { this.year = +value; }
34882               },
34883               {
34884                 key: 'M!',
34885                 regex: '0?[1-9]|1[0-2]',
34886                 apply: function(value) { this.month = value - 1; }
34887               },
34888               {
34889                 key: 'MMMM',
34890                 regex: $locale.DATETIME_FORMATS.MONTH.join('|'),
34891                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.MONTH.indexOf(value); }
34892               },
34893               {
34894                 key: 'MMM',
34895                 regex: $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
34896                 apply: function(value) { this.month = $locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value); }
34897               },
34898               {
34899                 key: 'MM',
34900                 regex: '0[1-9]|1[0-2]',
34901                 apply: function(value) { this.month = value - 1; }
34902               },
34903               {
34904                 key: 'M',
34905                 regex: '[1-9]|1[0-2]',
34906                 apply: function(value) { this.month = value - 1; }
34907               },
34908               {
34909                 key: 'd!',
34910                 regex: '[0-2]?[0-9]{1}|3[0-1]{1}',
34911                 apply: function(value) { this.date = +value; }
34912               },
34913               {
34914                 key: 'dd',
34915                 regex: '[0-2][0-9]{1}|3[0-1]{1}',
34916                 apply: function(value) { this.date = +value; }
34917               },
34918               {
34919                 key: 'd',
34920                 regex: '[1-2]?[0-9]{1}|3[0-1]{1}',
34921                 apply: function(value) { this.date = +value; }
34922               },
34923               {
34924                 key: 'EEEE',
34925                 regex: $locale.DATETIME_FORMATS.DAY.join('|')
34926               },
34927               {
34928                 key: 'EEE',
34929                 regex: $locale.DATETIME_FORMATS.SHORTDAY.join('|')
34930               },
34931               {
34932                 key: 'HH',
34933                 regex: '(?:0|1)[0-9]|2[0-3]',
34934                 apply: function(value) { this.hours = +value; }
34935               },
34936               {
34937                 key: 'hh',
34938                 regex: '0[0-9]|1[0-2]',
34939                 apply: function(value) { this.hours = +value; }
34940               },
34941               {
34942                 key: 'H',
34943                 regex: '1?[0-9]|2[0-3]',
34944                 apply: function(value) { this.hours = +value; }
34945               },
34946               {
34947                 key: 'h',
34948                 regex: '[0-9]|1[0-2]',
34949                 apply: function(value) { this.hours = +value; }
34950               },
34951               {
34952                 key: 'mm',
34953                 regex: '[0-5][0-9]',
34954                 apply: function(value) { this.minutes = +value; }
34955               },
34956               {
34957                 key: 'm',
34958                 regex: '[0-9]|[1-5][0-9]',
34959                 apply: function(value) { this.minutes = +value; }
34960               },
34961               {
34962                 key: 'sss',
34963                 regex: '[0-9][0-9][0-9]',
34964                 apply: function(value) { this.milliseconds = +value; }
34965               },
34966               {
34967                 key: 'ss',
34968                 regex: '[0-5][0-9]',
34969                 apply: function(value) { this.seconds = +value; }
34970               },
34971               {
34972                 key: 's',
34973                 regex: '[0-9]|[1-5][0-9]',
34974                 apply: function(value) { this.seconds = +value; }
34975               },
34976               {
34977                 key: 'a',
34978                 regex: $locale.DATETIME_FORMATS.AMPMS.join('|'),
34979                 apply: function(value) {
34980                   if (this.hours === 12) {
34981                     this.hours = 0;
34982                   }
34983
34984                   if (value === 'PM') {
34985                     this.hours += 12;
34986                   }
34987                 }
34988               },
34989               {
34990                 key: 'Z',
34991                 regex: '[+-]\\d{4}',
34992                 apply: function(value) {
34993                   var matches = value.match(/([+-])(\d{2})(\d{2})/),
34994                     sign = matches[1],
34995                     hours = matches[2],
34996                     minutes = matches[3];
34997                   this.hours += toInt(sign + hours);
34998                   this.minutes += toInt(sign + minutes);
34999                 }
35000               },
35001               {
35002                 key: 'ww',
35003                 regex: '[0-4][0-9]|5[0-3]'
35004               },
35005               {
35006                 key: 'w',
35007                 regex: '[0-9]|[1-4][0-9]|5[0-3]'
35008               },
35009               {
35010                 key: 'GGGG',
35011                 regex: $locale.DATETIME_FORMATS.ERANAMES.join('|').replace(/\s/g, '\\s')
35012               },
35013               {
35014                 key: 'GGG',
35015                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35016               },
35017               {
35018                 key: 'GG',
35019                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35020               },
35021               {
35022                 key: 'G',
35023                 regex: $locale.DATETIME_FORMATS.ERAS.join('|')
35024               }
35025             ];
35026           };
35027
35028           this.init();
35029
35030           function createParser(format) {
35031             var map = [], regex = format.split('');
35032
35033             // check for literal values
35034             var quoteIndex = format.indexOf('\'');
35035             if (quoteIndex > -1) {
35036               var inLiteral = false;
35037               format = format.split('');
35038               for (var i = quoteIndex; i < format.length; i++) {
35039                 if (inLiteral) {
35040                   if (format[i] === '\'') {
35041                     if (i + 1 < format.length && format[i+1] === '\'') { // escaped single quote
35042                       format[i+1] = '$';
35043                       regex[i+1] = '';
35044                     } else { // end of literal
35045                       regex[i] = '';
35046                       inLiteral = false;
35047                     }
35048                   }
35049                   format[i] = '$';
35050                 } else {
35051                   if (format[i] === '\'') { // start of literal
35052                     format[i] = '$';
35053                     regex[i] = '';
35054                     inLiteral = true;
35055                   }
35056                 }
35057               }
35058
35059               format = format.join('');
35060             }
35061
35062             angular.forEach(formatCodeToRegex, function(data) {
35063               var index = format.indexOf(data.key);
35064
35065               if (index > -1) {
35066                 format = format.split('');
35067
35068                 regex[index] = '(' + data.regex + ')';
35069                 format[index] = '$'; // Custom symbol to define consumed part of format
35070                 for (var i = index + 1, n = index + data.key.length; i < n; i++) {
35071                   regex[i] = '';
35072                   format[i] = '$';
35073                 }
35074                 format = format.join('');
35075
35076                 map.push({
35077                   index: index,
35078                   apply: data.apply,
35079                   matcher: data.regex
35080                 });
35081               }
35082             });
35083
35084             return {
35085               regex: new RegExp('^' + regex.join('') + '$'),
35086               map: orderByFilter(map, 'index')
35087             };
35088           }
35089
35090           this.parse = function(input, format, baseDate) {
35091             if (!angular.isString(input) || !format) {
35092               return input;
35093             }
35094
35095             format = $locale.DATETIME_FORMATS[format] || format;
35096             format = format.replace(SPECIAL_CHARACTERS_REGEXP, '\\$&');
35097
35098             if ($locale.id !== localeId) {
35099               this.init();
35100             }
35101
35102             if (!this.parsers[format]) {
35103               this.parsers[format] = createParser(format);
35104             }
35105
35106             var parser = this.parsers[format],
35107                 regex = parser.regex,
35108                 map = parser.map,
35109                 results = input.match(regex),
35110                 tzOffset = false;
35111             if (results && results.length) {
35112               var fields, dt;
35113               if (angular.isDate(baseDate) && !isNaN(baseDate.getTime())) {
35114                 fields = {
35115                   year: baseDate.getFullYear(),
35116                   month: baseDate.getMonth(),
35117                   date: baseDate.getDate(),
35118                   hours: baseDate.getHours(),
35119                   minutes: baseDate.getMinutes(),
35120                   seconds: baseDate.getSeconds(),
35121                   milliseconds: baseDate.getMilliseconds()
35122                 };
35123               } else {
35124                 if (baseDate) {
35125                   $log.warn('dateparser:', 'baseDate is not a valid date');
35126                 }
35127                 fields = { year: 1900, month: 0, date: 1, hours: 0, minutes: 0, seconds: 0, milliseconds: 0 };
35128               }
35129
35130               for (var i = 1, n = results.length; i < n; i++) {
35131                 var mapper = map[i - 1];
35132                 if (mapper.matcher === 'Z') {
35133                   tzOffset = true;
35134                 }
35135
35136                 if (mapper.apply) {
35137                   mapper.apply.call(fields, results[i]);
35138                 }
35139               }
35140
35141               var datesetter = tzOffset ? Date.prototype.setUTCFullYear :
35142                 Date.prototype.setFullYear;
35143               var timesetter = tzOffset ? Date.prototype.setUTCHours :
35144                 Date.prototype.setHours;
35145
35146               if (isValid(fields.year, fields.month, fields.date)) {
35147                 if (angular.isDate(baseDate) && !isNaN(baseDate.getTime()) && !tzOffset) {
35148                   dt = new Date(baseDate);
35149                   datesetter.call(dt, fields.year, fields.month, fields.date);
35150                   timesetter.call(dt, fields.hours, fields.minutes,
35151                     fields.seconds, fields.milliseconds);
35152                 } else {
35153                   dt = new Date(0);
35154                   datesetter.call(dt, fields.year, fields.month, fields.date);
35155                   timesetter.call(dt, fields.hours || 0, fields.minutes || 0,
35156                     fields.seconds || 0, fields.milliseconds || 0);
35157                 }
35158               }
35159
35160               return dt;
35161             }
35162           };
35163
35164           // Check if date is valid for specific month (and year for February).
35165           // Month: 0 = Jan, 1 = Feb, etc
35166           function isValid(year, month, date) {
35167             if (date < 1) {
35168               return false;
35169             }
35170
35171             if (month === 1 && date > 28) {
35172               return date === 29 && (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0);
35173             }
35174
35175             if (month === 3 || month === 5 || month === 8 || month === 10) {
35176               return date < 31;
35177             }
35178
35179             return true;
35180           }
35181
35182           function toInt(str) {
35183             return parseInt(str, 10);
35184           }
35185
35186           this.toTimezone = toTimezone;
35187           this.fromTimezone = fromTimezone;
35188           this.timezoneToOffset = timezoneToOffset;
35189           this.addDateMinutes = addDateMinutes;
35190           this.convertTimezoneToLocal = convertTimezoneToLocal;
35191           
35192           function toTimezone(date, timezone) {
35193             return date && timezone ? convertTimezoneToLocal(date, timezone) : date;
35194           }
35195
35196           function fromTimezone(date, timezone) {
35197             return date && timezone ? convertTimezoneToLocal(date, timezone, true) : date;
35198           }
35199
35200           //https://github.com/angular/angular.js/blob/4daafd3dbe6a80d578f5a31df1bb99c77559543e/src/Angular.js#L1207
35201           function timezoneToOffset(timezone, fallback) {
35202             var requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
35203             return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
35204           }
35205
35206           function addDateMinutes(date, minutes) {
35207             date = new Date(date.getTime());
35208             date.setMinutes(date.getMinutes() + minutes);
35209             return date;
35210           }
35211
35212           function convertTimezoneToLocal(date, timezone, reverse) {
35213             reverse = reverse ? -1 : 1;
35214             var timezoneOffset = timezoneToOffset(timezone, date.getTimezoneOffset());
35215             return addDateMinutes(date, reverse * (timezoneOffset - date.getTimezoneOffset()));
35216           }
35217         }]);
35218
35219         // Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to
35220         // at most one element.
35221         angular.module('ui.bootstrap.isClass', [])
35222         .directive('uibIsClass', [
35223                  '$animate',
35224         function ($animate) {
35225           //                    11111111          22222222
35226           var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/;
35227           //                    11111111           22222222
35228           var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/;
35229
35230           var dataPerTracked = {};
35231
35232           return {
35233             restrict: 'A',
35234             compile: function (tElement, tAttrs) {
35235               var linkedScopes = [];
35236               var instances = [];
35237               var expToData = {};
35238               var lastActivated = null;
35239               var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP);
35240               var onExp = onExpMatches[2];
35241               var expsStr = onExpMatches[1];
35242               var exps = expsStr.split(',');
35243
35244               return linkFn;
35245
35246               function linkFn(scope, element, attrs) {
35247                 linkedScopes.push(scope);
35248                 instances.push({
35249                   scope: scope,
35250                   element: element
35251                 });
35252
35253                 exps.forEach(function (exp, k) {
35254                   addForExp(exp, scope);
35255                 });
35256
35257                 scope.$on('$destroy', removeScope);
35258               }
35259
35260               function addForExp(exp, scope) {
35261                 var matches = exp.match(IS_REGEXP);
35262                 var clazz = scope.$eval(matches[1]);
35263                 var compareWithExp = matches[2];
35264                 var data = expToData[exp];
35265                 if (!data) {
35266                   var watchFn = function (compareWithVal) {
35267                     var newActivated = null;
35268                     instances.some(function (instance) {
35269                       var thisVal = instance.scope.$eval(onExp);
35270                       if (thisVal === compareWithVal) {
35271                         newActivated = instance;
35272                         return true;
35273                       }
35274                     });
35275                     if (data.lastActivated !== newActivated) {
35276                       if (data.lastActivated) {
35277                         $animate.removeClass(data.lastActivated.element, clazz);
35278                       }
35279                       if (newActivated) {
35280                         $animate.addClass(newActivated.element, clazz);
35281                       }
35282                       data.lastActivated = newActivated;
35283                     }
35284                   };
35285                   expToData[exp] = data = {
35286                     lastActivated: null,
35287                     scope: scope,
35288                     watchFn: watchFn,
35289                     compareWithExp: compareWithExp,
35290                     watcher: scope.$watch(compareWithExp, watchFn)
35291                   };
35292                 }
35293                 data.watchFn(scope.$eval(compareWithExp));
35294               }
35295
35296               function removeScope(e) {
35297                 var removedScope = e.targetScope;
35298                 var index = linkedScopes.indexOf(removedScope);
35299                 linkedScopes.splice(index, 1);
35300                 instances.splice(index, 1);
35301                 if (linkedScopes.length) {
35302                   var newWatchScope = linkedScopes[0];
35303                   angular.forEach(expToData, function (data) {
35304                     if (data.scope === removedScope) {
35305                       data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn);
35306                       data.scope = newWatchScope;
35307                     }
35308                   });
35309                 }
35310                 else {
35311                   expToData = {};
35312                 }
35313               }
35314             }
35315           };
35316         }]);
35317         angular.module('ui.bootstrap.position', [])
35318
35319         /**
35320          * A set of utility methods for working with the DOM.
35321          * It is meant to be used where we need to absolute-position elements in
35322          * relation to another element (this is the case for tooltips, popovers,
35323          * typeahead suggestions etc.).
35324          */
35325           .factory('$uibPosition', ['$document', '$window', function($document, $window) {
35326             /**
35327              * Used by scrollbarWidth() function to cache scrollbar's width.
35328              * Do not access this variable directly, use scrollbarWidth() instead.
35329              */
35330             var SCROLLBAR_WIDTH;
35331             var OVERFLOW_REGEX = {
35332               normal: /(auto|scroll)/,
35333               hidden: /(auto|scroll|hidden)/
35334             };
35335             var PLACEMENT_REGEX = {
35336               auto: /\s?auto?\s?/i,
35337               primary: /^(top|bottom|left|right)$/,
35338               secondary: /^(top|bottom|left|right|center)$/,
35339               vertical: /^(top|bottom)$/
35340             };
35341
35342             return {
35343
35344               /**
35345                * Provides a raw DOM element from a jQuery/jQLite element.
35346                *
35347                * @param {element} elem - The element to convert.
35348                *
35349                * @returns {element} A HTML element.
35350                */
35351               getRawNode: function(elem) {
35352                 return elem[0] || elem;
35353               },
35354
35355               /**
35356                * Provides a parsed number for a style property.  Strips
35357                * units and casts invalid numbers to 0.
35358                *
35359                * @param {string} value - The style value to parse.
35360                *
35361                * @returns {number} A valid number.
35362                */
35363               parseStyle: function(value) {
35364                 value = parseFloat(value);
35365                 return isFinite(value) ? value : 0;
35366               },
35367
35368               /**
35369                * Provides the closest positioned ancestor.
35370                *
35371                * @param {element} element - The element to get the offest parent for.
35372                *
35373                * @returns {element} The closest positioned ancestor.
35374                */
35375               offsetParent: function(elem) {
35376                 elem = this.getRawNode(elem);
35377
35378                 var offsetParent = elem.offsetParent || $document[0].documentElement;
35379
35380                 function isStaticPositioned(el) {
35381                   return ($window.getComputedStyle(el).position || 'static') === 'static';
35382                 }
35383
35384                 while (offsetParent && offsetParent !== $document[0].documentElement && isStaticPositioned(offsetParent)) {
35385                   offsetParent = offsetParent.offsetParent;
35386                 }
35387
35388                 return offsetParent || $document[0].documentElement;
35389               },
35390
35391               /**
35392                * Provides the scrollbar width, concept from TWBS measureScrollbar()
35393                * function in https://github.com/twbs/bootstrap/blob/master/js/modal.js
35394                *
35395                * @returns {number} The width of the browser scollbar.
35396                */
35397               scrollbarWidth: function() {
35398                 if (angular.isUndefined(SCROLLBAR_WIDTH)) {
35399                   var scrollElem = angular.element('<div style="position: absolute; top: -9999px; width: 50px; height: 50px; overflow: scroll;"></div>');
35400                   $document.find('body').append(scrollElem);
35401                   SCROLLBAR_WIDTH = scrollElem[0].offsetWidth - scrollElem[0].clientWidth;
35402                   SCROLLBAR_WIDTH = isFinite(SCROLLBAR_WIDTH) ? SCROLLBAR_WIDTH : 0;
35403                   scrollElem.remove();
35404                 }
35405
35406                 return SCROLLBAR_WIDTH;
35407               },
35408
35409               /**
35410                * Provides the closest scrollable ancestor.
35411                * A port of the jQuery UI scrollParent method:
35412                * https://github.com/jquery/jquery-ui/blob/master/ui/scroll-parent.js
35413                *
35414                * @param {element} elem - The element to find the scroll parent of.
35415                * @param {boolean=} [includeHidden=false] - Should scroll style of 'hidden' be considered,
35416                *   default is false.
35417                *
35418                * @returns {element} A HTML element.
35419                */
35420               scrollParent: function(elem, includeHidden) {
35421                 elem = this.getRawNode(elem);
35422
35423                 var overflowRegex = includeHidden ? OVERFLOW_REGEX.hidden : OVERFLOW_REGEX.normal;
35424                 var documentEl = $document[0].documentElement;
35425                 var elemStyle = $window.getComputedStyle(elem);
35426                 var excludeStatic = elemStyle.position === 'absolute';
35427                 var scrollParent = elem.parentElement || documentEl;
35428
35429                 if (scrollParent === documentEl || elemStyle.position === 'fixed') {
35430                   return documentEl;
35431                 }
35432
35433                 while (scrollParent.parentElement && scrollParent !== documentEl) {
35434                   var spStyle = $window.getComputedStyle(scrollParent);
35435                   if (excludeStatic && spStyle.position !== 'static') {
35436                     excludeStatic = false;
35437                   }
35438
35439                   if (!excludeStatic && overflowRegex.test(spStyle.overflow + spStyle.overflowY + spStyle.overflowX)) {
35440                     break;
35441                   }
35442                   scrollParent = scrollParent.parentElement;
35443                 }
35444
35445                 return scrollParent;
35446               },
35447
35448               /**
35449                * Provides read-only equivalent of jQuery's position function:
35450                * http://api.jquery.com/position/ - distance to closest positioned
35451                * ancestor.  Does not account for margins by default like jQuery position.
35452                *
35453                * @param {element} elem - The element to caclulate the position on.
35454                * @param {boolean=} [includeMargins=false] - Should margins be accounted
35455                * for, default is false.
35456                *
35457                * @returns {object} An object with the following properties:
35458                *   <ul>
35459                *     <li>**width**: the width of the element</li>
35460                *     <li>**height**: the height of the element</li>
35461                *     <li>**top**: distance to top edge of offset parent</li>
35462                *     <li>**left**: distance to left edge of offset parent</li>
35463                *   </ul>
35464                */
35465               position: function(elem, includeMagins) {
35466                 elem = this.getRawNode(elem);
35467
35468                 var elemOffset = this.offset(elem);
35469                 if (includeMagins) {
35470                   var elemStyle = $window.getComputedStyle(elem);
35471                   elemOffset.top -= this.parseStyle(elemStyle.marginTop);
35472                   elemOffset.left -= this.parseStyle(elemStyle.marginLeft);
35473                 }
35474                 var parent = this.offsetParent(elem);
35475                 var parentOffset = {top: 0, left: 0};
35476
35477                 if (parent !== $document[0].documentElement) {
35478                   parentOffset = this.offset(parent);
35479                   parentOffset.top += parent.clientTop - parent.scrollTop;
35480                   parentOffset.left += parent.clientLeft - parent.scrollLeft;
35481                 }
35482
35483                 return {
35484                   width: Math.round(angular.isNumber(elemOffset.width) ? elemOffset.width : elem.offsetWidth),
35485                   height: Math.round(angular.isNumber(elemOffset.height) ? elemOffset.height : elem.offsetHeight),
35486                   top: Math.round(elemOffset.top - parentOffset.top),
35487                   left: Math.round(elemOffset.left - parentOffset.left)
35488                 };
35489               },
35490
35491               /**
35492                * Provides read-only equivalent of jQuery's offset function:
35493                * http://api.jquery.com/offset/ - distance to viewport.  Does
35494                * not account for borders, margins, or padding on the body
35495                * element.
35496                *
35497                * @param {element} elem - The element to calculate the offset on.
35498                *
35499                * @returns {object} An object with the following properties:
35500                *   <ul>
35501                *     <li>**width**: the width of the element</li>
35502                *     <li>**height**: the height of the element</li>
35503                *     <li>**top**: distance to top edge of viewport</li>
35504                *     <li>**right**: distance to bottom edge of viewport</li>
35505                *   </ul>
35506                */
35507               offset: function(elem) {
35508                 elem = this.getRawNode(elem);
35509
35510                 var elemBCR = elem.getBoundingClientRect();
35511                 return {
35512                   width: Math.round(angular.isNumber(elemBCR.width) ? elemBCR.width : elem.offsetWidth),
35513                   height: Math.round(angular.isNumber(elemBCR.height) ? elemBCR.height : elem.offsetHeight),
35514                   top: Math.round(elemBCR.top + ($window.pageYOffset || $document[0].documentElement.scrollTop)),
35515                   left: Math.round(elemBCR.left + ($window.pageXOffset || $document[0].documentElement.scrollLeft))
35516                 };
35517               },
35518
35519               /**
35520                * Provides offset distance to the closest scrollable ancestor
35521                * or viewport.  Accounts for border and scrollbar width.
35522                *
35523                * Right and bottom dimensions represent the distance to the
35524                * respective edge of the viewport element.  If the element
35525                * edge extends beyond the viewport, a negative value will be
35526                * reported.
35527                *
35528                * @param {element} elem - The element to get the viewport offset for.
35529                * @param {boolean=} [useDocument=false] - Should the viewport be the document element instead
35530                * of the first scrollable element, default is false.
35531                * @param {boolean=} [includePadding=true] - Should the padding on the offset parent element
35532                * be accounted for, default is true.
35533                *
35534                * @returns {object} An object with the following properties:
35535                *   <ul>
35536                *     <li>**top**: distance to the top content edge of viewport element</li>
35537                *     <li>**bottom**: distance to the bottom content edge of viewport element</li>
35538                *     <li>**left**: distance to the left content edge of viewport element</li>
35539                *     <li>**right**: distance to the right content edge of viewport element</li>
35540                *   </ul>
35541                */
35542               viewportOffset: function(elem, useDocument, includePadding) {
35543                 elem = this.getRawNode(elem);
35544                 includePadding = includePadding !== false ? true : false;
35545
35546                 var elemBCR = elem.getBoundingClientRect();
35547                 var offsetBCR = {top: 0, left: 0, bottom: 0, right: 0};
35548
35549                 var offsetParent = useDocument ? $document[0].documentElement : this.scrollParent(elem);
35550                 var offsetParentBCR = offsetParent.getBoundingClientRect();
35551
35552                 offsetBCR.top = offsetParentBCR.top + offsetParent.clientTop;
35553                 offsetBCR.left = offsetParentBCR.left + offsetParent.clientLeft;
35554                 if (offsetParent === $document[0].documentElement) {
35555                   offsetBCR.top += $window.pageYOffset;
35556                   offsetBCR.left += $window.pageXOffset;
35557                 }
35558                 offsetBCR.bottom = offsetBCR.top + offsetParent.clientHeight;
35559                 offsetBCR.right = offsetBCR.left + offsetParent.clientWidth;
35560
35561                 if (includePadding) {
35562                   var offsetParentStyle = $window.getComputedStyle(offsetParent);
35563                   offsetBCR.top += this.parseStyle(offsetParentStyle.paddingTop);
35564                   offsetBCR.bottom -= this.parseStyle(offsetParentStyle.paddingBottom);
35565                   offsetBCR.left += this.parseStyle(offsetParentStyle.paddingLeft);
35566                   offsetBCR.right -= this.parseStyle(offsetParentStyle.paddingRight);
35567                 }
35568
35569                 return {
35570                   top: Math.round(elemBCR.top - offsetBCR.top),
35571                   bottom: Math.round(offsetBCR.bottom - elemBCR.bottom),
35572                   left: Math.round(elemBCR.left - offsetBCR.left),
35573                   right: Math.round(offsetBCR.right - elemBCR.right)
35574                 };
35575               },
35576
35577               /**
35578                * Provides an array of placement values parsed from a placement string.
35579                * Along with the 'auto' indicator, supported placement strings are:
35580                *   <ul>
35581                *     <li>top: element on top, horizontally centered on host element.</li>
35582                *     <li>top-left: element on top, left edge aligned with host element left edge.</li>
35583                *     <li>top-right: element on top, lerightft edge aligned with host element right edge.</li>
35584                *     <li>bottom: element on bottom, horizontally centered on host element.</li>
35585                *     <li>bottom-left: element on bottom, left edge aligned with host element left edge.</li>
35586                *     <li>bottom-right: element on bottom, right edge aligned with host element right edge.</li>
35587                *     <li>left: element on left, vertically centered on host element.</li>
35588                *     <li>left-top: element on left, top edge aligned with host element top edge.</li>
35589                *     <li>left-bottom: element on left, bottom edge aligned with host element bottom edge.</li>
35590                *     <li>right: element on right, vertically centered on host element.</li>
35591                *     <li>right-top: element on right, top edge aligned with host element top edge.</li>
35592                *     <li>right-bottom: element on right, bottom edge aligned with host element bottom edge.</li>
35593                *   </ul>
35594                * A placement string with an 'auto' indicator is expected to be
35595                * space separated from the placement, i.e: 'auto bottom-left'  If
35596                * the primary and secondary placement values do not match 'top,
35597                * bottom, left, right' then 'top' will be the primary placement and
35598                * 'center' will be the secondary placement.  If 'auto' is passed, true
35599                * will be returned as the 3rd value of the array.
35600                *
35601                * @param {string} placement - The placement string to parse.
35602                *
35603                * @returns {array} An array with the following values
35604                * <ul>
35605                *   <li>**[0]**: The primary placement.</li>
35606                *   <li>**[1]**: The secondary placement.</li>
35607                *   <li>**[2]**: If auto is passed: true, else undefined.</li>
35608                * </ul>
35609                */
35610               parsePlacement: function(placement) {
35611                 var autoPlace = PLACEMENT_REGEX.auto.test(placement);
35612                 if (autoPlace) {
35613                   placement = placement.replace(PLACEMENT_REGEX.auto, '');
35614                 }
35615
35616                 placement = placement.split('-');
35617
35618                 placement[0] = placement[0] || 'top';
35619                 if (!PLACEMENT_REGEX.primary.test(placement[0])) {
35620                   placement[0] = 'top';
35621                 }
35622
35623                 placement[1] = placement[1] || 'center';
35624                 if (!PLACEMENT_REGEX.secondary.test(placement[1])) {
35625                   placement[1] = 'center';
35626                 }
35627
35628                 if (autoPlace) {
35629                   placement[2] = true;
35630                 } else {
35631                   placement[2] = false;
35632                 }
35633
35634                 return placement;
35635               },
35636
35637               /**
35638                * Provides coordinates for an element to be positioned relative to
35639                * another element.  Passing 'auto' as part of the placement parameter
35640                * will enable smart placement - where the element fits. i.e:
35641                * 'auto left-top' will check to see if there is enough space to the left
35642                * of the hostElem to fit the targetElem, if not place right (same for secondary
35643                * top placement).  Available space is calculated using the viewportOffset
35644                * function.
35645                *
35646                * @param {element} hostElem - The element to position against.
35647                * @param {element} targetElem - The element to position.
35648                * @param {string=} [placement=top] - The placement for the targetElem,
35649                *   default is 'top'. 'center' is assumed as secondary placement for
35650                *   'top', 'left', 'right', and 'bottom' placements.  Available placements are:
35651                *   <ul>
35652                *     <li>top</li>
35653                *     <li>top-right</li>
35654                *     <li>top-left</li>
35655                *     <li>bottom</li>
35656                *     <li>bottom-left</li>
35657                *     <li>bottom-right</li>
35658                *     <li>left</li>
35659                *     <li>left-top</li>
35660                *     <li>left-bottom</li>
35661                *     <li>right</li>
35662                *     <li>right-top</li>
35663                *     <li>right-bottom</li>
35664                *   </ul>
35665                * @param {boolean=} [appendToBody=false] - Should the top and left values returned
35666                *   be calculated from the body element, default is false.
35667                *
35668                * @returns {object} An object with the following properties:
35669                *   <ul>
35670                *     <li>**top**: Value for targetElem top.</li>
35671                *     <li>**left**: Value for targetElem left.</li>
35672                *     <li>**placement**: The resolved placement.</li>
35673                *   </ul>
35674                */
35675               positionElements: function(hostElem, targetElem, placement, appendToBody) {
35676                 hostElem = this.getRawNode(hostElem);
35677                 targetElem = this.getRawNode(targetElem);
35678
35679                 // need to read from prop to support tests.
35680                 var targetWidth = angular.isDefined(targetElem.offsetWidth) ? targetElem.offsetWidth : targetElem.prop('offsetWidth');
35681                 var targetHeight = angular.isDefined(targetElem.offsetHeight) ? targetElem.offsetHeight : targetElem.prop('offsetHeight');
35682
35683                 placement = this.parsePlacement(placement);
35684
35685                 var hostElemPos = appendToBody ? this.offset(hostElem) : this.position(hostElem);
35686                 var targetElemPos = {top: 0, left: 0, placement: ''};
35687
35688                 if (placement[2]) {
35689                   var viewportOffset = this.viewportOffset(hostElem);
35690
35691                   var targetElemStyle = $window.getComputedStyle(targetElem);
35692                   var adjustedSize = {
35693                     width: targetWidth + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginLeft) + this.parseStyle(targetElemStyle.marginRight))),
35694                     height: targetHeight + Math.round(Math.abs(this.parseStyle(targetElemStyle.marginTop) + this.parseStyle(targetElemStyle.marginBottom)))
35695                   };
35696
35697                   placement[0] = placement[0] === 'top' && adjustedSize.height > viewportOffset.top && adjustedSize.height <= viewportOffset.bottom ? 'bottom' :
35698                                  placement[0] === 'bottom' && adjustedSize.height > viewportOffset.bottom && adjustedSize.height <= viewportOffset.top ? 'top' :
35699                                  placement[0] === 'left' && adjustedSize.width > viewportOffset.left && adjustedSize.width <= viewportOffset.right ? 'right' :
35700                                  placement[0] === 'right' && adjustedSize.width > viewportOffset.right && adjustedSize.width <= viewportOffset.left ? 'left' :
35701                                  placement[0];
35702
35703                   placement[1] = placement[1] === 'top' && adjustedSize.height - hostElemPos.height > viewportOffset.bottom && adjustedSize.height - hostElemPos.height <= viewportOffset.top ? 'bottom' :
35704                                  placement[1] === 'bottom' && adjustedSize.height - hostElemPos.height > viewportOffset.top && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom ? 'top' :
35705                                  placement[1] === 'left' && adjustedSize.width - hostElemPos.width > viewportOffset.right && adjustedSize.width - hostElemPos.width <= viewportOffset.left ? 'right' :
35706                                  placement[1] === 'right' && adjustedSize.width - hostElemPos.width > viewportOffset.left && adjustedSize.width - hostElemPos.width <= viewportOffset.right ? 'left' :
35707                                  placement[1];
35708
35709                   if (placement[1] === 'center') {
35710                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35711                       var xOverflow = hostElemPos.width / 2 - targetWidth / 2;
35712                       if (viewportOffset.left + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.right) {
35713                         placement[1] = 'left';
35714                       } else if (viewportOffset.right + xOverflow < 0 && adjustedSize.width - hostElemPos.width <= viewportOffset.left) {
35715                         placement[1] = 'right';
35716                       }
35717                     } else {
35718                       var yOverflow = hostElemPos.height / 2 - adjustedSize.height / 2;
35719                       if (viewportOffset.top + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.bottom) {
35720                         placement[1] = 'top';
35721                       } else if (viewportOffset.bottom + yOverflow < 0 && adjustedSize.height - hostElemPos.height <= viewportOffset.top) {
35722                         placement[1] = 'bottom';
35723                       }
35724                     }
35725                   }
35726                 }
35727
35728                 switch (placement[0]) {
35729                   case 'top':
35730                     targetElemPos.top = hostElemPos.top - targetHeight;
35731                     break;
35732                   case 'bottom':
35733                     targetElemPos.top = hostElemPos.top + hostElemPos.height;
35734                     break;
35735                   case 'left':
35736                     targetElemPos.left = hostElemPos.left - targetWidth;
35737                     break;
35738                   case 'right':
35739                     targetElemPos.left = hostElemPos.left + hostElemPos.width;
35740                     break;
35741                 }
35742
35743                 switch (placement[1]) {
35744                   case 'top':
35745                     targetElemPos.top = hostElemPos.top;
35746                     break;
35747                   case 'bottom':
35748                     targetElemPos.top = hostElemPos.top + hostElemPos.height - targetHeight;
35749                     break;
35750                   case 'left':
35751                     targetElemPos.left = hostElemPos.left;
35752                     break;
35753                   case 'right':
35754                     targetElemPos.left = hostElemPos.left + hostElemPos.width - targetWidth;
35755                     break;
35756                   case 'center':
35757                     if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35758                       targetElemPos.left = hostElemPos.left + hostElemPos.width / 2 - targetWidth / 2;
35759                     } else {
35760                       targetElemPos.top = hostElemPos.top + hostElemPos.height / 2 - targetHeight / 2;
35761                     }
35762                     break;
35763                 }
35764
35765                 targetElemPos.top = Math.round(targetElemPos.top);
35766                 targetElemPos.left = Math.round(targetElemPos.left);
35767                 targetElemPos.placement = placement[1] === 'center' ? placement[0] : placement[0] + '-' + placement[1];
35768
35769                 return targetElemPos;
35770               },
35771
35772               /**
35773               * Provides a way for positioning tooltip & dropdown
35774               * arrows when using placement options beyond the standard
35775               * left, right, top, or bottom.
35776               *
35777               * @param {element} elem - The tooltip/dropdown element.
35778               * @param {string} placement - The placement for the elem.
35779               */
35780               positionArrow: function(elem, placement) {
35781                 elem = this.getRawNode(elem);
35782
35783                 var isTooltip = true;
35784
35785                 var innerElem = elem.querySelector('.tooltip-inner');
35786                 if (!innerElem) {
35787                   isTooltip = false;
35788                   innerElem = elem.querySelector('.popover-inner');
35789                 }
35790                 if (!innerElem) {
35791                   return;
35792                 }
35793
35794                 var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow');
35795                 if (!arrowElem) {
35796                   return;
35797                 }
35798
35799                 placement = this.parsePlacement(placement);
35800                 if (placement[1] === 'center') {
35801                   // no adjustment necessary - just reset styles
35802                   angular.element(arrowElem).css({top: '', bottom: '', right: '', left: '', margin: ''});
35803                   return;
35804                 }
35805
35806                 var borderProp = 'border-' + placement[0] + '-width';
35807                 var borderWidth = $window.getComputedStyle(arrowElem)[borderProp];
35808
35809                 var borderRadiusProp = 'border-';
35810                 if (PLACEMENT_REGEX.vertical.test(placement[0])) {
35811                   borderRadiusProp += placement[0] + '-' + placement[1];
35812                 } else {
35813                   borderRadiusProp += placement[1] + '-' + placement[0];
35814                 }
35815                 borderRadiusProp += '-radius';
35816                 var borderRadius = $window.getComputedStyle(isTooltip ? innerElem : elem)[borderRadiusProp];
35817
35818                 var arrowCss = {
35819                   top: 'auto',
35820                   bottom: 'auto',
35821                   left: 'auto',
35822                   right: 'auto',
35823                   margin: 0
35824                 };
35825
35826                 switch (placement[0]) {
35827                   case 'top':
35828                     arrowCss.bottom = isTooltip ? '0' : '-' + borderWidth;
35829                     break;
35830                   case 'bottom':
35831                     arrowCss.top = isTooltip ? '0' : '-' + borderWidth;
35832                     break;
35833                   case 'left':
35834                     arrowCss.right = isTooltip ? '0' : '-' + borderWidth;
35835                     break;
35836                   case 'right':
35837                     arrowCss.left = isTooltip ? '0' : '-' + borderWidth;
35838                     break;
35839                 }
35840
35841                 arrowCss[placement[1]] = borderRadius;
35842
35843                 angular.element(arrowElem).css(arrowCss);
35844               }
35845             };
35846           }]);
35847
35848         angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass', 'ui.bootstrap.position'])
35849
35850         .value('$datepickerSuppressError', false)
35851
35852         .constant('uibDatepickerConfig', {
35853           formatDay: 'dd',
35854           formatMonth: 'MMMM',
35855           formatYear: 'yyyy',
35856           formatDayHeader: 'EEE',
35857           formatDayTitle: 'MMMM yyyy',
35858           formatMonthTitle: 'yyyy',
35859           datepickerMode: 'day',
35860           minMode: 'day',
35861           maxMode: 'year',
35862           showWeeks: true,
35863           startingDay: 0,
35864           yearRows: 4,
35865           yearColumns: 5,
35866           minDate: null,
35867           maxDate: null,
35868           shortcutPropagation: false,
35869           ngModelOptions: {}
35870         })
35871
35872         .controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser',
35873           function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) {
35874           var self = this,
35875               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
35876               ngModelOptions = {};
35877
35878           // Modes chain
35879           this.modes = ['day', 'month', 'year'];
35880
35881           // Interpolated configuration attributes
35882           angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle'], function(key) {
35883             self[key] = angular.isDefined($attrs[key]) ? $interpolate($attrs[key])($scope.$parent) : datepickerConfig[key];
35884           });
35885
35886           // Evaled configuration attributes
35887           angular.forEach(['showWeeks', 'startingDay', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) {
35888             self[key] = angular.isDefined($attrs[key]) ? $scope.$parent.$eval($attrs[key]) : datepickerConfig[key];
35889           });
35890
35891           // Watchable date attributes
35892           angular.forEach(['minDate', 'maxDate'], function(key) {
35893             if ($attrs[key]) {
35894               $scope.$parent.$watch($attrs[key], function(value) {
35895                 self[key] = value ? angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')) : null;
35896                 self.refreshView();
35897               });
35898             } else {
35899               self[key] = datepickerConfig[key] ? dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : null;
35900             }
35901           });
35902
35903           angular.forEach(['minMode', 'maxMode'], function(key) {
35904             if ($attrs[key]) {
35905               $scope.$parent.$watch($attrs[key], function(value) {
35906                 self[key] = $scope[key] = angular.isDefined(value) ? value : $attrs[key];
35907                 if (key === 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key]) ||
35908                   key === 'maxMode' && self.modes.indexOf($scope.datepickerMode) > self.modes.indexOf(self[key])) {
35909                   $scope.datepickerMode = self[key];
35910                 }
35911               });
35912             } else {
35913               self[key] = $scope[key] = datepickerConfig[key] || null;
35914             }
35915           });
35916
35917           $scope.datepickerMode = $scope.datepickerMode || datepickerConfig.datepickerMode;
35918           $scope.uniqueId = 'datepicker-' + $scope.$id + '-' + Math.floor(Math.random() * 10000);
35919
35920           if (angular.isDefined($attrs.initDate)) {
35921             this.activeDate = dateParser.fromTimezone($scope.$parent.$eval($attrs.initDate), ngModelOptions.timezone) || new Date();
35922             $scope.$parent.$watch($attrs.initDate, function(initDate) {
35923               if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
35924                 self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
35925                 self.refreshView();
35926               }
35927             });
35928           } else {
35929             this.activeDate = new Date();
35930           }
35931
35932           $scope.disabled = angular.isDefined($attrs.disabled) || false;
35933           if (angular.isDefined($attrs.ngDisabled)) {
35934             $scope.$parent.$watch($attrs.ngDisabled, function(disabled) {
35935               $scope.disabled = disabled;
35936               self.refreshView();
35937             });
35938           }
35939
35940           $scope.isActive = function(dateObject) {
35941             if (self.compare(dateObject.date, self.activeDate) === 0) {
35942               $scope.activeDateId = dateObject.uid;
35943               return true;
35944             }
35945             return false;
35946           };
35947
35948           this.init = function(ngModelCtrl_) {
35949             ngModelCtrl = ngModelCtrl_;
35950             ngModelOptions = ngModelCtrl_.$options || datepickerConfig.ngModelOptions;
35951
35952             if (ngModelCtrl.$modelValue) {
35953               this.activeDate = ngModelCtrl.$modelValue;
35954             }
35955
35956             ngModelCtrl.$render = function() {
35957               self.render();
35958             };
35959           };
35960
35961           this.render = function() {
35962             if (ngModelCtrl.$viewValue) {
35963               var date = new Date(ngModelCtrl.$viewValue),
35964                   isValid = !isNaN(date);
35965
35966               if (isValid) {
35967                 this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
35968               } else if (!$datepickerSuppressError) {
35969                 $log.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
35970               }
35971             }
35972             this.refreshView();
35973           };
35974
35975           this.refreshView = function() {
35976             if (this.element) {
35977               $scope.selectedDt = null;
35978               this._refreshView();
35979               if ($scope.activeDt) {
35980                 $scope.activeDateId = $scope.activeDt.uid;
35981               }
35982
35983               var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35984               date = dateParser.fromTimezone(date, ngModelOptions.timezone);
35985               ngModelCtrl.$setValidity('dateDisabled', !date ||
35986                 this.element && !this.isDisabled(date));
35987             }
35988           };
35989
35990           this.createDateObject = function(date, format) {
35991             var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
35992             model = dateParser.fromTimezone(model, ngModelOptions.timezone);
35993             var dt = {
35994               date: date,
35995               label: dateFilter(date, format),
35996               selected: model && this.compare(date, model) === 0,
35997               disabled: this.isDisabled(date),
35998               current: this.compare(date, new Date()) === 0,
35999               customClass: this.customClass(date) || null
36000             };
36001
36002             if (model && this.compare(date, model) === 0) {
36003               $scope.selectedDt = dt;
36004             }
36005
36006             if (self.activeDate && this.compare(dt.date, self.activeDate) === 0) {
36007               $scope.activeDt = dt;
36008             }
36009
36010             return dt;
36011           };
36012
36013           this.isDisabled = function(date) {
36014             return $scope.disabled ||
36015               this.minDate && this.compare(date, this.minDate) < 0 ||
36016               this.maxDate && this.compare(date, this.maxDate) > 0 ||
36017               $attrs.dateDisabled && $scope.dateDisabled({date: date, mode: $scope.datepickerMode});
36018           };
36019
36020           this.customClass = function(date) {
36021             return $scope.customClass({date: date, mode: $scope.datepickerMode});
36022           };
36023
36024           // Split array into smaller arrays
36025           this.split = function(arr, size) {
36026             var arrays = [];
36027             while (arr.length > 0) {
36028               arrays.push(arr.splice(0, size));
36029             }
36030             return arrays;
36031           };
36032
36033           $scope.select = function(date) {
36034             if ($scope.datepickerMode === self.minMode) {
36035               var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
36036               dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
36037               dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
36038               ngModelCtrl.$setViewValue(dt);
36039               ngModelCtrl.$render();
36040             } else {
36041               self.activeDate = date;
36042               $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) - 1];
36043             }
36044           };
36045
36046           $scope.move = function(direction) {
36047             var year = self.activeDate.getFullYear() + direction * (self.step.years || 0),
36048                 month = self.activeDate.getMonth() + direction * (self.step.months || 0);
36049             self.activeDate.setFullYear(year, month, 1);
36050             self.refreshView();
36051           };
36052
36053           $scope.toggleMode = function(direction) {
36054             direction = direction || 1;
36055
36056             if ($scope.datepickerMode === self.maxMode && direction === 1 ||
36057               $scope.datepickerMode === self.minMode && direction === -1) {
36058               return;
36059             }
36060
36061             $scope.datepickerMode = self.modes[self.modes.indexOf($scope.datepickerMode) + direction];
36062           };
36063
36064           // Key event mapper
36065           $scope.keys = { 13: 'enter', 32: 'space', 33: 'pageup', 34: 'pagedown', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
36066
36067           var focusElement = function() {
36068             self.element[0].focus();
36069           };
36070
36071           // Listen for focus requests from popup directive
36072           $scope.$on('uib:datepicker.focus', focusElement);
36073
36074           $scope.keydown = function(evt) {
36075             var key = $scope.keys[evt.which];
36076
36077             if (!key || evt.shiftKey || evt.altKey || $scope.disabled) {
36078               return;
36079             }
36080
36081             evt.preventDefault();
36082             if (!self.shortcutPropagation) {
36083               evt.stopPropagation();
36084             }
36085
36086             if (key === 'enter' || key === 'space') {
36087               if (self.isDisabled(self.activeDate)) {
36088                 return; // do nothing
36089               }
36090               $scope.select(self.activeDate);
36091             } else if (evt.ctrlKey && (key === 'up' || key === 'down')) {
36092               $scope.toggleMode(key === 'up' ? 1 : -1);
36093             } else {
36094               self.handleKeyDown(key, evt);
36095               self.refreshView();
36096             }
36097           };
36098         }])
36099
36100         .controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36101           var DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
36102
36103           this.step = { months: 1 };
36104           this.element = $element;
36105           function getDaysInMonth(year, month) {
36106             return month === 1 && year % 4 === 0 &&
36107               (year % 100 !== 0 || year % 400 === 0) ? 29 : DAYS_IN_MONTH[month];
36108           }
36109
36110           this.init = function(ctrl) {
36111             angular.extend(ctrl, this);
36112             scope.showWeeks = ctrl.showWeeks;
36113             ctrl.refreshView();
36114           };
36115
36116           this.getDates = function(startDate, n) {
36117             var dates = new Array(n), current = new Date(startDate), i = 0, date;
36118             while (i < n) {
36119               date = new Date(current);
36120               dates[i++] = date;
36121               current.setDate(current.getDate() + 1);
36122             }
36123             return dates;
36124           };
36125
36126           this._refreshView = function() {
36127             var year = this.activeDate.getFullYear(),
36128               month = this.activeDate.getMonth(),
36129               firstDayOfMonth = new Date(this.activeDate);
36130
36131             firstDayOfMonth.setFullYear(year, month, 1);
36132
36133             var difference = this.startingDay - firstDayOfMonth.getDay(),
36134               numDisplayedFromPreviousMonth = difference > 0 ?
36135                 7 - difference : - difference,
36136               firstDate = new Date(firstDayOfMonth);
36137
36138             if (numDisplayedFromPreviousMonth > 0) {
36139               firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
36140             }
36141
36142             // 42 is the number of days on a six-week calendar
36143             var days = this.getDates(firstDate, 42);
36144             for (var i = 0; i < 42; i ++) {
36145               days[i] = angular.extend(this.createDateObject(days[i], this.formatDay), {
36146                 secondary: days[i].getMonth() !== month,
36147                 uid: scope.uniqueId + '-' + i
36148               });
36149             }
36150
36151             scope.labels = new Array(7);
36152             for (var j = 0; j < 7; j++) {
36153               scope.labels[j] = {
36154                 abbr: dateFilter(days[j].date, this.formatDayHeader),
36155                 full: dateFilter(days[j].date, 'EEEE')
36156               };
36157             }
36158
36159             scope.title = dateFilter(this.activeDate, this.formatDayTitle);
36160             scope.rows = this.split(days, 7);
36161
36162             if (scope.showWeeks) {
36163               scope.weekNumbers = [];
36164               var thursdayIndex = (4 + 7 - this.startingDay) % 7,
36165                   numWeeks = scope.rows.length;
36166               for (var curWeek = 0; curWeek < numWeeks; curWeek++) {
36167                 scope.weekNumbers.push(
36168                   getISO8601WeekNumber(scope.rows[curWeek][thursdayIndex].date));
36169               }
36170             }
36171           };
36172
36173           this.compare = function(date1, date2) {
36174             var _date1 = new Date(date1.getFullYear(), date1.getMonth(), date1.getDate());
36175             var _date2 = new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36176             _date1.setFullYear(date1.getFullYear());
36177             _date2.setFullYear(date2.getFullYear());
36178             return _date1 - _date2;
36179           };
36180
36181           function getISO8601WeekNumber(date) {
36182             var checkDate = new Date(date);
36183             checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
36184             var time = checkDate.getTime();
36185             checkDate.setMonth(0); // Compare with Jan 1
36186             checkDate.setDate(1);
36187             return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
36188           }
36189
36190           this.handleKeyDown = function(key, evt) {
36191             var date = this.activeDate.getDate();
36192
36193             if (key === 'left') {
36194               date = date - 1;
36195             } else if (key === 'up') {
36196               date = date - 7;
36197             } else if (key === 'right') {
36198               date = date + 1;
36199             } else if (key === 'down') {
36200               date = date + 7;
36201             } else if (key === 'pageup' || key === 'pagedown') {
36202               var month = this.activeDate.getMonth() + (key === 'pageup' ? - 1 : 1);
36203               this.activeDate.setMonth(month, 1);
36204               date = Math.min(getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth()), date);
36205             } else if (key === 'home') {
36206               date = 1;
36207             } else if (key === 'end') {
36208               date = getDaysInMonth(this.activeDate.getFullYear(), this.activeDate.getMonth());
36209             }
36210             this.activeDate.setDate(date);
36211           };
36212         }])
36213
36214         .controller('UibMonthpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36215           this.step = { years: 1 };
36216           this.element = $element;
36217
36218           this.init = function(ctrl) {
36219             angular.extend(ctrl, this);
36220             ctrl.refreshView();
36221           };
36222
36223           this._refreshView = function() {
36224             var months = new Array(12),
36225                 year = this.activeDate.getFullYear(),
36226                 date;
36227
36228             for (var i = 0; i < 12; i++) {
36229               date = new Date(this.activeDate);
36230               date.setFullYear(year, i, 1);
36231               months[i] = angular.extend(this.createDateObject(date, this.formatMonth), {
36232                 uid: scope.uniqueId + '-' + i
36233               });
36234             }
36235
36236             scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
36237             scope.rows = this.split(months, 3);
36238           };
36239
36240           this.compare = function(date1, date2) {
36241             var _date1 = new Date(date1.getFullYear(), date1.getMonth());
36242             var _date2 = new Date(date2.getFullYear(), date2.getMonth());
36243             _date1.setFullYear(date1.getFullYear());
36244             _date2.setFullYear(date2.getFullYear());
36245             return _date1 - _date2;
36246           };
36247
36248           this.handleKeyDown = function(key, evt) {
36249             var date = this.activeDate.getMonth();
36250
36251             if (key === 'left') {
36252               date = date - 1;
36253             } else if (key === 'up') {
36254               date = date - 3;
36255             } else if (key === 'right') {
36256               date = date + 1;
36257             } else if (key === 'down') {
36258               date = date + 3;
36259             } else if (key === 'pageup' || key === 'pagedown') {
36260               var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
36261               this.activeDate.setFullYear(year);
36262             } else if (key === 'home') {
36263               date = 0;
36264             } else if (key === 'end') {
36265               date = 11;
36266             }
36267             this.activeDate.setMonth(date);
36268           };
36269         }])
36270
36271         .controller('UibYearpickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
36272           var columns, range;
36273           this.element = $element;
36274
36275           function getStartingYear(year) {
36276             return parseInt((year - 1) / range, 10) * range + 1;
36277           }
36278
36279           this.yearpickerInit = function() {
36280             columns = this.yearColumns;
36281             range = this.yearRows * columns;
36282             this.step = { years: range };
36283           };
36284
36285           this._refreshView = function() {
36286             var years = new Array(range), date;
36287
36288             for (var i = 0, start = getStartingYear(this.activeDate.getFullYear()); i < range; i++) {
36289               date = new Date(this.activeDate);
36290               date.setFullYear(start + i, 0, 1);
36291               years[i] = angular.extend(this.createDateObject(date, this.formatYear), {
36292                 uid: scope.uniqueId + '-' + i
36293               });
36294             }
36295
36296             scope.title = [years[0].label, years[range - 1].label].join(' - ');
36297             scope.rows = this.split(years, columns);
36298             scope.columns = columns;
36299           };
36300
36301           this.compare = function(date1, date2) {
36302             return date1.getFullYear() - date2.getFullYear();
36303           };
36304
36305           this.handleKeyDown = function(key, evt) {
36306             var date = this.activeDate.getFullYear();
36307
36308             if (key === 'left') {
36309               date = date - 1;
36310             } else if (key === 'up') {
36311               date = date - columns;
36312             } else if (key === 'right') {
36313               date = date + 1;
36314             } else if (key === 'down') {
36315               date = date + columns;
36316             } else if (key === 'pageup' || key === 'pagedown') {
36317               date += (key === 'pageup' ? - 1 : 1) * range;
36318             } else if (key === 'home') {
36319               date = getStartingYear(this.activeDate.getFullYear());
36320             } else if (key === 'end') {
36321               date = getStartingYear(this.activeDate.getFullYear()) + range - 1;
36322             }
36323             this.activeDate.setFullYear(date);
36324           };
36325         }])
36326
36327         .directive('uibDatepicker', function() {
36328           return {
36329             replace: true,
36330             templateUrl: function(element, attrs) {
36331               return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
36332             },
36333             scope: {
36334               datepickerMode: '=?',
36335               dateDisabled: '&',
36336               customClass: '&',
36337               shortcutPropagation: '&?'
36338             },
36339             require: ['uibDatepicker', '^ngModel'],
36340             controller: 'UibDatepickerController',
36341             controllerAs: 'datepicker',
36342             link: function(scope, element, attrs, ctrls) {
36343               var datepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
36344
36345               datepickerCtrl.init(ngModelCtrl);
36346             }
36347           };
36348         })
36349
36350         .directive('uibDaypicker', function() {
36351           return {
36352             replace: true,
36353             templateUrl: function(element, attrs) {
36354               return attrs.templateUrl || 'uib/template/datepicker/day.html';
36355             },
36356             require: ['^uibDatepicker', 'uibDaypicker'],
36357             controller: 'UibDaypickerController',
36358             link: function(scope, element, attrs, ctrls) {
36359               var datepickerCtrl = ctrls[0],
36360                 daypickerCtrl = ctrls[1];
36361
36362               daypickerCtrl.init(datepickerCtrl);
36363             }
36364           };
36365         })
36366
36367         .directive('uibMonthpicker', function() {
36368           return {
36369             replace: true,
36370             templateUrl: function(element, attrs) {
36371               return attrs.templateUrl || 'uib/template/datepicker/month.html';
36372             },
36373             require: ['^uibDatepicker', 'uibMonthpicker'],
36374             controller: 'UibMonthpickerController',
36375             link: function(scope, element, attrs, ctrls) {
36376               var datepickerCtrl = ctrls[0],
36377                 monthpickerCtrl = ctrls[1];
36378
36379               monthpickerCtrl.init(datepickerCtrl);
36380             }
36381           };
36382         })
36383
36384         .directive('uibYearpicker', function() {
36385           return {
36386             replace: true,
36387             templateUrl: function(element, attrs) {
36388               return attrs.templateUrl || 'uib/template/datepicker/year.html';
36389             },
36390             require: ['^uibDatepicker', 'uibYearpicker'],
36391             controller: 'UibYearpickerController',
36392             link: function(scope, element, attrs, ctrls) {
36393               var ctrl = ctrls[0];
36394               angular.extend(ctrl, ctrls[1]);
36395               ctrl.yearpickerInit();
36396
36397               ctrl.refreshView();
36398             }
36399           };
36400         })
36401
36402         .constant('uibDatepickerPopupConfig', {
36403           datepickerPopup: 'yyyy-MM-dd',
36404           datepickerPopupTemplateUrl: 'uib/template/datepicker/popup.html',
36405           datepickerTemplateUrl: 'uib/template/datepicker/datepicker.html',
36406           html5Types: {
36407             date: 'yyyy-MM-dd',
36408             'datetime-local': 'yyyy-MM-ddTHH:mm:ss.sss',
36409             'month': 'yyyy-MM'
36410           },
36411           currentText: 'Today',
36412           clearText: 'Clear',
36413           closeText: 'Done',
36414           closeOnDateSelection: true,
36415           appendToBody: false,
36416           showButtonBar: true,
36417           onOpenFocus: true,
36418           altInputFormats: []
36419         })
36420
36421         .controller('UibDatepickerPopupController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$document', '$rootScope', '$uibPosition', 'dateFilter', 'uibDateParser', 'uibDatepickerPopupConfig', '$timeout', 'uibDatepickerConfig',
36422         function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig) {
36423           var self = this;
36424           var cache = {},
36425             isHtml5DateInput = false;
36426           var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
36427             datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl,
36428             ngModel, ngModelOptions, $popup, altInputFormats;
36429
36430           scope.watchData = {};
36431
36432           this.init = function(_ngModel_) {
36433             ngModel = _ngModel_;
36434             ngModelOptions = _ngModel_.$options || datepickerConfig.ngModelOptions;
36435             closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$parent.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
36436             appendToBody = angular.isDefined(attrs.datepickerAppendToBody) ? scope.$parent.$eval(attrs.datepickerAppendToBody) : datepickerPopupConfig.appendToBody;
36437             onOpenFocus = angular.isDefined(attrs.onOpenFocus) ? scope.$parent.$eval(attrs.onOpenFocus) : datepickerPopupConfig.onOpenFocus;
36438             datepickerPopupTemplateUrl = angular.isDefined(attrs.datepickerPopupTemplateUrl) ? attrs.datepickerPopupTemplateUrl : datepickerPopupConfig.datepickerPopupTemplateUrl;
36439             datepickerTemplateUrl = angular.isDefined(attrs.datepickerTemplateUrl) ? attrs.datepickerTemplateUrl : datepickerPopupConfig.datepickerTemplateUrl;
36440             altInputFormats = angular.isDefined(attrs.altInputFormats) ? scope.$parent.$eval(attrs.altInputFormats) : datepickerPopupConfig.altInputFormats;
36441
36442             scope.showButtonBar = angular.isDefined(attrs.showButtonBar) ? scope.$parent.$eval(attrs.showButtonBar) : datepickerPopupConfig.showButtonBar;
36443
36444             if (datepickerPopupConfig.html5Types[attrs.type]) {
36445               dateFormat = datepickerPopupConfig.html5Types[attrs.type];
36446               isHtml5DateInput = true;
36447             } else {
36448               dateFormat = attrs.uibDatepickerPopup || datepickerPopupConfig.datepickerPopup;
36449               attrs.$observe('uibDatepickerPopup', function(value, oldValue) {
36450                   var newDateFormat = value || datepickerPopupConfig.datepickerPopup;
36451                   // Invalidate the $modelValue to ensure that formatters re-run
36452                   // FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
36453                   if (newDateFormat !== dateFormat) {
36454                     dateFormat = newDateFormat;
36455                     ngModel.$modelValue = null;
36456
36457                     if (!dateFormat) {
36458                       throw new Error('uibDatepickerPopup must have a date format specified.');
36459                     }
36460                   }
36461               });
36462             }
36463
36464             if (!dateFormat) {
36465               throw new Error('uibDatepickerPopup must have a date format specified.');
36466             }
36467
36468             if (isHtml5DateInput && attrs.uibDatepickerPopup) {
36469               throw new Error('HTML5 date input types do not support custom formats.');
36470             }
36471
36472             // popup element used to display calendar
36473             popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
36474             scope.ngModelOptions = angular.copy(ngModelOptions);
36475             scope.ngModelOptions.timezone = null;
36476             popupEl.attr({
36477               'ng-model': 'date',
36478               'ng-model-options': 'ngModelOptions',
36479               'ng-change': 'dateSelection(date)',
36480               'template-url': datepickerPopupTemplateUrl
36481             });
36482
36483             // datepicker element
36484             datepickerEl = angular.element(popupEl.children()[0]);
36485             datepickerEl.attr('template-url', datepickerTemplateUrl);
36486
36487             if (isHtml5DateInput) {
36488               if (attrs.type === 'month') {
36489                 datepickerEl.attr('datepicker-mode', '"month"');
36490                 datepickerEl.attr('min-mode', 'month');
36491               }
36492             }
36493
36494             if (attrs.datepickerOptions) {
36495               var options = scope.$parent.$eval(attrs.datepickerOptions);
36496               if (options && options.initDate) {
36497                 scope.initDate = dateParser.fromTimezone(options.initDate, ngModelOptions.timezone);
36498                 datepickerEl.attr('init-date', 'initDate');
36499                 delete options.initDate;
36500               }
36501               angular.forEach(options, function(value, option) {
36502                 datepickerEl.attr(cameltoDash(option), value);
36503               });
36504             }
36505
36506             angular.forEach(['minMode', 'maxMode'], function(key) {
36507               if (attrs[key]) {
36508                 scope.$parent.$watch(function() { return attrs[key]; }, function(value) {
36509                   scope.watchData[key] = value;
36510                 });
36511                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36512               }
36513             });
36514
36515             angular.forEach(['datepickerMode', 'shortcutPropagation'], function(key) {
36516               if (attrs[key]) {
36517                 var getAttribute = $parse(attrs[key]);
36518                 var propConfig = {
36519                   get: function() {
36520                     return getAttribute(scope.$parent);
36521                   }
36522                 };
36523
36524                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36525
36526                 // Propagate changes from datepicker to outside
36527                 if (key === 'datepickerMode') {
36528                   var setAttribute = getAttribute.assign;
36529                   propConfig.set = function(v) {
36530                     setAttribute(scope.$parent, v);
36531                   };
36532                 }
36533
36534                 Object.defineProperty(scope.watchData, key, propConfig);
36535               }
36536             });
36537
36538             angular.forEach(['minDate', 'maxDate', 'initDate'], function(key) {
36539               if (attrs[key]) {
36540                 var getAttribute = $parse(attrs[key]);
36541
36542                 scope.$parent.$watch(getAttribute, function(value) {
36543                   if (key === 'minDate' || key === 'maxDate') {
36544                     cache[key] = angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium'));
36545                   }
36546
36547                   scope.watchData[key] = cache[key] || dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
36548                 });
36549
36550                 datepickerEl.attr(cameltoDash(key), 'watchData.' + key);
36551               }
36552             });
36553
36554             if (attrs.dateDisabled) {
36555               datepickerEl.attr('date-disabled', 'dateDisabled({ date: date, mode: mode })');
36556             }
36557
36558             angular.forEach(['formatDay', 'formatMonth', 'formatYear', 'formatDayHeader', 'formatDayTitle', 'formatMonthTitle', 'showWeeks', 'startingDay', 'yearRows', 'yearColumns'], function(key) {
36559               if (angular.isDefined(attrs[key])) {
36560                 datepickerEl.attr(cameltoDash(key), attrs[key]);
36561               }
36562             });
36563
36564             if (attrs.customClass) {
36565               datepickerEl.attr('custom-class', 'customClass({ date: date, mode: mode })');
36566             }
36567
36568             if (!isHtml5DateInput) {
36569               // Internal API to maintain the correct ng-invalid-[key] class
36570               ngModel.$$parserName = 'date';
36571               ngModel.$validators.date = validator;
36572               ngModel.$parsers.unshift(parseDate);
36573               ngModel.$formatters.push(function(value) {
36574                 if (ngModel.$isEmpty(value)) {
36575                   scope.date = value;
36576                   return value;
36577                 }
36578                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36579                 return dateFilter(scope.date, dateFormat);
36580               });
36581             } else {
36582               ngModel.$formatters.push(function(value) {
36583                 scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
36584                 return value;
36585               });
36586             }
36587
36588             // Detect changes in the view from the text box
36589             ngModel.$viewChangeListeners.push(function() {
36590               scope.date = parseDateString(ngModel.$viewValue);
36591             });
36592
36593             element.bind('keydown', inputKeydownBind);
36594
36595             $popup = $compile(popupEl)(scope);
36596             // Prevent jQuery cache memory leak (template is now redundant after linking)
36597             popupEl.remove();
36598
36599             if (appendToBody) {
36600               $document.find('body').append($popup);
36601             } else {
36602               element.after($popup);
36603             }
36604
36605             scope.$on('$destroy', function() {
36606               if (scope.isOpen === true) {
36607                 if (!$rootScope.$$phase) {
36608                   scope.$apply(function() {
36609                     scope.isOpen = false;
36610                   });
36611                 }
36612               }
36613
36614               $popup.remove();
36615               element.unbind('keydown', inputKeydownBind);
36616               $document.unbind('click', documentClickBind);
36617             });
36618           };
36619
36620           scope.getText = function(key) {
36621             return scope[key + 'Text'] || datepickerPopupConfig[key + 'Text'];
36622           };
36623
36624           scope.isDisabled = function(date) {
36625             if (date === 'today') {
36626               date = new Date();
36627             }
36628
36629             return scope.watchData.minDate && scope.compare(date, cache.minDate) < 0 ||
36630               scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0;
36631           };
36632
36633           scope.compare = function(date1, date2) {
36634             return new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate());
36635           };
36636
36637           // Inner change
36638           scope.dateSelection = function(dt) {
36639             if (angular.isDefined(dt)) {
36640               scope.date = dt;
36641             }
36642             var date = scope.date ? dateFilter(scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
36643             element.val(date);
36644             ngModel.$setViewValue(date);
36645
36646             if (closeOnDateSelection) {
36647               scope.isOpen = false;
36648               element[0].focus();
36649             }
36650           };
36651
36652           scope.keydown = function(evt) {
36653             if (evt.which === 27) {
36654               evt.stopPropagation();
36655               scope.isOpen = false;
36656               element[0].focus();
36657             }
36658           };
36659
36660           scope.select = function(date) {
36661             if (date === 'today') {
36662               var today = new Date();
36663               if (angular.isDate(scope.date)) {
36664                 date = new Date(scope.date);
36665                 date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
36666               } else {
36667                 date = new Date(today.setHours(0, 0, 0, 0));
36668               }
36669             }
36670             scope.dateSelection(date);
36671           };
36672
36673           scope.close = function() {
36674             scope.isOpen = false;
36675             element[0].focus();
36676           };
36677
36678           scope.disabled = angular.isDefined(attrs.disabled) || false;
36679           if (attrs.ngDisabled) {
36680             scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) {
36681               scope.disabled = disabled;
36682             });
36683           }
36684
36685           scope.$watch('isOpen', function(value) {
36686             if (value) {
36687               if (!scope.disabled) {
36688                 scope.position = appendToBody ? $position.offset(element) : $position.position(element);
36689                 scope.position.top = scope.position.top + element.prop('offsetHeight');
36690
36691                 $timeout(function() {
36692                   if (onOpenFocus) {
36693                     scope.$broadcast('uib:datepicker.focus');
36694                   }
36695                   $document.bind('click', documentClickBind);
36696                 }, 0, false);
36697               } else {
36698                 scope.isOpen = false;
36699               }
36700             } else {
36701               $document.unbind('click', documentClickBind);
36702             }
36703           });
36704
36705           function cameltoDash(string) {
36706             return string.replace(/([A-Z])/g, function($1) { return '-' + $1.toLowerCase(); });
36707           }
36708
36709           function parseDateString(viewValue) {
36710             var date = dateParser.parse(viewValue, dateFormat, scope.date);
36711             if (isNaN(date)) {
36712               for (var i = 0; i < altInputFormats.length; i++) {
36713                 date = dateParser.parse(viewValue, altInputFormats[i], scope.date);
36714                 if (!isNaN(date)) {
36715                   return date;
36716                 }
36717               }
36718             }
36719             return date;
36720           }
36721
36722           function parseDate(viewValue) {
36723             if (angular.isNumber(viewValue)) {
36724               // presumably timestamp to date object
36725               viewValue = new Date(viewValue);
36726             }
36727
36728             if (!viewValue) {
36729               return null;
36730             }
36731
36732             if (angular.isDate(viewValue) && !isNaN(viewValue)) {
36733               return viewValue;
36734             }
36735
36736             if (angular.isString(viewValue)) {
36737               var date = parseDateString(viewValue);
36738               if (!isNaN(date)) {
36739                 return dateParser.toTimezone(date, ngModelOptions.timezone);
36740               }
36741             }
36742
36743             return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
36744           }
36745
36746           function validator(modelValue, viewValue) {
36747             var value = modelValue || viewValue;
36748
36749             if (!attrs.ngRequired && !value) {
36750               return true;
36751             }
36752
36753             if (angular.isNumber(value)) {
36754               value = new Date(value);
36755             }
36756
36757             if (!value) {
36758               return true;
36759             }
36760
36761             if (angular.isDate(value) && !isNaN(value)) {
36762               return true;
36763             }
36764
36765             if (angular.isString(value)) {
36766               return !isNaN(parseDateString(viewValue));
36767             }
36768
36769             return false;
36770           }
36771
36772           function documentClickBind(event) {
36773             if (!scope.isOpen && scope.disabled) {
36774               return;
36775             }
36776
36777             var popup = $popup[0];
36778             var dpContainsTarget = element[0].contains(event.target);
36779             // The popup node may not be an element node
36780             // In some browsers (IE) only element nodes have the 'contains' function
36781             var popupContainsTarget = popup.contains !== undefined && popup.contains(event.target);
36782             if (scope.isOpen && !(dpContainsTarget || popupContainsTarget)) {
36783               scope.$apply(function() {
36784                 scope.isOpen = false;
36785               });
36786             }
36787           }
36788
36789           function inputKeydownBind(evt) {
36790             if (evt.which === 27 && scope.isOpen) {
36791               evt.preventDefault();
36792               evt.stopPropagation();
36793               scope.$apply(function() {
36794                 scope.isOpen = false;
36795               });
36796               element[0].focus();
36797             } else if (evt.which === 40 && !scope.isOpen) {
36798               evt.preventDefault();
36799               evt.stopPropagation();
36800               scope.$apply(function() {
36801                 scope.isOpen = true;
36802               });
36803             }
36804           }
36805         }])
36806
36807         .directive('uibDatepickerPopup', function() {
36808           return {
36809             require: ['ngModel', 'uibDatepickerPopup'],
36810             controller: 'UibDatepickerPopupController',
36811             scope: {
36812               isOpen: '=?',
36813               currentText: '@',
36814               clearText: '@',
36815               closeText: '@',
36816               dateDisabled: '&',
36817               customClass: '&'
36818             },
36819             link: function(scope, element, attrs, ctrls) {
36820               var ngModel = ctrls[0],
36821                 ctrl = ctrls[1];
36822
36823               ctrl.init(ngModel);
36824             }
36825           };
36826         })
36827
36828         .directive('uibDatepickerPopupWrap', function() {
36829           return {
36830             replace: true,
36831             transclude: true,
36832             templateUrl: function(element, attrs) {
36833               return attrs.templateUrl || 'uib/template/datepicker/popup.html';
36834             }
36835           };
36836         });
36837
36838         angular.module('ui.bootstrap.debounce', [])
36839         /**
36840          * A helper, internal service that debounces a function
36841          */
36842           .factory('$$debounce', ['$timeout', function($timeout) {
36843             return function(callback, debounceTime) {
36844               var timeoutPromise;
36845
36846               return function() {
36847                 var self = this;
36848                 var args = Array.prototype.slice.call(arguments);
36849                 if (timeoutPromise) {
36850                   $timeout.cancel(timeoutPromise);
36851                 }
36852
36853                 timeoutPromise = $timeout(function() {
36854                   callback.apply(self, args);
36855                 }, debounceTime);
36856               };
36857             };
36858           }]);
36859
36860         angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
36861
36862         .constant('uibDropdownConfig', {
36863           appendToOpenClass: 'uib-dropdown-open',
36864           openClass: 'open'
36865         })
36866
36867         .service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
36868           var openScope = null;
36869
36870           this.open = function(dropdownScope) {
36871             if (!openScope) {
36872               $document.on('click', closeDropdown);
36873               $document.on('keydown', keybindFilter);
36874             }
36875
36876             if (openScope && openScope !== dropdownScope) {
36877               openScope.isOpen = false;
36878             }
36879
36880             openScope = dropdownScope;
36881           };
36882
36883           this.close = function(dropdownScope) {
36884             if (openScope === dropdownScope) {
36885               openScope = null;
36886               $document.off('click', closeDropdown);
36887               $document.off('keydown', keybindFilter);
36888             }
36889           };
36890
36891           var closeDropdown = function(evt) {
36892             // This method may still be called during the same mouse event that
36893             // unbound this event handler. So check openScope before proceeding.
36894             if (!openScope) { return; }
36895
36896             if (evt && openScope.getAutoClose() === 'disabled') { return; }
36897
36898             if (evt && evt.which === 3) { return; }
36899
36900             var toggleElement = openScope.getToggleElement();
36901             if (evt && toggleElement && toggleElement[0].contains(evt.target)) {
36902               return;
36903             }
36904
36905             var dropdownElement = openScope.getDropdownElement();
36906             if (evt && openScope.getAutoClose() === 'outsideClick' &&
36907               dropdownElement && dropdownElement[0].contains(evt.target)) {
36908               return;
36909             }
36910
36911             openScope.isOpen = false;
36912
36913             if (!$rootScope.$$phase) {
36914               openScope.$apply();
36915             }
36916           };
36917
36918           var keybindFilter = function(evt) {
36919             if (evt.which === 27) {
36920               openScope.focusToggleElement();
36921               closeDropdown();
36922             } else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
36923               evt.preventDefault();
36924               evt.stopPropagation();
36925               openScope.focusDropdownEntry(evt.which);
36926             }
36927           };
36928         }])
36929
36930         .controller('UibDropdownController', ['$scope', '$element', '$attrs', '$parse', 'uibDropdownConfig', 'uibDropdownService', '$animate', '$uibPosition', '$document', '$compile', '$templateRequest', function($scope, $element, $attrs, $parse, dropdownConfig, uibDropdownService, $animate, $position, $document, $compile, $templateRequest) {
36931           var self = this,
36932             scope = $scope.$new(), // create a child scope so we are not polluting original one
36933             templateScope,
36934             appendToOpenClass = dropdownConfig.appendToOpenClass,
36935             openClass = dropdownConfig.openClass,
36936             getIsOpen,
36937             setIsOpen = angular.noop,
36938             toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
36939             appendToBody = false,
36940             appendTo = null,
36941             keynavEnabled = false,
36942             selectedOption = null,
36943             body = $document.find('body');
36944
36945           $element.addClass('dropdown');
36946
36947           this.init = function() {
36948             if ($attrs.isOpen) {
36949               getIsOpen = $parse($attrs.isOpen);
36950               setIsOpen = getIsOpen.assign;
36951
36952               $scope.$watch(getIsOpen, function(value) {
36953                 scope.isOpen = !!value;
36954               });
36955             }
36956
36957             if (angular.isDefined($attrs.dropdownAppendTo)) {
36958               var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
36959               if (appendToEl) {
36960                 appendTo = angular.element(appendToEl);
36961               }
36962             }
36963
36964             appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
36965             keynavEnabled = angular.isDefined($attrs.keyboardNav);
36966
36967             if (appendToBody && !appendTo) {
36968               appendTo = body;
36969             }
36970
36971             if (appendTo && self.dropdownMenu) {
36972               appendTo.append(self.dropdownMenu);
36973               $element.on('$destroy', function handleDestroyEvent() {
36974                 self.dropdownMenu.remove();
36975               });
36976             }
36977           };
36978
36979           this.toggle = function(open) {
36980             return scope.isOpen = arguments.length ? !!open : !scope.isOpen;
36981           };
36982
36983           // Allow other directives to watch status
36984           this.isOpen = function() {
36985             return scope.isOpen;
36986           };
36987
36988           scope.getToggleElement = function() {
36989             return self.toggleElement;
36990           };
36991
36992           scope.getAutoClose = function() {
36993             return $attrs.autoClose || 'always'; //or 'outsideClick' or 'disabled'
36994           };
36995
36996           scope.getElement = function() {
36997             return $element;
36998           };
36999
37000           scope.isKeynavEnabled = function() {
37001             return keynavEnabled;
37002           };
37003
37004           scope.focusDropdownEntry = function(keyCode) {
37005             var elems = self.dropdownMenu ? //If append to body is used.
37006               angular.element(self.dropdownMenu).find('a') :
37007               $element.find('ul').eq(0).find('a');
37008
37009             switch (keyCode) {
37010               case 40: {
37011                 if (!angular.isNumber(self.selectedOption)) {
37012                   self.selectedOption = 0;
37013                 } else {
37014                   self.selectedOption = self.selectedOption === elems.length - 1 ?
37015                     self.selectedOption :
37016                     self.selectedOption + 1;
37017                 }
37018                 break;
37019               }
37020               case 38: {
37021                 if (!angular.isNumber(self.selectedOption)) {
37022                   self.selectedOption = elems.length - 1;
37023                 } else {
37024                   self.selectedOption = self.selectedOption === 0 ?
37025                     0 : self.selectedOption - 1;
37026                 }
37027                 break;
37028               }
37029             }
37030             elems[self.selectedOption].focus();
37031           };
37032
37033           scope.getDropdownElement = function() {
37034             return self.dropdownMenu;
37035           };
37036
37037           scope.focusToggleElement = function() {
37038             if (self.toggleElement) {
37039               self.toggleElement[0].focus();
37040             }
37041           };
37042
37043           scope.$watch('isOpen', function(isOpen, wasOpen) {
37044             if (appendTo && self.dropdownMenu) {
37045               var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
37046                 css,
37047                 rightalign;
37048
37049               css = {
37050                 top: pos.top + 'px',
37051                 display: isOpen ? 'block' : 'none'
37052               };
37053
37054               rightalign = self.dropdownMenu.hasClass('dropdown-menu-right');
37055               if (!rightalign) {
37056                 css.left = pos.left + 'px';
37057                 css.right = 'auto';
37058               } else {
37059                 css.left = 'auto';
37060                 css.right = window.innerWidth -
37061                   (pos.left + $element.prop('offsetWidth')) + 'px';
37062               }
37063
37064               // Need to adjust our positioning to be relative to the appendTo container
37065               // if it's not the body element
37066               if (!appendToBody) {
37067                 var appendOffset = $position.offset(appendTo);
37068
37069                 css.top = pos.top - appendOffset.top + 'px';
37070
37071                 if (!rightalign) {
37072                   css.left = pos.left - appendOffset.left + 'px';
37073                 } else {
37074                   css.right = window.innerWidth -
37075                     (pos.left - appendOffset.left + $element.prop('offsetWidth')) + 'px';
37076                 }
37077               }
37078
37079               self.dropdownMenu.css(css);
37080             }
37081
37082             var openContainer = appendTo ? appendTo : $element;
37083
37084             $animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
37085               if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
37086                 toggleInvoker($scope, { open: !!isOpen });
37087               }
37088             });
37089
37090             if (isOpen) {
37091               if (self.dropdownMenuTemplateUrl) {
37092                 $templateRequest(self.dropdownMenuTemplateUrl).then(function(tplContent) {
37093                   templateScope = scope.$new();
37094                   $compile(tplContent.trim())(templateScope, function(dropdownElement) {
37095                     var newEl = dropdownElement;
37096                     self.dropdownMenu.replaceWith(newEl);
37097                     self.dropdownMenu = newEl;
37098                   });
37099                 });
37100               }
37101
37102               scope.focusToggleElement();
37103               uibDropdownService.open(scope);
37104             } else {
37105               if (self.dropdownMenuTemplateUrl) {
37106                 if (templateScope) {
37107                   templateScope.$destroy();
37108                 }
37109                 var newEl = angular.element('<ul class="dropdown-menu"></ul>');
37110                 self.dropdownMenu.replaceWith(newEl);
37111                 self.dropdownMenu = newEl;
37112               }
37113
37114               uibDropdownService.close(scope);
37115               self.selectedOption = null;
37116             }
37117
37118             if (angular.isFunction(setIsOpen)) {
37119               setIsOpen($scope, isOpen);
37120             }
37121           });
37122
37123           $scope.$on('$locationChangeSuccess', function() {
37124             if (scope.getAutoClose() !== 'disabled') {
37125               scope.isOpen = false;
37126             }
37127           });
37128         }])
37129
37130         .directive('uibDropdown', function() {
37131           return {
37132             controller: 'UibDropdownController',
37133             link: function(scope, element, attrs, dropdownCtrl) {
37134               dropdownCtrl.init();
37135             }
37136           };
37137         })
37138
37139         .directive('uibDropdownMenu', function() {
37140           return {
37141             restrict: 'A',
37142             require: '?^uibDropdown',
37143             link: function(scope, element, attrs, dropdownCtrl) {
37144               if (!dropdownCtrl || angular.isDefined(attrs.dropdownNested)) {
37145                 return;
37146               }
37147
37148               element.addClass('dropdown-menu');
37149
37150               var tplUrl = attrs.templateUrl;
37151               if (tplUrl) {
37152                 dropdownCtrl.dropdownMenuTemplateUrl = tplUrl;
37153               }
37154
37155               if (!dropdownCtrl.dropdownMenu) {
37156                 dropdownCtrl.dropdownMenu = element;
37157               }
37158             }
37159           };
37160         })
37161
37162         .directive('uibDropdownToggle', function() {
37163           return {
37164             require: '?^uibDropdown',
37165             link: function(scope, element, attrs, dropdownCtrl) {
37166               if (!dropdownCtrl) {
37167                 return;
37168               }
37169
37170               element.addClass('dropdown-toggle');
37171
37172               dropdownCtrl.toggleElement = element;
37173
37174               var toggleDropdown = function(event) {
37175                 event.preventDefault();
37176
37177                 if (!element.hasClass('disabled') && !attrs.disabled) {
37178                   scope.$apply(function() {
37179                     dropdownCtrl.toggle();
37180                   });
37181                 }
37182               };
37183
37184               element.bind('click', toggleDropdown);
37185
37186               // WAI-ARIA
37187               element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
37188               scope.$watch(dropdownCtrl.isOpen, function(isOpen) {
37189                 element.attr('aria-expanded', !!isOpen);
37190               });
37191
37192               scope.$on('$destroy', function() {
37193                 element.unbind('click', toggleDropdown);
37194               });
37195             }
37196           };
37197         });
37198
37199         angular.module('ui.bootstrap.stackedMap', [])
37200         /**
37201          * A helper, internal data structure that acts as a map but also allows getting / removing
37202          * elements in the LIFO order
37203          */
37204           .factory('$$stackedMap', function() {
37205             return {
37206               createNew: function() {
37207                 var stack = [];
37208
37209                 return {
37210                   add: function(key, value) {
37211                     stack.push({
37212                       key: key,
37213                       value: value
37214                     });
37215                   },
37216                   get: function(key) {
37217                     for (var i = 0; i < stack.length; i++) {
37218                       if (key === stack[i].key) {
37219                         return stack[i];
37220                       }
37221                     }
37222                   },
37223                   keys: function() {
37224                     var keys = [];
37225                     for (var i = 0; i < stack.length; i++) {
37226                       keys.push(stack[i].key);
37227                     }
37228                     return keys;
37229                   },
37230                   top: function() {
37231                     return stack[stack.length - 1];
37232                   },
37233                   remove: function(key) {
37234                     var idx = -1;
37235                     for (var i = 0; i < stack.length; i++) {
37236                       if (key === stack[i].key) {
37237                         idx = i;
37238                         break;
37239                       }
37240                     }
37241                     return stack.splice(idx, 1)[0];
37242                   },
37243                   removeTop: function() {
37244                     return stack.splice(stack.length - 1, 1)[0];
37245                   },
37246                   length: function() {
37247                     return stack.length;
37248                   }
37249                 };
37250               }
37251             };
37252           });
37253         angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap'])
37254         /**
37255          * A helper, internal data structure that stores all references attached to key
37256          */
37257           .factory('$$multiMap', function() {
37258             return {
37259               createNew: function() {
37260                 var map = {};
37261
37262                 return {
37263                   entries: function() {
37264                     return Object.keys(map).map(function(key) {
37265                       return {
37266                         key: key,
37267                         value: map[key]
37268                       };
37269                     });
37270                   },
37271                   get: function(key) {
37272                     return map[key];
37273                   },
37274                   hasKey: function(key) {
37275                     return !!map[key];
37276                   },
37277                   keys: function() {
37278                     return Object.keys(map);
37279                   },
37280                   put: function(key, value) {
37281                     if (!map[key]) {
37282                       map[key] = [];
37283                     }
37284
37285                     map[key].push(value);
37286                   },
37287                   remove: function(key, value) {
37288                     var values = map[key];
37289
37290                     if (!values) {
37291                       return;
37292                     }
37293
37294                     var idx = values.indexOf(value);
37295
37296                     if (idx !== -1) {
37297                       values.splice(idx, 1);
37298                     }
37299
37300                     if (!values.length) {
37301                       delete map[key];
37302                     }
37303                   }
37304                 };
37305               }
37306             };
37307           })
37308
37309         /**
37310          * Pluggable resolve mechanism for the modal resolve resolution
37311          * Supports UI Router's $resolve service
37312          */
37313           .provider('$uibResolve', function() {
37314             var resolve = this;
37315             this.resolver = null;
37316
37317             this.setResolver = function(resolver) {
37318               this.resolver = resolver;
37319             };
37320
37321             this.$get = ['$injector', '$q', function($injector, $q) {
37322               var resolver = resolve.resolver ? $injector.get(resolve.resolver) : null;
37323               return {
37324                 resolve: function(invocables, locals, parent, self) {
37325                   if (resolver) {
37326                     return resolver.resolve(invocables, locals, parent, self);
37327                   }
37328
37329                   var promises = [];
37330
37331                   angular.forEach(invocables, function(value) {
37332                     if (angular.isFunction(value) || angular.isArray(value)) {
37333                       promises.push($q.resolve($injector.invoke(value)));
37334                     } else if (angular.isString(value)) {
37335                       promises.push($q.resolve($injector.get(value)));
37336                     } else {
37337                       promises.push($q.resolve(value));
37338                     }
37339                   });
37340
37341                   return $q.all(promises).then(function(resolves) {
37342                     var resolveObj = {};
37343                     var resolveIter = 0;
37344                     angular.forEach(invocables, function(value, key) {
37345                       resolveObj[key] = resolves[resolveIter++];
37346                     });
37347
37348                     return resolveObj;
37349                   });
37350                 }
37351               };
37352             }];
37353           })
37354
37355         /**
37356          * A helper directive for the $modal service. It creates a backdrop element.
37357          */
37358           .directive('uibModalBackdrop', ['$animateCss', '$injector', '$uibModalStack',
37359           function($animateCss, $injector, $modalStack) {
37360             return {
37361               replace: true,
37362               templateUrl: 'uib/template/modal/backdrop.html',
37363               compile: function(tElement, tAttrs) {
37364                 tElement.addClass(tAttrs.backdropClass);
37365                 return linkFn;
37366               }
37367             };
37368
37369             function linkFn(scope, element, attrs) {
37370               if (attrs.modalInClass) {
37371                 $animateCss(element, {
37372                   addClass: attrs.modalInClass
37373                 }).start();
37374
37375                 scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37376                   var done = setIsAsync();
37377                   if (scope.modalOptions.animation) {
37378                     $animateCss(element, {
37379                       removeClass: attrs.modalInClass
37380                     }).start().then(done);
37381                   } else {
37382                     done();
37383                   }
37384                 });
37385               }
37386             }
37387           }])
37388
37389           .directive('uibModalWindow', ['$uibModalStack', '$q', '$animate', '$animateCss', '$document',
37390           function($modalStack, $q, $animate, $animateCss, $document) {
37391             return {
37392               scope: {
37393                 index: '@'
37394               },
37395               replace: true,
37396               transclude: true,
37397               templateUrl: function(tElement, tAttrs) {
37398                 return tAttrs.templateUrl || 'uib/template/modal/window.html';
37399               },
37400               link: function(scope, element, attrs) {
37401                 element.addClass(attrs.windowClass || '');
37402                 element.addClass(attrs.windowTopClass || '');
37403                 scope.size = attrs.size;
37404
37405                 scope.close = function(evt) {
37406                   var modal = $modalStack.getTop();
37407                   if (modal && modal.value.backdrop &&
37408                     modal.value.backdrop !== 'static' &&
37409                     evt.target === evt.currentTarget) {
37410                     evt.preventDefault();
37411                     evt.stopPropagation();
37412                     $modalStack.dismiss(modal.key, 'backdrop click');
37413                   }
37414                 };
37415
37416                 // moved from template to fix issue #2280
37417                 element.on('click', scope.close);
37418
37419                 // This property is only added to the scope for the purpose of detecting when this directive is rendered.
37420                 // We can detect that by using this property in the template associated with this directive and then use
37421                 // {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
37422                 scope.$isRendered = true;
37423
37424                 // Deferred object that will be resolved when this modal is render.
37425                 var modalRenderDeferObj = $q.defer();
37426                 // Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
37427                 // In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
37428                 attrs.$observe('modalRender', function(value) {
37429                   if (value === 'true') {
37430                     modalRenderDeferObj.resolve();
37431                   }
37432                 });
37433
37434                 modalRenderDeferObj.promise.then(function() {
37435                   var animationPromise = null;
37436
37437                   if (attrs.modalInClass) {
37438                     animationPromise = $animateCss(element, {
37439                       addClass: attrs.modalInClass
37440                     }).start();
37441
37442                     scope.$on($modalStack.NOW_CLOSING_EVENT, function(e, setIsAsync) {
37443                       var done = setIsAsync();
37444                       if ($animateCss) {
37445                         $animateCss(element, {
37446                           removeClass: attrs.modalInClass
37447                         }).start().then(done);
37448                       } else {
37449                         $animate.removeClass(element, attrs.modalInClass).then(done);
37450                       }
37451                     });
37452                   }
37453
37454
37455                   $q.when(animationPromise).then(function() {
37456                     /**
37457                      * If something within the freshly-opened modal already has focus (perhaps via a
37458                      * directive that causes focus). then no need to try and focus anything.
37459                      */
37460                     if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
37461                       var inputWithAutofocus = element[0].querySelector('[autofocus]');
37462                       /**
37463                        * Auto-focusing of a freshly-opened modal element causes any child elements
37464                        * with the autofocus attribute to lose focus. This is an issue on touch
37465                        * based devices which will show and then hide the onscreen keyboard.
37466                        * Attempts to refocus the autofocus element via JavaScript will not reopen
37467                        * the onscreen keyboard. Fixed by updated the focusing logic to only autofocus
37468                        * the modal element if the modal does not contain an autofocus element.
37469                        */
37470                       if (inputWithAutofocus) {
37471                         inputWithAutofocus.focus();
37472                       } else {
37473                         element[0].focus();
37474                       }
37475                     }
37476                   });
37477
37478                   // Notify {@link $modalStack} that modal is rendered.
37479                   var modal = $modalStack.getTop();
37480                   if (modal) {
37481                     $modalStack.modalRendered(modal.key);
37482                   }
37483                 });
37484               }
37485             };
37486           }])
37487
37488           .directive('uibModalAnimationClass', function() {
37489             return {
37490               compile: function(tElement, tAttrs) {
37491                 if (tAttrs.modalAnimation) {
37492                   tElement.addClass(tAttrs.uibModalAnimationClass);
37493                 }
37494               }
37495             };
37496           })
37497
37498           .directive('uibModalTransclude', function() {
37499             return {
37500               link: function(scope, element, attrs, controller, transclude) {
37501                 transclude(scope.$parent, function(clone) {
37502                   element.empty();
37503                   element.append(clone);
37504                 });
37505               }
37506             };
37507           })
37508
37509           .factory('$uibModalStack', ['$animate', '$animateCss', '$document',
37510             '$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap',
37511             function($animate, $animateCss, $document, $compile, $rootScope, $q, $$multiMap, $$stackedMap) {
37512               var OPENED_MODAL_CLASS = 'modal-open';
37513
37514               var backdropDomEl, backdropScope;
37515               var openedWindows = $$stackedMap.createNew();
37516               var openedClasses = $$multiMap.createNew();
37517               var $modalStack = {
37518                 NOW_CLOSING_EVENT: 'modal.stack.now-closing'
37519               };
37520
37521               //Modal focus behavior
37522               var focusableElementList;
37523               var focusIndex = 0;
37524               var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
37525                 'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
37526                 'iframe, object, embed, *[tabindex], *[contenteditable=true]';
37527
37528               function backdropIndex() {
37529                 var topBackdropIndex = -1;
37530                 var opened = openedWindows.keys();
37531                 for (var i = 0; i < opened.length; i++) {
37532                   if (openedWindows.get(opened[i]).value.backdrop) {
37533                     topBackdropIndex = i;
37534                   }
37535                 }
37536                 return topBackdropIndex;
37537               }
37538
37539               $rootScope.$watch(backdropIndex, function(newBackdropIndex) {
37540                 if (backdropScope) {
37541                   backdropScope.index = newBackdropIndex;
37542                 }
37543               });
37544
37545               function removeModalWindow(modalInstance, elementToReceiveFocus) {
37546                 var modalWindow = openedWindows.get(modalInstance).value;
37547                 var appendToElement = modalWindow.appendTo;
37548
37549                 //clean up the stack
37550                 openedWindows.remove(modalInstance);
37551
37552                 removeAfterAnimate(modalWindow.modalDomEl, modalWindow.modalScope, function() {
37553                   var modalBodyClass = modalWindow.openedClass || OPENED_MODAL_CLASS;
37554                   openedClasses.remove(modalBodyClass, modalInstance);
37555                   appendToElement.toggleClass(modalBodyClass, openedClasses.hasKey(modalBodyClass));
37556                   toggleTopWindowClass(true);
37557                 });
37558                 checkRemoveBackdrop();
37559
37560                 //move focus to specified element if available, or else to body
37561                 if (elementToReceiveFocus && elementToReceiveFocus.focus) {
37562                   elementToReceiveFocus.focus();
37563                 } else if (appendToElement.focus) {
37564                   appendToElement.focus();
37565                 }
37566               }
37567
37568               // Add or remove "windowTopClass" from the top window in the stack
37569               function toggleTopWindowClass(toggleSwitch) {
37570                 var modalWindow;
37571
37572                 if (openedWindows.length() > 0) {
37573                   modalWindow = openedWindows.top().value;
37574                   modalWindow.modalDomEl.toggleClass(modalWindow.windowTopClass || '', toggleSwitch);
37575                 }
37576               }
37577
37578               function checkRemoveBackdrop() {
37579                 //remove backdrop if no longer needed
37580                 if (backdropDomEl && backdropIndex() === -1) {
37581                   var backdropScopeRef = backdropScope;
37582                   removeAfterAnimate(backdropDomEl, backdropScope, function() {
37583                     backdropScopeRef = null;
37584                   });
37585                   backdropDomEl = undefined;
37586                   backdropScope = undefined;
37587                 }
37588               }
37589
37590               function removeAfterAnimate(domEl, scope, done, closedDeferred) {
37591                 var asyncDeferred;
37592                 var asyncPromise = null;
37593                 var setIsAsync = function() {
37594                   if (!asyncDeferred) {
37595                     asyncDeferred = $q.defer();
37596                     asyncPromise = asyncDeferred.promise;
37597                   }
37598
37599                   return function asyncDone() {
37600                     asyncDeferred.resolve();
37601                   };
37602                 };
37603                 scope.$broadcast($modalStack.NOW_CLOSING_EVENT, setIsAsync);
37604
37605                 // Note that it's intentional that asyncPromise might be null.
37606                 // That's when setIsAsync has not been called during the
37607                 // NOW_CLOSING_EVENT broadcast.
37608                 return $q.when(asyncPromise).then(afterAnimating);
37609
37610                 function afterAnimating() {
37611                   if (afterAnimating.done) {
37612                     return;
37613                   }
37614                   afterAnimating.done = true;
37615
37616                   $animateCss(domEl, {
37617                     event: 'leave'
37618                   }).start().then(function() {
37619                     domEl.remove();
37620                     if (closedDeferred) {
37621                       closedDeferred.resolve();
37622                     }
37623                   });
37624
37625                   scope.$destroy();
37626                   if (done) {
37627                     done();
37628                   }
37629                 }
37630               }
37631
37632               $document.on('keydown', keydownListener);
37633
37634               $rootScope.$on('$destroy', function() {
37635                 $document.off('keydown', keydownListener);
37636               });
37637
37638               function keydownListener(evt) {
37639                 if (evt.isDefaultPrevented()) {
37640                   return evt;
37641                 }
37642
37643                 var modal = openedWindows.top();
37644                 if (modal) {
37645                   switch (evt.which) {
37646                     case 27: {
37647                       if (modal.value.keyboard) {
37648                         evt.preventDefault();
37649                         $rootScope.$apply(function() {
37650                           $modalStack.dismiss(modal.key, 'escape key press');
37651                         });
37652                       }
37653                       break;
37654                     }
37655                     case 9: {
37656                       $modalStack.loadFocusElementList(modal);
37657                       var focusChanged = false;
37658                       if (evt.shiftKey) {
37659                         if ($modalStack.isFocusInFirstItem(evt)) {
37660                           focusChanged = $modalStack.focusLastFocusableElement();
37661                         }
37662                       } else {
37663                         if ($modalStack.isFocusInLastItem(evt)) {
37664                           focusChanged = $modalStack.focusFirstFocusableElement();
37665                         }
37666                       }
37667
37668                       if (focusChanged) {
37669                         evt.preventDefault();
37670                         evt.stopPropagation();
37671                       }
37672                       break;
37673                     }
37674                   }
37675                 }
37676               }
37677
37678               $modalStack.open = function(modalInstance, modal) {
37679                 var modalOpener = $document[0].activeElement,
37680                   modalBodyClass = modal.openedClass || OPENED_MODAL_CLASS;
37681
37682                 toggleTopWindowClass(false);
37683
37684                 openedWindows.add(modalInstance, {
37685                   deferred: modal.deferred,
37686                   renderDeferred: modal.renderDeferred,
37687                   closedDeferred: modal.closedDeferred,
37688                   modalScope: modal.scope,
37689                   backdrop: modal.backdrop,
37690                   keyboard: modal.keyboard,
37691                   openedClass: modal.openedClass,
37692                   windowTopClass: modal.windowTopClass,
37693                   animation: modal.animation,
37694                   appendTo: modal.appendTo
37695                 });
37696
37697                 openedClasses.put(modalBodyClass, modalInstance);
37698
37699                 var appendToElement = modal.appendTo,
37700                     currBackdropIndex = backdropIndex();
37701
37702                 if (!appendToElement.length) {
37703                   throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
37704                 }
37705
37706                 if (currBackdropIndex >= 0 && !backdropDomEl) {
37707                   backdropScope = $rootScope.$new(true);
37708                   backdropScope.modalOptions = modal;
37709                   backdropScope.index = currBackdropIndex;
37710                   backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
37711                   backdropDomEl.attr('backdrop-class', modal.backdropClass);
37712                   if (modal.animation) {
37713                     backdropDomEl.attr('modal-animation', 'true');
37714                   }
37715                   $compile(backdropDomEl)(backdropScope);
37716                   $animate.enter(backdropDomEl, appendToElement);
37717                 }
37718
37719                 var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
37720                 angularDomEl.attr({
37721                   'template-url': modal.windowTemplateUrl,
37722                   'window-class': modal.windowClass,
37723                   'window-top-class': modal.windowTopClass,
37724                   'size': modal.size,
37725                   'index': openedWindows.length() - 1,
37726                   'animate': 'animate'
37727                 }).html(modal.content);
37728                 if (modal.animation) {
37729                   angularDomEl.attr('modal-animation', 'true');
37730                 }
37731
37732                 $animate.enter(angularDomEl, appendToElement)
37733                   .then(function() {
37734                     $compile(angularDomEl)(modal.scope);
37735                     $animate.addClass(appendToElement, modalBodyClass);
37736                   });
37737
37738                 openedWindows.top().value.modalDomEl = angularDomEl;
37739                 openedWindows.top().value.modalOpener = modalOpener;
37740
37741                 $modalStack.clearFocusListCache();
37742               };
37743
37744               function broadcastClosing(modalWindow, resultOrReason, closing) {
37745                 return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
37746               }
37747
37748               $modalStack.close = function(modalInstance, result) {
37749                 var modalWindow = openedWindows.get(modalInstance);
37750                 if (modalWindow && broadcastClosing(modalWindow, result, true)) {
37751                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37752                   modalWindow.value.deferred.resolve(result);
37753                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37754                   return true;
37755                 }
37756                 return !modalWindow;
37757               };
37758
37759               $modalStack.dismiss = function(modalInstance, reason) {
37760                 var modalWindow = openedWindows.get(modalInstance);
37761                 if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
37762                   modalWindow.value.modalScope.$$uibDestructionScheduled = true;
37763                   modalWindow.value.deferred.reject(reason);
37764                   removeModalWindow(modalInstance, modalWindow.value.modalOpener);
37765                   return true;
37766                 }
37767                 return !modalWindow;
37768               };
37769
37770               $modalStack.dismissAll = function(reason) {
37771                 var topModal = this.getTop();
37772                 while (topModal && this.dismiss(topModal.key, reason)) {
37773                   topModal = this.getTop();
37774                 }
37775               };
37776
37777               $modalStack.getTop = function() {
37778                 return openedWindows.top();
37779               };
37780
37781               $modalStack.modalRendered = function(modalInstance) {
37782                 var modalWindow = openedWindows.get(modalInstance);
37783                 if (modalWindow) {
37784                   modalWindow.value.renderDeferred.resolve();
37785                 }
37786               };
37787
37788               $modalStack.focusFirstFocusableElement = function() {
37789                 if (focusableElementList.length > 0) {
37790                   focusableElementList[0].focus();
37791                   return true;
37792                 }
37793                 return false;
37794               };
37795               $modalStack.focusLastFocusableElement = function() {
37796                 if (focusableElementList.length > 0) {
37797                   focusableElementList[focusableElementList.length - 1].focus();
37798                   return true;
37799                 }
37800                 return false;
37801               };
37802
37803               $modalStack.isFocusInFirstItem = function(evt) {
37804                 if (focusableElementList.length > 0) {
37805                   return (evt.target || evt.srcElement) === focusableElementList[0];
37806                 }
37807                 return false;
37808               };
37809
37810               $modalStack.isFocusInLastItem = function(evt) {
37811                 if (focusableElementList.length > 0) {
37812                   return (evt.target || evt.srcElement) === focusableElementList[focusableElementList.length - 1];
37813                 }
37814                 return false;
37815               };
37816
37817               $modalStack.clearFocusListCache = function() {
37818                 focusableElementList = [];
37819                 focusIndex = 0;
37820               };
37821
37822               $modalStack.loadFocusElementList = function(modalWindow) {
37823                 if (focusableElementList === undefined || !focusableElementList.length) {
37824                   if (modalWindow) {
37825                     var modalDomE1 = modalWindow.value.modalDomEl;
37826                     if (modalDomE1 && modalDomE1.length) {
37827                       focusableElementList = modalDomE1[0].querySelectorAll(tababbleSelector);
37828                     }
37829                   }
37830                 }
37831               };
37832
37833               return $modalStack;
37834             }])
37835
37836           .provider('$uibModal', function() {
37837             var $modalProvider = {
37838               options: {
37839                 animation: true,
37840                 backdrop: true, //can also be false or 'static'
37841                 keyboard: true
37842               },
37843               $get: ['$rootScope', '$q', '$document', '$templateRequest', '$controller', '$uibResolve', '$uibModalStack',
37844                 function ($rootScope, $q, $document, $templateRequest, $controller, $uibResolve, $modalStack) {
37845                   var $modal = {};
37846
37847                   function getTemplatePromise(options) {
37848                     return options.template ? $q.when(options.template) :
37849                       $templateRequest(angular.isFunction(options.templateUrl) ?
37850                         options.templateUrl() : options.templateUrl);
37851                   }
37852
37853                   var promiseChain = null;
37854                   $modal.getPromiseChain = function() {
37855                     return promiseChain;
37856                   };
37857
37858                   $modal.open = function(modalOptions) {
37859                     var modalResultDeferred = $q.defer();
37860                     var modalOpenedDeferred = $q.defer();
37861                     var modalClosedDeferred = $q.defer();
37862                     var modalRenderDeferred = $q.defer();
37863
37864                     //prepare an instance of a modal to be injected into controllers and returned to a caller
37865                     var modalInstance = {
37866                       result: modalResultDeferred.promise,
37867                       opened: modalOpenedDeferred.promise,
37868                       closed: modalClosedDeferred.promise,
37869                       rendered: modalRenderDeferred.promise,
37870                       close: function (result) {
37871                         return $modalStack.close(modalInstance, result);
37872                       },
37873                       dismiss: function (reason) {
37874                         return $modalStack.dismiss(modalInstance, reason);
37875                       }
37876                     };
37877
37878                     //merge and clean up options
37879                     modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
37880                     modalOptions.resolve = modalOptions.resolve || {};
37881                     modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
37882
37883                     //verify options
37884                     if (!modalOptions.template && !modalOptions.templateUrl) {
37885                       throw new Error('One of template or templateUrl options is required.');
37886                     }
37887
37888                     var templateAndResolvePromise =
37889                       $q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
37890
37891                     function resolveWithTemplate() {
37892                       return templateAndResolvePromise;
37893                     }
37894
37895                     // Wait for the resolution of the existing promise chain.
37896                     // Then switch to our own combined promise dependency (regardless of how the previous modal fared).
37897                     // Then add to $modalStack and resolve opened.
37898                     // Finally clean up the chain variable if no subsequent modal has overwritten it.
37899                     var samePromise;
37900                     samePromise = promiseChain = $q.all([promiseChain])
37901                       .then(resolveWithTemplate, resolveWithTemplate)
37902                       .then(function resolveSuccess(tplAndVars) {
37903                         var providedScope = modalOptions.scope || $rootScope;
37904
37905                         var modalScope = providedScope.$new();
37906                         modalScope.$close = modalInstance.close;
37907                         modalScope.$dismiss = modalInstance.dismiss;
37908
37909                         modalScope.$on('$destroy', function() {
37910                           if (!modalScope.$$uibDestructionScheduled) {
37911                             modalScope.$dismiss('$uibUnscheduledDestruction');
37912                           }
37913                         });
37914
37915                         var ctrlInstance, ctrlLocals = {};
37916
37917                         //controllers
37918                         if (modalOptions.controller) {
37919                           ctrlLocals.$scope = modalScope;
37920                           ctrlLocals.$uibModalInstance = modalInstance;
37921                           angular.forEach(tplAndVars[1], function(value, key) {
37922                             ctrlLocals[key] = value;
37923                           });
37924
37925                           ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
37926                           if (modalOptions.controllerAs) {
37927                             if (modalOptions.bindToController) {
37928                               ctrlInstance.$close = modalScope.$close;
37929                               ctrlInstance.$dismiss = modalScope.$dismiss;
37930                               angular.extend(ctrlInstance, providedScope);
37931                             }
37932
37933                             modalScope[modalOptions.controllerAs] = ctrlInstance;
37934                           }
37935                         }
37936
37937                         $modalStack.open(modalInstance, {
37938                           scope: modalScope,
37939                           deferred: modalResultDeferred,
37940                           renderDeferred: modalRenderDeferred,
37941                           closedDeferred: modalClosedDeferred,
37942                           content: tplAndVars[0],
37943                           animation: modalOptions.animation,
37944                           backdrop: modalOptions.backdrop,
37945                           keyboard: modalOptions.keyboard,
37946                           backdropClass: modalOptions.backdropClass,
37947                           windowTopClass: modalOptions.windowTopClass,
37948                           windowClass: modalOptions.windowClass,
37949                           windowTemplateUrl: modalOptions.windowTemplateUrl,
37950                           size: modalOptions.size,
37951                           openedClass: modalOptions.openedClass,
37952                           appendTo: modalOptions.appendTo
37953                         });
37954                         modalOpenedDeferred.resolve(true);
37955
37956                     }, function resolveError(reason) {
37957                       modalOpenedDeferred.reject(reason);
37958                       modalResultDeferred.reject(reason);
37959                     })['finally'](function() {
37960                       if (promiseChain === samePromise) {
37961                         promiseChain = null;
37962                       }
37963                     });
37964
37965                     return modalInstance;
37966                   };
37967
37968                   return $modal;
37969                 }
37970               ]
37971             };
37972
37973             return $modalProvider;
37974           });
37975
37976         angular.module('ui.bootstrap.paging', [])
37977         /**
37978          * Helper internal service for generating common controller code between the
37979          * pager and pagination components
37980          */
37981         .factory('uibPaging', ['$parse', function($parse) {
37982           return {
37983             create: function(ctrl, $scope, $attrs) {
37984               ctrl.setNumPages = $attrs.numPages ? $parse($attrs.numPages).assign : angular.noop;
37985               ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl
37986
37987               ctrl.init = function(ngModelCtrl, config) {
37988                 ctrl.ngModelCtrl = ngModelCtrl;
37989                 ctrl.config = config;
37990
37991                 ngModelCtrl.$render = function() {
37992                   ctrl.render();
37993                 };
37994
37995                 if ($attrs.itemsPerPage) {
37996                   $scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) {
37997                     ctrl.itemsPerPage = parseInt(value, 10);
37998                     $scope.totalPages = ctrl.calculateTotalPages();
37999                     ctrl.updatePage();
38000                   });
38001                 } else {
38002                   ctrl.itemsPerPage = config.itemsPerPage;
38003                 }
38004
38005                 $scope.$watch('totalItems', function(newTotal, oldTotal) {
38006                   if (angular.isDefined(newTotal) || newTotal !== oldTotal) {
38007                     $scope.totalPages = ctrl.calculateTotalPages();
38008                     ctrl.updatePage();
38009                   }
38010                 });
38011               };
38012
38013               ctrl.calculateTotalPages = function() {
38014                 var totalPages = ctrl.itemsPerPage < 1 ? 1 : Math.ceil($scope.totalItems / ctrl.itemsPerPage);
38015                 return Math.max(totalPages || 0, 1);
38016               };
38017
38018               ctrl.render = function() {
38019                 $scope.page = parseInt(ctrl.ngModelCtrl.$viewValue, 10) || 1;
38020               };
38021
38022               $scope.selectPage = function(page, evt) {
38023                 if (evt) {
38024                   evt.preventDefault();
38025                 }
38026
38027                 var clickAllowed = !$scope.ngDisabled || !evt;
38028                 if (clickAllowed && $scope.page !== page && page > 0 && page <= $scope.totalPages) {
38029                   if (evt && evt.target) {
38030                     evt.target.blur();
38031                   }
38032                   ctrl.ngModelCtrl.$setViewValue(page);
38033                   ctrl.ngModelCtrl.$render();
38034                 }
38035               };
38036
38037               $scope.getText = function(key) {
38038                 return $scope[key + 'Text'] || ctrl.config[key + 'Text'];
38039               };
38040
38041               $scope.noPrevious = function() {
38042                 return $scope.page === 1;
38043               };
38044
38045               $scope.noNext = function() {
38046                 return $scope.page === $scope.totalPages;
38047               };
38048
38049               ctrl.updatePage = function() {
38050                 ctrl.setNumPages($scope.$parent, $scope.totalPages); // Readonly variable
38051
38052                 if ($scope.page > $scope.totalPages) {
38053                   $scope.selectPage($scope.totalPages);
38054                 } else {
38055                   ctrl.ngModelCtrl.$render();
38056                 }
38057               };
38058             }
38059           };
38060         }]);
38061
38062         angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
38063
38064         .controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
38065           $scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
38066
38067           uibPaging.create(this, $scope, $attrs);
38068         }])
38069
38070         .constant('uibPagerConfig', {
38071           itemsPerPage: 10,
38072           previousText: '« Previous',
38073           nextText: 'Next »',
38074           align: true
38075         })
38076
38077         .directive('uibPager', ['uibPagerConfig', function(uibPagerConfig) {
38078           return {
38079             scope: {
38080               totalItems: '=',
38081               previousText: '@',
38082               nextText: '@',
38083               ngDisabled: '='
38084             },
38085             require: ['uibPager', '?ngModel'],
38086             controller: 'UibPagerController',
38087             controllerAs: 'pager',
38088             templateUrl: function(element, attrs) {
38089               return attrs.templateUrl || 'uib/template/pager/pager.html';
38090             },
38091             replace: true,
38092             link: function(scope, element, attrs, ctrls) {
38093               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38094
38095               if (!ngModelCtrl) {
38096                 return; // do nothing if no ng-model
38097               }
38098
38099               paginationCtrl.init(ngModelCtrl, uibPagerConfig);
38100             }
38101           };
38102         }]);
38103
38104         angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
38105         .controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
38106           var ctrl = this;
38107           // Setup configuration parameters
38108           var maxSize = angular.isDefined($attrs.maxSize) ? $scope.$parent.$eval($attrs.maxSize) : uibPaginationConfig.maxSize,
38109             rotate = angular.isDefined($attrs.rotate) ? $scope.$parent.$eval($attrs.rotate) : uibPaginationConfig.rotate,
38110             forceEllipses = angular.isDefined($attrs.forceEllipses) ? $scope.$parent.$eval($attrs.forceEllipses) : uibPaginationConfig.forceEllipses,
38111             boundaryLinkNumbers = angular.isDefined($attrs.boundaryLinkNumbers) ? $scope.$parent.$eval($attrs.boundaryLinkNumbers) : uibPaginationConfig.boundaryLinkNumbers;
38112           $scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
38113           $scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
38114
38115           uibPaging.create(this, $scope, $attrs);
38116
38117           if ($attrs.maxSize) {
38118             $scope.$parent.$watch($parse($attrs.maxSize), function(value) {
38119               maxSize = parseInt(value, 10);
38120               ctrl.render();
38121             });
38122           }
38123
38124           // Create page object used in template
38125           function makePage(number, text, isActive) {
38126             return {
38127               number: number,
38128               text: text,
38129               active: isActive
38130             };
38131           }
38132
38133           function getPages(currentPage, totalPages) {
38134             var pages = [];
38135
38136             // Default page limits
38137             var startPage = 1, endPage = totalPages;
38138             var isMaxSized = angular.isDefined(maxSize) && maxSize < totalPages;
38139
38140             // recompute if maxSize
38141             if (isMaxSized) {
38142               if (rotate) {
38143                 // Current page is displayed in the middle of the visible ones
38144                 startPage = Math.max(currentPage - Math.floor(maxSize / 2), 1);
38145                 endPage = startPage + maxSize - 1;
38146
38147                 // Adjust if limit is exceeded
38148                 if (endPage > totalPages) {
38149                   endPage = totalPages;
38150                   startPage = endPage - maxSize + 1;
38151                 }
38152               } else {
38153                 // Visible pages are paginated with maxSize
38154                 startPage = (Math.ceil(currentPage / maxSize) - 1) * maxSize + 1;
38155
38156                 // Adjust last page if limit is exceeded
38157                 endPage = Math.min(startPage + maxSize - 1, totalPages);
38158               }
38159             }
38160
38161             // Add page number links
38162             for (var number = startPage; number <= endPage; number++) {
38163               var page = makePage(number, number, number === currentPage);
38164               pages.push(page);
38165             }
38166
38167             // Add links to move between page sets
38168             if (isMaxSized && maxSize > 0 && (!rotate || forceEllipses || boundaryLinkNumbers)) {
38169               if (startPage > 1) {
38170                 if (!boundaryLinkNumbers || startPage > 3) { //need ellipsis for all options unless range is too close to beginning
38171                 var previousPageSet = makePage(startPage - 1, '...', false);
38172                 pages.unshift(previousPageSet);
38173               }
38174                 if (boundaryLinkNumbers) {
38175                   if (startPage === 3) { //need to replace ellipsis when the buttons would be sequential
38176                     var secondPageLink = makePage(2, '2', false);
38177                     pages.unshift(secondPageLink);
38178                   }
38179                   //add the first page
38180                   var firstPageLink = makePage(1, '1', false);
38181                   pages.unshift(firstPageLink);
38182                 }
38183               }
38184
38185               if (endPage < totalPages) {
38186                 if (!boundaryLinkNumbers || endPage < totalPages - 2) { //need ellipsis for all options unless range is too close to end
38187                 var nextPageSet = makePage(endPage + 1, '...', false);
38188                 pages.push(nextPageSet);
38189               }
38190                 if (boundaryLinkNumbers) {
38191                   if (endPage === totalPages - 2) { //need to replace ellipsis when the buttons would be sequential
38192                     var secondToLastPageLink = makePage(totalPages - 1, totalPages - 1, false);
38193                     pages.push(secondToLastPageLink);
38194                   }
38195                   //add the last page
38196                   var lastPageLink = makePage(totalPages, totalPages, false);
38197                   pages.push(lastPageLink);
38198                 }
38199               }
38200             }
38201             return pages;
38202           }
38203
38204           var originalRender = this.render;
38205           this.render = function() {
38206             originalRender();
38207             if ($scope.page > 0 && $scope.page <= $scope.totalPages) {
38208               $scope.pages = getPages($scope.page, $scope.totalPages);
38209             }
38210           };
38211         }])
38212
38213         .constant('uibPaginationConfig', {
38214           itemsPerPage: 10,
38215           boundaryLinks: false,
38216           boundaryLinkNumbers: false,
38217           directionLinks: true,
38218           firstText: 'First',
38219           previousText: 'Previous',
38220           nextText: 'Next',
38221           lastText: 'Last',
38222           rotate: true,
38223           forceEllipses: false
38224         })
38225
38226         .directive('uibPagination', ['$parse', 'uibPaginationConfig', function($parse, uibPaginationConfig) {
38227           return {
38228             scope: {
38229               totalItems: '=',
38230               firstText: '@',
38231               previousText: '@',
38232               nextText: '@',
38233               lastText: '@',
38234               ngDisabled:'='
38235             },
38236             require: ['uibPagination', '?ngModel'],
38237             controller: 'UibPaginationController',
38238             controllerAs: 'pagination',
38239             templateUrl: function(element, attrs) {
38240               return attrs.templateUrl || 'uib/template/pagination/pagination.html';
38241             },
38242             replace: true,
38243             link: function(scope, element, attrs, ctrls) {
38244               var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
38245
38246               if (!ngModelCtrl) {
38247                  return; // do nothing if no ng-model
38248               }
38249
38250               paginationCtrl.init(ngModelCtrl, uibPaginationConfig);
38251             }
38252           };
38253         }]);
38254
38255         /**
38256          * The following features are still outstanding: animation as a
38257          * function, placement as a function, inside, support for more triggers than
38258          * just mouse enter/leave, html tooltips, and selector delegation.
38259          */
38260         angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.stackedMap'])
38261
38262         /**
38263          * The $tooltip service creates tooltip- and popover-like directives as well as
38264          * houses global options for them.
38265          */
38266         .provider('$uibTooltip', function() {
38267           // The default options tooltip and popover.
38268           var defaultOptions = {
38269             placement: 'top',
38270             placementClassPrefix: '',
38271             animation: true,
38272             popupDelay: 0,
38273             popupCloseDelay: 0,
38274             useContentExp: false
38275           };
38276
38277           // Default hide triggers for each show trigger
38278           var triggerMap = {
38279             'mouseenter': 'mouseleave',
38280             'click': 'click',
38281             'outsideClick': 'outsideClick',
38282             'focus': 'blur',
38283             'none': ''
38284           };
38285
38286           // The options specified to the provider globally.
38287           var globalOptions = {};
38288
38289           /**
38290            * `options({})` allows global configuration of all tooltips in the
38291            * application.
38292            *
38293            *   var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
38294            *     // place tooltips left instead of top by default
38295            *     $tooltipProvider.options( { placement: 'left' } );
38296            *   });
38297            */
38298                 this.options = function(value) {
38299                         angular.extend(globalOptions, value);
38300                 };
38301
38302           /**
38303            * This allows you to extend the set of trigger mappings available. E.g.:
38304            *
38305            *   $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
38306            */
38307           this.setTriggers = function setTriggers(triggers) {
38308             angular.extend(triggerMap, triggers);
38309           };
38310
38311           /**
38312            * This is a helper function for translating camel-case to snake_case.
38313            */
38314           function snake_case(name) {
38315             var regexp = /[A-Z]/g;
38316             var separator = '-';
38317             return name.replace(regexp, function(letter, pos) {
38318               return (pos ? separator : '') + letter.toLowerCase();
38319             });
38320           }
38321
38322           /**
38323            * Returns the actual instance of the $tooltip service.
38324            * TODO support multiple triggers
38325            */
38326           this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
38327             var openedTooltips = $$stackedMap.createNew();
38328             $document.on('keypress', keypressListener);
38329
38330             $rootScope.$on('$destroy', function() {
38331               $document.off('keypress', keypressListener);
38332             });
38333
38334             function keypressListener(e) {
38335               if (e.which === 27) {
38336                 var last = openedTooltips.top();
38337                 if (last) {
38338                   last.value.close();
38339                   openedTooltips.removeTop();
38340                   last = null;
38341                 }
38342               }
38343             }
38344
38345             return function $tooltip(ttType, prefix, defaultTriggerShow, options) {
38346               options = angular.extend({}, defaultOptions, globalOptions, options);
38347
38348               /**
38349                * Returns an object of show and hide triggers.
38350                *
38351                * If a trigger is supplied,
38352                * it is used to show the tooltip; otherwise, it will use the `trigger`
38353                * option passed to the `$tooltipProvider.options` method; else it will
38354                * default to the trigger supplied to this directive factory.
38355                *
38356                * The hide trigger is based on the show trigger. If the `trigger` option
38357                * was passed to the `$tooltipProvider.options` method, it will use the
38358                * mapped trigger from `triggerMap` or the passed trigger if the map is
38359                * undefined; otherwise, it uses the `triggerMap` value of the show
38360                * trigger; else it will just use the show trigger.
38361                */
38362               function getTriggers(trigger) {
38363                 var show = (trigger || options.trigger || defaultTriggerShow).split(' ');
38364                 var hide = show.map(function(trigger) {
38365                   return triggerMap[trigger] || trigger;
38366                 });
38367                 return {
38368                   show: show,
38369                   hide: hide
38370                 };
38371               }
38372
38373               var directiveName = snake_case(ttType);
38374
38375               var startSym = $interpolate.startSymbol();
38376               var endSym = $interpolate.endSymbol();
38377               var template =
38378                 '<div '+ directiveName + '-popup '+
38379                   'title="' + startSym + 'title' + endSym + '" '+
38380                   (options.useContentExp ?
38381                     'content-exp="contentExp()" ' :
38382                     'content="' + startSym + 'content' + endSym + '" ') +
38383                   'placement="' + startSym + 'placement' + endSym + '" '+
38384                   'popup-class="' + startSym + 'popupClass' + endSym + '" '+
38385                   'animation="animation" ' +
38386                   'is-open="isOpen"' +
38387                   'origin-scope="origScope" ' +
38388                   'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' +
38389                   '>' +
38390                 '</div>';
38391
38392               return {
38393                 compile: function(tElem, tAttrs) {
38394                   var tooltipLinker = $compile(template);
38395
38396                   return function link(scope, element, attrs, tooltipCtrl) {
38397                     var tooltip;
38398                     var tooltipLinkedScope;
38399                     var transitionTimeout;
38400                     var showTimeout;
38401                     var hideTimeout;
38402                     var positionTimeout;
38403                     var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
38404                     var triggers = getTriggers(undefined);
38405                     var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
38406                     var ttScope = scope.$new(true);
38407                     var repositionScheduled = false;
38408                     var isOpenParse = angular.isDefined(attrs[prefix + 'IsOpen']) ? $parse(attrs[prefix + 'IsOpen']) : false;
38409                     var contentParse = options.useContentExp ? $parse(attrs[ttType]) : false;
38410                     var observers = [];
38411
38412                     var positionTooltip = function() {
38413                       // check if tooltip exists and is not empty
38414                       if (!tooltip || !tooltip.html()) { return; }
38415
38416                       if (!positionTimeout) {
38417                         positionTimeout = $timeout(function() {
38418                           // Reset the positioning.
38419                           tooltip.css({ top: 0, left: 0 });
38420
38421                           // Now set the calculated positioning.
38422                           var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
38423                           tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px', visibility: 'visible' });
38424
38425                           // If the placement class is prefixed, still need
38426                           // to remove the TWBS standard class.
38427                           if (options.placementClassPrefix) {
38428                             tooltip.removeClass('top bottom left right');
38429                           }
38430
38431                           tooltip.removeClass(
38432                             options.placementClassPrefix + 'top ' +
38433                             options.placementClassPrefix + 'top-left ' +
38434                             options.placementClassPrefix + 'top-right ' +
38435                             options.placementClassPrefix + 'bottom ' +
38436                             options.placementClassPrefix + 'bottom-left ' +
38437                             options.placementClassPrefix + 'bottom-right ' +
38438                             options.placementClassPrefix + 'left ' +
38439                             options.placementClassPrefix + 'left-top ' +
38440                             options.placementClassPrefix + 'left-bottom ' +
38441                             options.placementClassPrefix + 'right ' +
38442                             options.placementClassPrefix + 'right-top ' +
38443                             options.placementClassPrefix + 'right-bottom');
38444
38445                           var placement = ttPosition.placement.split('-');
38446                           tooltip.addClass(placement[0], options.placementClassPrefix + ttPosition.placement);
38447                           $position.positionArrow(tooltip, ttPosition.placement);
38448
38449                           positionTimeout = null;
38450                         }, 0, false);
38451                       }
38452                     };
38453
38454                     // Set up the correct scope to allow transclusion later
38455                     ttScope.origScope = scope;
38456
38457                     // By default, the tooltip is not open.
38458                     // TODO add ability to start tooltip opened
38459                     ttScope.isOpen = false;
38460                     openedTooltips.add(ttScope, {
38461                       close: hide
38462                     });
38463
38464                     function toggleTooltipBind() {
38465                       if (!ttScope.isOpen) {
38466                         showTooltipBind();
38467                       } else {
38468                         hideTooltipBind();
38469                       }
38470                     }
38471
38472                     // Show the tooltip with delay if specified, otherwise show it immediately
38473                     function showTooltipBind() {
38474                       if (hasEnableExp && !scope.$eval(attrs[prefix + 'Enable'])) {
38475                         return;
38476                       }
38477
38478                       cancelHide();
38479                       prepareTooltip();
38480
38481                       if (ttScope.popupDelay) {
38482                         // Do nothing if the tooltip was already scheduled to pop-up.
38483                         // This happens if show is triggered multiple times before any hide is triggered.
38484                         if (!showTimeout) {
38485                           showTimeout = $timeout(show, ttScope.popupDelay, false);
38486                         }
38487                       } else {
38488                         show();
38489                       }
38490                     }
38491
38492                     function hideTooltipBind() {
38493                       cancelShow();
38494
38495                       if (ttScope.popupCloseDelay) {
38496                         if (!hideTimeout) {
38497                           hideTimeout = $timeout(hide, ttScope.popupCloseDelay, false);
38498                         }
38499                       } else {
38500                         hide();
38501                       }
38502                     }
38503
38504                     // Show the tooltip popup element.
38505                     function show() {
38506                       cancelShow();
38507                       cancelHide();
38508
38509                       // Don't show empty tooltips.
38510                       if (!ttScope.content) {
38511                         return angular.noop;
38512                       }
38513
38514                       createTooltip();
38515
38516                       // And show the tooltip.
38517                       ttScope.$evalAsync(function() {
38518                         ttScope.isOpen = true;
38519                         assignIsOpen(true);
38520                         positionTooltip();
38521                       });
38522                     }
38523
38524                     function cancelShow() {
38525                       if (showTimeout) {
38526                         $timeout.cancel(showTimeout);
38527                         showTimeout = null;
38528                       }
38529
38530                       if (positionTimeout) {
38531                         $timeout.cancel(positionTimeout);
38532                         positionTimeout = null;
38533                       }
38534                     }
38535
38536                     // Hide the tooltip popup element.
38537                     function hide() {
38538                       if (!ttScope) {
38539                         return;
38540                       }
38541
38542                       // First things first: we don't show it anymore.
38543                       ttScope.$evalAsync(function() {
38544                         ttScope.isOpen = false;
38545                         assignIsOpen(false);
38546                         // And now we remove it from the DOM. However, if we have animation, we
38547                         // need to wait for it to expire beforehand.
38548                         // FIXME: this is a placeholder for a port of the transitions library.
38549                         // The fade transition in TWBS is 150ms.
38550                         if (ttScope.animation) {
38551                           if (!transitionTimeout) {
38552                             transitionTimeout = $timeout(removeTooltip, 150, false);
38553                           }
38554                         } else {
38555                           removeTooltip();
38556                         }
38557                       });
38558                     }
38559
38560                     function cancelHide() {
38561                       if (hideTimeout) {
38562                         $timeout.cancel(hideTimeout);
38563                         hideTimeout = null;
38564                       }
38565                       if (transitionTimeout) {
38566                         $timeout.cancel(transitionTimeout);
38567                         transitionTimeout = null;
38568                       }
38569                     }
38570
38571                     function createTooltip() {
38572                       // There can only be one tooltip element per directive shown at once.
38573                       if (tooltip) {
38574                         return;
38575                       }
38576
38577                       tooltipLinkedScope = ttScope.$new();
38578                       tooltip = tooltipLinker(tooltipLinkedScope, function(tooltip) {
38579                         if (appendToBody) {
38580                           $document.find('body').append(tooltip);
38581                         } else {
38582                           element.after(tooltip);
38583                         }
38584                       });
38585
38586                       prepObservers();
38587                     }
38588
38589                     function removeTooltip() {
38590                       cancelShow();
38591                       cancelHide();
38592                       unregisterObservers();
38593
38594                       if (tooltip) {
38595                         tooltip.remove();
38596                         tooltip = null;
38597                       }
38598                       if (tooltipLinkedScope) {
38599                         tooltipLinkedScope.$destroy();
38600                         tooltipLinkedScope = null;
38601                       }
38602                     }
38603
38604                     /**
38605                      * Set the initial scope values. Once
38606                      * the tooltip is created, the observers
38607                      * will be added to keep things in sync.
38608                      */
38609                     function prepareTooltip() {
38610                       ttScope.title = attrs[prefix + 'Title'];
38611                       if (contentParse) {
38612                         ttScope.content = contentParse(scope);
38613                       } else {
38614                         ttScope.content = attrs[ttType];
38615                       }
38616
38617                       ttScope.popupClass = attrs[prefix + 'Class'];
38618                       ttScope.placement = angular.isDefined(attrs[prefix + 'Placement']) ? attrs[prefix + 'Placement'] : options.placement;
38619
38620                       var delay = parseInt(attrs[prefix + 'PopupDelay'], 10);
38621                       var closeDelay = parseInt(attrs[prefix + 'PopupCloseDelay'], 10);
38622                       ttScope.popupDelay = !isNaN(delay) ? delay : options.popupDelay;
38623                       ttScope.popupCloseDelay = !isNaN(closeDelay) ? closeDelay : options.popupCloseDelay;
38624                     }
38625
38626                     function assignIsOpen(isOpen) {
38627                       if (isOpenParse && angular.isFunction(isOpenParse.assign)) {
38628                         isOpenParse.assign(scope, isOpen);
38629                       }
38630                     }
38631
38632                     ttScope.contentExp = function() {
38633                       return ttScope.content;
38634                     };
38635
38636                     /**
38637                      * Observe the relevant attributes.
38638                      */
38639                     attrs.$observe('disabled', function(val) {
38640                       if (val) {
38641                         cancelShow();
38642                       }
38643
38644                       if (val && ttScope.isOpen) {
38645                         hide();
38646                       }
38647                     });
38648
38649                     if (isOpenParse) {
38650                       scope.$watch(isOpenParse, function(val) {
38651                         if (ttScope && !val === ttScope.isOpen) {
38652                           toggleTooltipBind();
38653                         }
38654                       });
38655                     }
38656
38657                     function prepObservers() {
38658                       observers.length = 0;
38659
38660                       if (contentParse) {
38661                         observers.push(
38662                           scope.$watch(contentParse, function(val) {
38663                             ttScope.content = val;
38664                             if (!val && ttScope.isOpen) {
38665                               hide();
38666                             }
38667                           })
38668                         );
38669
38670                         observers.push(
38671                           tooltipLinkedScope.$watch(function() {
38672                             if (!repositionScheduled) {
38673                               repositionScheduled = true;
38674                               tooltipLinkedScope.$$postDigest(function() {
38675                                 repositionScheduled = false;
38676                                 if (ttScope && ttScope.isOpen) {
38677                                   positionTooltip();
38678                                 }
38679                               });
38680                             }
38681                           })
38682                         );
38683                       } else {
38684                         observers.push(
38685                           attrs.$observe(ttType, function(val) {
38686                             ttScope.content = val;
38687                             if (!val && ttScope.isOpen) {
38688                               hide();
38689                             } else {
38690                               positionTooltip();
38691                             }
38692                           })
38693                         );
38694                       }
38695
38696                       observers.push(
38697                         attrs.$observe(prefix + 'Title', function(val) {
38698                           ttScope.title = val;
38699                           if (ttScope.isOpen) {
38700                             positionTooltip();
38701                           }
38702                         })
38703                       );
38704
38705                       observers.push(
38706                         attrs.$observe(prefix + 'Placement', function(val) {
38707                           ttScope.placement = val ? val : options.placement;
38708                           if (ttScope.isOpen) {
38709                             positionTooltip();
38710                           }
38711                         })
38712                       );
38713                     }
38714
38715                     function unregisterObservers() {
38716                       if (observers.length) {
38717                         angular.forEach(observers, function(observer) {
38718                           observer();
38719                         });
38720                         observers.length = 0;
38721                       }
38722                     }
38723
38724                     // hide tooltips/popovers for outsideClick trigger
38725                     function bodyHideTooltipBind(e) {
38726                       if (!ttScope || !ttScope.isOpen || !tooltip) {
38727                         return;
38728                       }
38729                       // make sure the tooltip/popover link or tool tooltip/popover itself were not clicked
38730                       if (!element[0].contains(e.target) && !tooltip[0].contains(e.target)) {
38731                         hideTooltipBind();
38732                       }
38733                     }
38734
38735                     var unregisterTriggers = function() {
38736                       triggers.show.forEach(function(trigger) {
38737                         if (trigger === 'outsideClick') {
38738                           element.off('click', toggleTooltipBind);
38739                         } else {
38740                           element.off(trigger, showTooltipBind);
38741                           element.off(trigger, toggleTooltipBind);
38742                         }
38743                       });
38744                       triggers.hide.forEach(function(trigger) {
38745                         if (trigger === 'outsideClick') {
38746                           $document.off('click', bodyHideTooltipBind);
38747                         } else {
38748                           element.off(trigger, hideTooltipBind);
38749                         }
38750                       });
38751                     };
38752
38753                     function prepTriggers() {
38754                       var val = attrs[prefix + 'Trigger'];
38755                       unregisterTriggers();
38756
38757                       triggers = getTriggers(val);
38758
38759                       if (triggers.show !== 'none') {
38760                         triggers.show.forEach(function(trigger, idx) {
38761                           if (trigger === 'outsideClick') {
38762                             element.on('click', toggleTooltipBind);
38763                             $document.on('click', bodyHideTooltipBind);
38764                           } else if (trigger === triggers.hide[idx]) {
38765                             element.on(trigger, toggleTooltipBind);
38766                           } else if (trigger) {
38767                             element.on(trigger, showTooltipBind);
38768                             element.on(triggers.hide[idx], hideTooltipBind);
38769                           }
38770
38771                           element.on('keypress', function(e) {
38772                             if (e.which === 27) {
38773                               hideTooltipBind();
38774                             }
38775                           });
38776                         });
38777                       }
38778                     }
38779
38780                     prepTriggers();
38781
38782                     var animation = scope.$eval(attrs[prefix + 'Animation']);
38783                     ttScope.animation = angular.isDefined(animation) ? !!animation : options.animation;
38784
38785                     var appendToBodyVal;
38786                     var appendKey = prefix + 'AppendToBody';
38787                     if (appendKey in attrs && attrs[appendKey] === undefined) {
38788                       appendToBodyVal = true;
38789                     } else {
38790                       appendToBodyVal = scope.$eval(attrs[appendKey]);
38791                     }
38792
38793                     appendToBody = angular.isDefined(appendToBodyVal) ? appendToBodyVal : appendToBody;
38794
38795                     // if a tooltip is attached to <body> we need to remove it on
38796                     // location change as its parent scope will probably not be destroyed
38797                     // by the change.
38798                     if (appendToBody) {
38799                       scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess() {
38800                         if (ttScope.isOpen) {
38801                           hide();
38802                         }
38803                       });
38804                     }
38805
38806                     // Make sure tooltip is destroyed and removed.
38807                     scope.$on('$destroy', function onDestroyTooltip() {
38808                       unregisterTriggers();
38809                       removeTooltip();
38810                       openedTooltips.remove(ttScope);
38811                       ttScope = null;
38812                     });
38813                   };
38814                 }
38815               };
38816             };
38817           }];
38818         })
38819
38820         // This is mostly ngInclude code but with a custom scope
38821         .directive('uibTooltipTemplateTransclude', [
38822                  '$animate', '$sce', '$compile', '$templateRequest',
38823         function ($animate, $sce, $compile, $templateRequest) {
38824           return {
38825             link: function(scope, elem, attrs) {
38826               var origScope = scope.$eval(attrs.tooltipTemplateTranscludeScope);
38827
38828               var changeCounter = 0,
38829                 currentScope,
38830                 previousElement,
38831                 currentElement;
38832
38833               var cleanupLastIncludeContent = function() {
38834                 if (previousElement) {
38835                   previousElement.remove();
38836                   previousElement = null;
38837                 }
38838
38839                 if (currentScope) {
38840                   currentScope.$destroy();
38841                   currentScope = null;
38842                 }
38843
38844                 if (currentElement) {
38845                   $animate.leave(currentElement).then(function() {
38846                     previousElement = null;
38847                   });
38848                   previousElement = currentElement;
38849                   currentElement = null;
38850                 }
38851               };
38852
38853               scope.$watch($sce.parseAsResourceUrl(attrs.uibTooltipTemplateTransclude), function(src) {
38854                 var thisChangeId = ++changeCounter;
38855
38856                 if (src) {
38857                   //set the 2nd param to true to ignore the template request error so that the inner
38858                   //contents and scope can be cleaned up.
38859                   $templateRequest(src, true).then(function(response) {
38860                     if (thisChangeId !== changeCounter) { return; }
38861                     var newScope = origScope.$new();
38862                     var template = response;
38863
38864                     var clone = $compile(template)(newScope, function(clone) {
38865                       cleanupLastIncludeContent();
38866                       $animate.enter(clone, elem);
38867                     });
38868
38869                     currentScope = newScope;
38870                     currentElement = clone;
38871
38872                     currentScope.$emit('$includeContentLoaded', src);
38873                   }, function() {
38874                     if (thisChangeId === changeCounter) {
38875                       cleanupLastIncludeContent();
38876                       scope.$emit('$includeContentError', src);
38877                     }
38878                   });
38879                   scope.$emit('$includeContentRequested', src);
38880                 } else {
38881                   cleanupLastIncludeContent();
38882                 }
38883               });
38884
38885               scope.$on('$destroy', cleanupLastIncludeContent);
38886             }
38887           };
38888         }])
38889
38890         /**
38891          * Note that it's intentional that these classes are *not* applied through $animate.
38892          * They must not be animated as they're expected to be present on the tooltip on
38893          * initialization.
38894          */
38895         .directive('uibTooltipClasses', ['$uibPosition', function($uibPosition) {
38896           return {
38897             restrict: 'A',
38898             link: function(scope, element, attrs) {
38899               // need to set the primary position so the
38900               // arrow has space during position measure.
38901               // tooltip.positionTooltip()
38902               if (scope.placement) {
38903                 // // There are no top-left etc... classes
38904                 // // in TWBS, so we need the primary position.
38905                 var position = $uibPosition.parsePlacement(scope.placement);
38906                 element.addClass(position[0]);
38907               } else {
38908                 element.addClass('top');
38909               }
38910
38911               if (scope.popupClass) {
38912                 element.addClass(scope.popupClass);
38913               }
38914
38915               if (scope.animation()) {
38916                 element.addClass(attrs.tooltipAnimationClass);
38917               }
38918             }
38919           };
38920         }])
38921
38922         .directive('uibTooltipPopup', function() {
38923           return {
38924             replace: true,
38925             scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38926             templateUrl: 'uib/template/tooltip/tooltip-popup.html'
38927           };
38928         })
38929
38930         .directive('uibTooltip', [ '$uibTooltip', function($uibTooltip) {
38931           return $uibTooltip('uibTooltip', 'tooltip', 'mouseenter');
38932         }])
38933
38934         .directive('uibTooltipTemplatePopup', function() {
38935           return {
38936             replace: true,
38937             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38938               originScope: '&' },
38939             templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
38940           };
38941         })
38942
38943         .directive('uibTooltipTemplate', ['$uibTooltip', function($uibTooltip) {
38944           return $uibTooltip('uibTooltipTemplate', 'tooltip', 'mouseenter', {
38945             useContentExp: true
38946           });
38947         }])
38948
38949         .directive('uibTooltipHtmlPopup', function() {
38950           return {
38951             replace: true,
38952             scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38953             templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
38954           };
38955         })
38956
38957         .directive('uibTooltipHtml', ['$uibTooltip', function($uibTooltip) {
38958           return $uibTooltip('uibTooltipHtml', 'tooltip', 'mouseenter', {
38959             useContentExp: true
38960           });
38961         }]);
38962
38963         /**
38964          * The following features are still outstanding: popup delay, animation as a
38965          * function, placement as a function, inside, support for more triggers than
38966          * just mouse enter/leave, and selector delegatation.
38967          */
38968         angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
38969
38970         .directive('uibPopoverTemplatePopup', function() {
38971           return {
38972             replace: true,
38973             scope: { title: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
38974               originScope: '&' },
38975             templateUrl: 'uib/template/popover/popover-template.html'
38976           };
38977         })
38978
38979         .directive('uibPopoverTemplate', ['$uibTooltip', function($uibTooltip) {
38980           return $uibTooltip('uibPopoverTemplate', 'popover', 'click', {
38981             useContentExp: true
38982           });
38983         }])
38984
38985         .directive('uibPopoverHtmlPopup', function() {
38986           return {
38987             replace: true,
38988             scope: { contentExp: '&', title: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
38989             templateUrl: 'uib/template/popover/popover-html.html'
38990           };
38991         })
38992
38993         .directive('uibPopoverHtml', ['$uibTooltip', function($uibTooltip) {
38994           return $uibTooltip('uibPopoverHtml', 'popover', 'click', {
38995             useContentExp: true
38996           });
38997         }])
38998
38999         .directive('uibPopoverPopup', function() {
39000           return {
39001             replace: true,
39002             scope: { title: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
39003             templateUrl: 'uib/template/popover/popover.html'
39004           };
39005         })
39006
39007         .directive('uibPopover', ['$uibTooltip', function($uibTooltip) {
39008           return $uibTooltip('uibPopover', 'popover', 'click');
39009         }]);
39010
39011         angular.module('ui.bootstrap.progressbar', [])
39012
39013         .constant('uibProgressConfig', {
39014           animate: true,
39015           max: 100
39016         })
39017
39018         .controller('UibProgressController', ['$scope', '$attrs', 'uibProgressConfig', function($scope, $attrs, progressConfig) {
39019           var self = this,
39020               animate = angular.isDefined($attrs.animate) ? $scope.$parent.$eval($attrs.animate) : progressConfig.animate;
39021
39022           this.bars = [];
39023           $scope.max = angular.isDefined($scope.max) ? $scope.max : progressConfig.max;
39024
39025           this.addBar = function(bar, element, attrs) {
39026             if (!animate) {
39027               element.css({'transition': 'none'});
39028             }
39029
39030             this.bars.push(bar);
39031
39032             bar.max = $scope.max;
39033             bar.title = attrs && angular.isDefined(attrs.title) ? attrs.title : 'progressbar';
39034
39035             bar.$watch('value', function(value) {
39036               bar.recalculatePercentage();
39037             });
39038
39039             bar.recalculatePercentage = function() {
39040               var totalPercentage = self.bars.reduce(function(total, bar) {
39041                 bar.percent = +(100 * bar.value / bar.max).toFixed(2);
39042                 return total + bar.percent;
39043               }, 0);
39044
39045               if (totalPercentage > 100) {
39046                 bar.percent -= totalPercentage - 100;
39047               }
39048             };
39049
39050             bar.$on('$destroy', function() {
39051               element = null;
39052               self.removeBar(bar);
39053             });
39054           };
39055
39056           this.removeBar = function(bar) {
39057             this.bars.splice(this.bars.indexOf(bar), 1);
39058             this.bars.forEach(function (bar) {
39059               bar.recalculatePercentage();
39060             });
39061           };
39062
39063           $scope.$watch('max', function(max) {
39064             self.bars.forEach(function(bar) {
39065               bar.max = $scope.max;
39066               bar.recalculatePercentage();
39067             });
39068           });
39069         }])
39070
39071         .directive('uibProgress', function() {
39072           return {
39073             replace: true,
39074             transclude: true,
39075             controller: 'UibProgressController',
39076             require: 'uibProgress',
39077             scope: {
39078               max: '=?'
39079             },
39080             templateUrl: 'uib/template/progressbar/progress.html'
39081           };
39082         })
39083
39084         .directive('uibBar', function() {
39085           return {
39086             replace: true,
39087             transclude: true,
39088             require: '^uibProgress',
39089             scope: {
39090               value: '=',
39091               type: '@'
39092             },
39093             templateUrl: 'uib/template/progressbar/bar.html',
39094             link: function(scope, element, attrs, progressCtrl) {
39095               progressCtrl.addBar(scope, element, attrs);
39096             }
39097           };
39098         })
39099
39100         .directive('uibProgressbar', function() {
39101           return {
39102             replace: true,
39103             transclude: true,
39104             controller: 'UibProgressController',
39105             scope: {
39106               value: '=',
39107               max: '=?',
39108               type: '@'
39109             },
39110             templateUrl: 'uib/template/progressbar/progressbar.html',
39111             link: function(scope, element, attrs, progressCtrl) {
39112               progressCtrl.addBar(scope, angular.element(element.children()[0]), {title: attrs.title});
39113             }
39114           };
39115         });
39116
39117         angular.module('ui.bootstrap.rating', [])
39118
39119         .constant('uibRatingConfig', {
39120           max: 5,
39121           stateOn: null,
39122           stateOff: null,
39123           titles : ['one', 'two', 'three', 'four', 'five']
39124         })
39125
39126         .controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
39127           var ngModelCtrl = { $setViewValue: angular.noop };
39128
39129           this.init = function(ngModelCtrl_) {
39130             ngModelCtrl = ngModelCtrl_;
39131             ngModelCtrl.$render = this.render;
39132
39133             ngModelCtrl.$formatters.push(function(value) {
39134               if (angular.isNumber(value) && value << 0 !== value) {
39135                 value = Math.round(value);
39136               }
39137
39138               return value;
39139             });
39140
39141             this.stateOn = angular.isDefined($attrs.stateOn) ? $scope.$parent.$eval($attrs.stateOn) : ratingConfig.stateOn;
39142             this.stateOff = angular.isDefined($attrs.stateOff) ? $scope.$parent.$eval($attrs.stateOff) : ratingConfig.stateOff;
39143             var tmpTitles = angular.isDefined($attrs.titles) ? $scope.$parent.$eval($attrs.titles) : ratingConfig.titles ;
39144             this.titles = angular.isArray(tmpTitles) && tmpTitles.length > 0 ?
39145               tmpTitles : ratingConfig.titles;
39146
39147             var ratingStates = angular.isDefined($attrs.ratingStates) ?
39148               $scope.$parent.$eval($attrs.ratingStates) :
39149               new Array(angular.isDefined($attrs.max) ? $scope.$parent.$eval($attrs.max) : ratingConfig.max);
39150             $scope.range = this.buildTemplateObjects(ratingStates);
39151           };
39152
39153           this.buildTemplateObjects = function(states) {
39154             for (var i = 0, n = states.length; i < n; i++) {
39155               states[i] = angular.extend({ index: i }, { stateOn: this.stateOn, stateOff: this.stateOff, title: this.getTitle(i) }, states[i]);
39156             }
39157             return states;
39158           };
39159
39160           this.getTitle = function(index) {
39161             if (index >= this.titles.length) {
39162               return index + 1;
39163             }
39164
39165             return this.titles[index];
39166           };
39167
39168           $scope.rate = function(value) {
39169             if (!$scope.readonly && value >= 0 && value <= $scope.range.length) {
39170               ngModelCtrl.$setViewValue(ngModelCtrl.$viewValue === value ? 0 : value);
39171               ngModelCtrl.$render();
39172             }
39173           };
39174
39175           $scope.enter = function(value) {
39176             if (!$scope.readonly) {
39177               $scope.value = value;
39178             }
39179             $scope.onHover({value: value});
39180           };
39181
39182           $scope.reset = function() {
39183             $scope.value = ngModelCtrl.$viewValue;
39184             $scope.onLeave();
39185           };
39186
39187           $scope.onKeydown = function(evt) {
39188             if (/(37|38|39|40)/.test(evt.which)) {
39189               evt.preventDefault();
39190               evt.stopPropagation();
39191               $scope.rate($scope.value + (evt.which === 38 || evt.which === 39 ? 1 : -1));
39192             }
39193           };
39194
39195           this.render = function() {
39196             $scope.value = ngModelCtrl.$viewValue;
39197           };
39198         }])
39199
39200         .directive('uibRating', function() {
39201           return {
39202             require: ['uibRating', 'ngModel'],
39203             scope: {
39204               readonly: '=?',
39205               onHover: '&',
39206               onLeave: '&'
39207             },
39208             controller: 'UibRatingController',
39209             templateUrl: 'uib/template/rating/rating.html',
39210             replace: true,
39211             link: function(scope, element, attrs, ctrls) {
39212               var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39213               ratingCtrl.init(ngModelCtrl);
39214             }
39215           };
39216         });
39217
39218         angular.module('ui.bootstrap.tabs', [])
39219
39220         .controller('UibTabsetController', ['$scope', function ($scope) {
39221           var ctrl = this,
39222               tabs = ctrl.tabs = $scope.tabs = [];
39223
39224           ctrl.select = function(selectedTab) {
39225             angular.forEach(tabs, function(tab) {
39226               if (tab.active && tab !== selectedTab) {
39227                 tab.active = false;
39228                 tab.onDeselect();
39229                 selectedTab.selectCalled = false;
39230               }
39231             });
39232             selectedTab.active = true;
39233             // only call select if it has not already been called
39234             if (!selectedTab.selectCalled) {
39235               selectedTab.onSelect();
39236               selectedTab.selectCalled = true;
39237             }
39238           };
39239
39240           ctrl.addTab = function addTab(tab) {
39241             tabs.push(tab);
39242             // we can't run the select function on the first tab
39243             // since that would select it twice
39244             if (tabs.length === 1 && tab.active !== false) {
39245               tab.active = true;
39246             } else if (tab.active) {
39247               ctrl.select(tab);
39248             } else {
39249               tab.active = false;
39250             }
39251           };
39252
39253           ctrl.removeTab = function removeTab(tab) {
39254             var index = tabs.indexOf(tab);
39255             //Select a new tab if the tab to be removed is selected and not destroyed
39256             if (tab.active && tabs.length > 1 && !destroyed) {
39257               //If this is the last tab, select the previous tab. else, the next tab.
39258               var newActiveIndex = index === tabs.length - 1 ? index - 1 : index + 1;
39259               ctrl.select(tabs[newActiveIndex]);
39260             }
39261             tabs.splice(index, 1);
39262           };
39263
39264           var destroyed;
39265           $scope.$on('$destroy', function() {
39266             destroyed = true;
39267           });
39268         }])
39269
39270         .directive('uibTabset', function() {
39271           return {
39272             transclude: true,
39273             replace: true,
39274             scope: {
39275               type: '@'
39276             },
39277             controller: 'UibTabsetController',
39278             templateUrl: 'uib/template/tabs/tabset.html',
39279             link: function(scope, element, attrs) {
39280               scope.vertical = angular.isDefined(attrs.vertical) ? scope.$parent.$eval(attrs.vertical) : false;
39281               scope.justified = angular.isDefined(attrs.justified) ? scope.$parent.$eval(attrs.justified) : false;
39282             }
39283           };
39284         })
39285
39286         .directive('uibTab', ['$parse', function($parse) {
39287           return {
39288             require: '^uibTabset',
39289             replace: true,
39290             templateUrl: 'uib/template/tabs/tab.html',
39291             transclude: true,
39292             scope: {
39293               active: '=?',
39294               heading: '@',
39295               onSelect: '&select', //This callback is called in contentHeadingTransclude
39296                                   //once it inserts the tab's content into the dom
39297               onDeselect: '&deselect'
39298             },
39299             controller: function() {
39300               //Empty controller so other directives can require being 'under' a tab
39301             },
39302             controllerAs: 'tab',
39303             link: function(scope, elm, attrs, tabsetCtrl, transclude) {
39304               scope.$watch('active', function(active) {
39305                 if (active) {
39306                   tabsetCtrl.select(scope);
39307                 }
39308               });
39309
39310               scope.disabled = false;
39311               if (attrs.disable) {
39312                 scope.$parent.$watch($parse(attrs.disable), function(value) {
39313                   scope.disabled = !! value;
39314                 });
39315               }
39316
39317               scope.select = function() {
39318                 if (!scope.disabled) {
39319                   scope.active = true;
39320                 }
39321               };
39322
39323               tabsetCtrl.addTab(scope);
39324               scope.$on('$destroy', function() {
39325                 tabsetCtrl.removeTab(scope);
39326               });
39327
39328               //We need to transclude later, once the content container is ready.
39329               //when this link happens, we're inside a tab heading.
39330               scope.$transcludeFn = transclude;
39331             }
39332           };
39333         }])
39334
39335         .directive('uibTabHeadingTransclude', function() {
39336           return {
39337             restrict: 'A',
39338             require: '^uibTab',
39339             link: function(scope, elm) {
39340               scope.$watch('headingElement', function updateHeadingElement(heading) {
39341                 if (heading) {
39342                   elm.html('');
39343                   elm.append(heading);
39344                 }
39345               });
39346             }
39347           };
39348         })
39349
39350         .directive('uibTabContentTransclude', function() {
39351           return {
39352             restrict: 'A',
39353             require: '^uibTabset',
39354             link: function(scope, elm, attrs) {
39355               var tab = scope.$eval(attrs.uibTabContentTransclude);
39356
39357               //Now our tab is ready to be transcluded: both the tab heading area
39358               //and the tab content area are loaded.  Transclude 'em both.
39359               tab.$transcludeFn(tab.$parent, function(contents) {
39360                 angular.forEach(contents, function(node) {
39361                   if (isTabHeading(node)) {
39362                     //Let tabHeadingTransclude know.
39363                     tab.headingElement = node;
39364                   } else {
39365                     elm.append(node);
39366                   }
39367                 });
39368               });
39369             }
39370           };
39371
39372           function isTabHeading(node) {
39373             return node.tagName && (
39374               node.hasAttribute('uib-tab-heading') ||
39375               node.hasAttribute('data-uib-tab-heading') ||
39376               node.hasAttribute('x-uib-tab-heading') ||
39377               node.tagName.toLowerCase() === 'uib-tab-heading' ||
39378               node.tagName.toLowerCase() === 'data-uib-tab-heading' ||
39379               node.tagName.toLowerCase() === 'x-uib-tab-heading'
39380             );
39381           }
39382         });
39383
39384         angular.module('ui.bootstrap.timepicker', [])
39385
39386         .constant('uibTimepickerConfig', {
39387           hourStep: 1,
39388           minuteStep: 1,
39389           secondStep: 1,
39390           showMeridian: true,
39391           showSeconds: false,
39392           meridians: null,
39393           readonlyInput: false,
39394           mousewheel: true,
39395           arrowkeys: true,
39396           showSpinners: true
39397         })
39398
39399         .controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
39400           var selected = new Date(),
39401               ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
39402               meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS;
39403
39404           $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0;
39405           $element.removeAttr('tabindex');
39406
39407           this.init = function(ngModelCtrl_, inputs) {
39408             ngModelCtrl = ngModelCtrl_;
39409             ngModelCtrl.$render = this.render;
39410
39411             ngModelCtrl.$formatters.unshift(function(modelValue) {
39412               return modelValue ? new Date(modelValue) : null;
39413             });
39414
39415             var hoursInputEl = inputs.eq(0),
39416                 minutesInputEl = inputs.eq(1),
39417                 secondsInputEl = inputs.eq(2);
39418
39419             var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
39420
39421             if (mousewheel) {
39422               this.setupMousewheelEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39423             }
39424
39425             var arrowkeys = angular.isDefined($attrs.arrowkeys) ? $scope.$parent.$eval($attrs.arrowkeys) : timepickerConfig.arrowkeys;
39426             if (arrowkeys) {
39427               this.setupArrowkeyEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39428             }
39429
39430             $scope.readonlyInput = angular.isDefined($attrs.readonlyInput) ? $scope.$parent.$eval($attrs.readonlyInput) : timepickerConfig.readonlyInput;
39431             this.setupInputEvents(hoursInputEl, minutesInputEl, secondsInputEl);
39432           };
39433
39434           var hourStep = timepickerConfig.hourStep;
39435           if ($attrs.hourStep) {
39436             $scope.$parent.$watch($parse($attrs.hourStep), function(value) {
39437               hourStep = parseInt(value, 10);
39438             });
39439           }
39440
39441           var minuteStep = timepickerConfig.minuteStep;
39442           if ($attrs.minuteStep) {
39443             $scope.$parent.$watch($parse($attrs.minuteStep), function(value) {
39444               minuteStep = parseInt(value, 10);
39445             });
39446           }
39447
39448           var min;
39449           $scope.$parent.$watch($parse($attrs.min), function(value) {
39450             var dt = new Date(value);
39451             min = isNaN(dt) ? undefined : dt;
39452           });
39453
39454           var max;
39455           $scope.$parent.$watch($parse($attrs.max), function(value) {
39456             var dt = new Date(value);
39457             max = isNaN(dt) ? undefined : dt;
39458           });
39459
39460           var disabled = false;
39461           if ($attrs.ngDisabled) {
39462             $scope.$parent.$watch($parse($attrs.ngDisabled), function(value) {
39463               disabled = value;
39464             });
39465           }
39466
39467           $scope.noIncrementHours = function() {
39468             var incrementedSelected = addMinutes(selected, hourStep * 60);
39469             return disabled || incrementedSelected > max ||
39470               incrementedSelected < selected && incrementedSelected < min;
39471           };
39472
39473           $scope.noDecrementHours = function() {
39474             var decrementedSelected = addMinutes(selected, -hourStep * 60);
39475             return disabled || decrementedSelected < min ||
39476               decrementedSelected > selected && decrementedSelected > max;
39477           };
39478
39479           $scope.noIncrementMinutes = function() {
39480             var incrementedSelected = addMinutes(selected, minuteStep);
39481             return disabled || incrementedSelected > max ||
39482               incrementedSelected < selected && incrementedSelected < min;
39483           };
39484
39485           $scope.noDecrementMinutes = function() {
39486             var decrementedSelected = addMinutes(selected, -minuteStep);
39487             return disabled || decrementedSelected < min ||
39488               decrementedSelected > selected && decrementedSelected > max;
39489           };
39490
39491           $scope.noIncrementSeconds = function() {
39492             var incrementedSelected = addSeconds(selected, secondStep);
39493             return disabled || incrementedSelected > max ||
39494               incrementedSelected < selected && incrementedSelected < min;
39495           };
39496
39497           $scope.noDecrementSeconds = function() {
39498             var decrementedSelected = addSeconds(selected, -secondStep);
39499             return disabled || decrementedSelected < min ||
39500               decrementedSelected > selected && decrementedSelected > max;
39501           };
39502
39503           $scope.noToggleMeridian = function() {
39504             if (selected.getHours() < 12) {
39505               return disabled || addMinutes(selected, 12 * 60) > max;
39506             }
39507
39508             return disabled || addMinutes(selected, -12 * 60) < min;
39509           };
39510
39511           var secondStep = timepickerConfig.secondStep;
39512           if ($attrs.secondStep) {
39513             $scope.$parent.$watch($parse($attrs.secondStep), function(value) {
39514               secondStep = parseInt(value, 10);
39515             });
39516           }
39517
39518           $scope.showSeconds = timepickerConfig.showSeconds;
39519           if ($attrs.showSeconds) {
39520             $scope.$parent.$watch($parse($attrs.showSeconds), function(value) {
39521               $scope.showSeconds = !!value;
39522             });
39523           }
39524
39525           // 12H / 24H mode
39526           $scope.showMeridian = timepickerConfig.showMeridian;
39527           if ($attrs.showMeridian) {
39528             $scope.$parent.$watch($parse($attrs.showMeridian), function(value) {
39529               $scope.showMeridian = !!value;
39530
39531               if (ngModelCtrl.$error.time) {
39532                 // Evaluate from template
39533                 var hours = getHoursFromTemplate(), minutes = getMinutesFromTemplate();
39534                 if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39535                   selected.setHours(hours);
39536                   refresh();
39537                 }
39538               } else {
39539                 updateTemplate();
39540               }
39541             });
39542           }
39543
39544           // Get $scope.hours in 24H mode if valid
39545           function getHoursFromTemplate() {
39546             var hours = parseInt($scope.hours, 10);
39547             var valid = $scope.showMeridian ? hours > 0 && hours < 13 :
39548               hours >= 0 && hours < 24;
39549             if (!valid) {
39550               return undefined;
39551             }
39552
39553             if ($scope.showMeridian) {
39554               if (hours === 12) {
39555                 hours = 0;
39556               }
39557               if ($scope.meridian === meridians[1]) {
39558                 hours = hours + 12;
39559               }
39560             }
39561             return hours;
39562           }
39563
39564           function getMinutesFromTemplate() {
39565             var minutes = parseInt($scope.minutes, 10);
39566             return minutes >= 0 && minutes < 60 ? minutes : undefined;
39567           }
39568
39569           function getSecondsFromTemplate() {
39570             var seconds = parseInt($scope.seconds, 10);
39571             return seconds >= 0 && seconds < 60 ? seconds : undefined;
39572           }
39573
39574           function pad(value) {
39575             if (value === null) {
39576               return '';
39577             }
39578
39579             return angular.isDefined(value) && value.toString().length < 2 ?
39580               '0' + value : value.toString();
39581           }
39582
39583           // Respond on mousewheel spin
39584           this.setupMousewheelEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39585             var isScrollingUp = function(e) {
39586               if (e.originalEvent) {
39587                 e = e.originalEvent;
39588               }
39589               //pick correct delta variable depending on event
39590               var delta = e.wheelDelta ? e.wheelDelta : -e.deltaY;
39591               return e.detail || delta > 0;
39592             };
39593
39594             hoursInputEl.bind('mousewheel wheel', function(e) {
39595               if (!disabled) {
39596                 $scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
39597               }
39598               e.preventDefault();
39599             });
39600
39601             minutesInputEl.bind('mousewheel wheel', function(e) {
39602               if (!disabled) {
39603                 $scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
39604               }
39605               e.preventDefault();
39606             });
39607
39608              secondsInputEl.bind('mousewheel wheel', function(e) {
39609               if (!disabled) {
39610                 $scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
39611               }
39612               e.preventDefault();
39613             });
39614           };
39615
39616           // Respond on up/down arrowkeys
39617           this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39618             hoursInputEl.bind('keydown', function(e) {
39619               if (!disabled) {
39620                 if (e.which === 38) { // up
39621                   e.preventDefault();
39622                   $scope.incrementHours();
39623                   $scope.$apply();
39624                 } else if (e.which === 40) { // down
39625                   e.preventDefault();
39626                   $scope.decrementHours();
39627                   $scope.$apply();
39628                 }
39629               }
39630             });
39631
39632             minutesInputEl.bind('keydown', function(e) {
39633               if (!disabled) {
39634                 if (e.which === 38) { // up
39635                   e.preventDefault();
39636                   $scope.incrementMinutes();
39637                   $scope.$apply();
39638                 } else if (e.which === 40) { // down
39639                   e.preventDefault();
39640                   $scope.decrementMinutes();
39641                   $scope.$apply();
39642                 }
39643               }
39644             });
39645
39646             secondsInputEl.bind('keydown', function(e) {
39647               if (!disabled) {
39648                 if (e.which === 38) { // up
39649                   e.preventDefault();
39650                   $scope.incrementSeconds();
39651                   $scope.$apply();
39652                 } else if (e.which === 40) { // down
39653                   e.preventDefault();
39654                   $scope.decrementSeconds();
39655                   $scope.$apply();
39656                 }
39657               }
39658             });
39659           };
39660
39661           this.setupInputEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
39662             if ($scope.readonlyInput) {
39663               $scope.updateHours = angular.noop;
39664               $scope.updateMinutes = angular.noop;
39665               $scope.updateSeconds = angular.noop;
39666               return;
39667             }
39668
39669             var invalidate = function(invalidHours, invalidMinutes, invalidSeconds) {
39670               ngModelCtrl.$setViewValue(null);
39671               ngModelCtrl.$setValidity('time', false);
39672               if (angular.isDefined(invalidHours)) {
39673                 $scope.invalidHours = invalidHours;
39674               }
39675
39676               if (angular.isDefined(invalidMinutes)) {
39677                 $scope.invalidMinutes = invalidMinutes;
39678               }
39679
39680               if (angular.isDefined(invalidSeconds)) {
39681                 $scope.invalidSeconds = invalidSeconds;
39682               }
39683             };
39684
39685             $scope.updateHours = function() {
39686               var hours = getHoursFromTemplate(),
39687                 minutes = getMinutesFromTemplate();
39688
39689               ngModelCtrl.$setDirty();
39690
39691               if (angular.isDefined(hours) && angular.isDefined(minutes)) {
39692                 selected.setHours(hours);
39693                 selected.setMinutes(minutes);
39694                 if (selected < min || selected > max) {
39695                   invalidate(true);
39696                 } else {
39697                   refresh('h');
39698                 }
39699               } else {
39700                 invalidate(true);
39701               }
39702             };
39703
39704             hoursInputEl.bind('blur', function(e) {
39705               ngModelCtrl.$setTouched();
39706               if ($scope.hours === null || $scope.hours === '') {
39707                 invalidate(true);
39708               } else if (!$scope.invalidHours && $scope.hours < 10) {
39709                 $scope.$apply(function() {
39710                   $scope.hours = pad($scope.hours);
39711                 });
39712               }
39713             });
39714
39715             $scope.updateMinutes = function() {
39716               var minutes = getMinutesFromTemplate(),
39717                 hours = getHoursFromTemplate();
39718
39719               ngModelCtrl.$setDirty();
39720
39721               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39722                 selected.setHours(hours);
39723                 selected.setMinutes(minutes);
39724                 if (selected < min || selected > max) {
39725                   invalidate(undefined, true);
39726                 } else {
39727                   refresh('m');
39728                 }
39729               } else {
39730                 invalidate(undefined, true);
39731               }
39732             };
39733
39734             minutesInputEl.bind('blur', function(e) {
39735               ngModelCtrl.$setTouched();
39736               if ($scope.minutes === null) {
39737                 invalidate(undefined, true);
39738               } else if (!$scope.invalidMinutes && $scope.minutes < 10) {
39739                 $scope.$apply(function() {
39740                   $scope.minutes = pad($scope.minutes);
39741                 });
39742               }
39743             });
39744
39745             $scope.updateSeconds = function() {
39746               var seconds = getSecondsFromTemplate();
39747
39748               ngModelCtrl.$setDirty();
39749
39750               if (angular.isDefined(seconds)) {
39751                 selected.setSeconds(seconds);
39752                 refresh('s');
39753               } else {
39754                 invalidate(undefined, undefined, true);
39755               }
39756             };
39757
39758             secondsInputEl.bind('blur', function(e) {
39759               if (!$scope.invalidSeconds && $scope.seconds < 10) {
39760                 $scope.$apply( function() {
39761                   $scope.seconds = pad($scope.seconds);
39762                 });
39763               }
39764             });
39765
39766           };
39767
39768           this.render = function() {
39769             var date = ngModelCtrl.$viewValue;
39770
39771             if (isNaN(date)) {
39772               ngModelCtrl.$setValidity('time', false);
39773               $log.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.');
39774             } else {
39775               if (date) {
39776                 selected = date;
39777               }
39778
39779               if (selected < min || selected > max) {
39780                 ngModelCtrl.$setValidity('time', false);
39781                 $scope.invalidHours = true;
39782                 $scope.invalidMinutes = true;
39783               } else {
39784                 makeValid();
39785               }
39786               updateTemplate();
39787             }
39788           };
39789
39790           // Call internally when we know that model is valid.
39791           function refresh(keyboardChange) {
39792             makeValid();
39793             ngModelCtrl.$setViewValue(new Date(selected));
39794             updateTemplate(keyboardChange);
39795           }
39796
39797           function makeValid() {
39798             ngModelCtrl.$setValidity('time', true);
39799             $scope.invalidHours = false;
39800             $scope.invalidMinutes = false;
39801             $scope.invalidSeconds = false;
39802           }
39803
39804           function updateTemplate(keyboardChange) {
39805             if (!ngModelCtrl.$modelValue) {
39806               $scope.hours = null;
39807               $scope.minutes = null;
39808               $scope.seconds = null;
39809               $scope.meridian = meridians[0];
39810             } else {
39811               var hours = selected.getHours(),
39812                 minutes = selected.getMinutes(),
39813                 seconds = selected.getSeconds();
39814
39815               if ($scope.showMeridian) {
39816                 hours = hours === 0 || hours === 12 ? 12 : hours % 12; // Convert 24 to 12 hour system
39817               }
39818
39819               $scope.hours = keyboardChange === 'h' ? hours : pad(hours);
39820               if (keyboardChange !== 'm') {
39821                 $scope.minutes = pad(minutes);
39822               }
39823               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39824
39825               if (keyboardChange !== 's') {
39826                 $scope.seconds = pad(seconds);
39827               }
39828               $scope.meridian = selected.getHours() < 12 ? meridians[0] : meridians[1];
39829             }
39830           }
39831
39832           function addSecondsToSelected(seconds) {
39833             selected = addSeconds(selected, seconds);
39834             refresh();
39835           }
39836
39837           function addMinutes(selected, minutes) {
39838             return addSeconds(selected, minutes*60);
39839           }
39840
39841           function addSeconds(date, seconds) {
39842             var dt = new Date(date.getTime() + seconds * 1000);
39843             var newDate = new Date(date);
39844             newDate.setHours(dt.getHours(), dt.getMinutes(), dt.getSeconds());
39845             return newDate;
39846           }
39847
39848           $scope.showSpinners = angular.isDefined($attrs.showSpinners) ?
39849             $scope.$parent.$eval($attrs.showSpinners) : timepickerConfig.showSpinners;
39850
39851           $scope.incrementHours = function() {
39852             if (!$scope.noIncrementHours()) {
39853               addSecondsToSelected(hourStep * 60 * 60);
39854             }
39855           };
39856
39857           $scope.decrementHours = function() {
39858             if (!$scope.noDecrementHours()) {
39859               addSecondsToSelected(-hourStep * 60 * 60);
39860             }
39861           };
39862
39863           $scope.incrementMinutes = function() {
39864             if (!$scope.noIncrementMinutes()) {
39865               addSecondsToSelected(minuteStep * 60);
39866             }
39867           };
39868
39869           $scope.decrementMinutes = function() {
39870             if (!$scope.noDecrementMinutes()) {
39871               addSecondsToSelected(-minuteStep * 60);
39872             }
39873           };
39874
39875           $scope.incrementSeconds = function() {
39876             if (!$scope.noIncrementSeconds()) {
39877               addSecondsToSelected(secondStep);
39878             }
39879           };
39880
39881           $scope.decrementSeconds = function() {
39882             if (!$scope.noDecrementSeconds()) {
39883               addSecondsToSelected(-secondStep);
39884             }
39885           };
39886
39887           $scope.toggleMeridian = function() {
39888             var minutes = getMinutesFromTemplate(),
39889                 hours = getHoursFromTemplate();
39890
39891             if (!$scope.noToggleMeridian()) {
39892               if (angular.isDefined(minutes) && angular.isDefined(hours)) {
39893                 addSecondsToSelected(12 * 60 * (selected.getHours() < 12 ? 60 : -60));
39894               } else {
39895                 $scope.meridian = $scope.meridian === meridians[0] ? meridians[1] : meridians[0];
39896               }
39897             }
39898           };
39899
39900           $scope.blur = function() {
39901             ngModelCtrl.$setTouched();
39902           };
39903         }])
39904
39905         .directive('uibTimepicker', function() {
39906           return {
39907             require: ['uibTimepicker', '?^ngModel'],
39908             controller: 'UibTimepickerController',
39909             controllerAs: 'timepicker',
39910             replace: true,
39911             scope: {},
39912             templateUrl: function(element, attrs) {
39913               return attrs.templateUrl || 'uib/template/timepicker/timepicker.html';
39914             },
39915             link: function(scope, element, attrs, ctrls) {
39916               var timepickerCtrl = ctrls[0], ngModelCtrl = ctrls[1];
39917
39918               if (ngModelCtrl) {
39919                 timepickerCtrl.init(ngModelCtrl, element.find('input'));
39920               }
39921             }
39922           };
39923         });
39924
39925         angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap.position'])
39926
39927         /**
39928          * A helper service that can parse typeahead's syntax (string provided by users)
39929          * Extracted to a separate service for ease of unit testing
39930          */
39931           .factory('uibTypeaheadParser', ['$parse', function($parse) {
39932             //                      00000111000000000000022200000000000000003333333333333330000000000044000
39933             var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
39934             return {
39935               parse: function(input) {
39936                 var match = input.match(TYPEAHEAD_REGEXP);
39937                 if (!match) {
39938                   throw new Error(
39939                     'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
39940                       ' but got "' + input + '".');
39941                 }
39942
39943                 return {
39944                   itemName: match[3],
39945                   source: $parse(match[4]),
39946                   viewMapper: $parse(match[2] || match[1]),
39947                   modelMapper: $parse(match[1])
39948                 };
39949               }
39950             };
39951           }])
39952
39953           .controller('UibTypeaheadController', ['$scope', '$element', '$attrs', '$compile', '$parse', '$q', '$timeout', '$document', '$window', '$rootScope', '$$debounce', '$uibPosition', 'uibTypeaheadParser',
39954             function(originalScope, element, attrs, $compile, $parse, $q, $timeout, $document, $window, $rootScope, $$debounce, $position, typeaheadParser) {
39955             var HOT_KEYS = [9, 13, 27, 38, 40];
39956             var eventDebounceTime = 200;
39957             var modelCtrl, ngModelOptions;
39958             //SUPPORTED ATTRIBUTES (OPTIONS)
39959
39960             //minimal no of characters that needs to be entered before typeahead kicks-in
39961             var minLength = originalScope.$eval(attrs.typeaheadMinLength);
39962             if (!minLength && minLength !== 0) {
39963               minLength = 1;
39964             }
39965
39966             //minimal wait time after last character typed before typeahead kicks-in
39967             var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
39968
39969             //should it restrict model values to the ones selected from the popup only?
39970             var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
39971             originalScope.$watch(attrs.typeaheadEditable, function (newVal) {
39972               isEditable = newVal !== false;
39973             });
39974
39975             //binding to a variable that indicates if matches are being retrieved asynchronously
39976             var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
39977
39978             //a callback executed when a match is selected
39979             var onSelectCallback = $parse(attrs.typeaheadOnSelect);
39980
39981             //should it select highlighted popup value when losing focus?
39982             var isSelectOnBlur = angular.isDefined(attrs.typeaheadSelectOnBlur) ? originalScope.$eval(attrs.typeaheadSelectOnBlur) : false;
39983
39984             //binding to a variable that indicates if there were no results after the query is completed
39985             var isNoResultsSetter = $parse(attrs.typeaheadNoResults).assign || angular.noop;
39986
39987             var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined;
39988
39989             var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false;
39990
39991             var appendTo = attrs.typeaheadAppendTo ?
39992               originalScope.$eval(attrs.typeaheadAppendTo) : null;
39993
39994             var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false;
39995
39996             //If input matches an item of the list exactly, select it automatically
39997             var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false;
39998
39999             //binding to a variable that indicates if dropdown is open
40000             var isOpenSetter = $parse(attrs.typeaheadIsOpen).assign || angular.noop;
40001
40002             var showHint = originalScope.$eval(attrs.typeaheadShowHint) || false;
40003
40004             //INTERNAL VARIABLES
40005
40006             //model setter executed upon match selection
40007             var parsedModel = $parse(attrs.ngModel);
40008             var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
40009             var $setModelValue = function(scope, newValue) {
40010               if (angular.isFunction(parsedModel(originalScope)) &&
40011                 ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
40012                 return invokeModelSetter(scope, {$$$p: newValue});
40013               }
40014
40015               return parsedModel.assign(scope, newValue);
40016             };
40017
40018             //expressions used by typeahead
40019             var parserResult = typeaheadParser.parse(attrs.uibTypeahead);
40020
40021             var hasFocus;
40022
40023             //Used to avoid bug in iOS webview where iOS keyboard does not fire
40024             //mousedown & mouseup events
40025             //Issue #3699
40026             var selected;
40027
40028             //create a child scope for the typeahead directive so we are not polluting original scope
40029             //with typeahead-specific data (matches, query etc.)
40030             var scope = originalScope.$new();
40031             var offDestroy = originalScope.$on('$destroy', function() {
40032               scope.$destroy();
40033             });
40034             scope.$on('$destroy', offDestroy);
40035
40036             // WAI-ARIA
40037             var popupId = 'typeahead-' + scope.$id + '-' + Math.floor(Math.random() * 10000);
40038             element.attr({
40039               'aria-autocomplete': 'list',
40040               'aria-expanded': false,
40041               'aria-owns': popupId
40042             });
40043
40044             var inputsContainer, hintInputElem;
40045             //add read-only input to show hint
40046             if (showHint) {
40047               inputsContainer = angular.element('<div></div>');
40048               inputsContainer.css('position', 'relative');
40049               element.after(inputsContainer);
40050               hintInputElem = element.clone();
40051               hintInputElem.attr('placeholder', '');
40052               hintInputElem.val('');
40053               hintInputElem.css({
40054                 'position': 'absolute',
40055                 'top': '0px',
40056                 'left': '0px',
40057                 'border-color': 'transparent',
40058                 'box-shadow': 'none',
40059                 'opacity': 1,
40060                 'background': 'none 0% 0% / auto repeat scroll padding-box border-box rgb(255, 255, 255)',
40061                 'color': '#999'
40062               });
40063               element.css({
40064                 'position': 'relative',
40065                 'vertical-align': 'top',
40066                 'background-color': 'transparent'
40067               });
40068               inputsContainer.append(hintInputElem);
40069               hintInputElem.after(element);
40070             }
40071
40072             //pop-up element used to display matches
40073             var popUpEl = angular.element('<div uib-typeahead-popup></div>');
40074             popUpEl.attr({
40075               id: popupId,
40076               matches: 'matches',
40077               active: 'activeIdx',
40078               select: 'select(activeIdx, evt)',
40079               'move-in-progress': 'moveInProgress',
40080               query: 'query',
40081               position: 'position',
40082               'assign-is-open': 'assignIsOpen(isOpen)',
40083               debounce: 'debounceUpdate'
40084             });
40085             //custom item template
40086             if (angular.isDefined(attrs.typeaheadTemplateUrl)) {
40087               popUpEl.attr('template-url', attrs.typeaheadTemplateUrl);
40088             }
40089
40090             if (angular.isDefined(attrs.typeaheadPopupTemplateUrl)) {
40091               popUpEl.attr('popup-template-url', attrs.typeaheadPopupTemplateUrl);
40092             }
40093
40094             var resetHint = function() {
40095               if (showHint) {
40096                 hintInputElem.val('');
40097               }
40098             };
40099
40100             var resetMatches = function() {
40101               scope.matches = [];
40102               scope.activeIdx = -1;
40103               element.attr('aria-expanded', false);
40104               resetHint();
40105             };
40106
40107             var getMatchId = function(index) {
40108               return popupId + '-option-' + index;
40109             };
40110
40111             // Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
40112             // This attribute is added or removed automatically when the `activeIdx` changes.
40113             scope.$watch('activeIdx', function(index) {
40114               if (index < 0) {
40115                 element.removeAttr('aria-activedescendant');
40116               } else {
40117                 element.attr('aria-activedescendant', getMatchId(index));
40118               }
40119             });
40120
40121             var inputIsExactMatch = function(inputValue, index) {
40122               if (scope.matches.length > index && inputValue) {
40123                 return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase();
40124               }
40125
40126               return false;
40127             };
40128
40129             var getMatchesAsync = function(inputValue, evt) {
40130               var locals = {$viewValue: inputValue};
40131               isLoadingSetter(originalScope, true);
40132               isNoResultsSetter(originalScope, false);
40133               $q.when(parserResult.source(originalScope, locals)).then(function(matches) {
40134                 //it might happen that several async queries were in progress if a user were typing fast
40135                 //but we are interested only in responses that correspond to the current view value
40136                 var onCurrentRequest = inputValue === modelCtrl.$viewValue;
40137                 if (onCurrentRequest && hasFocus) {
40138                   if (matches && matches.length > 0) {
40139                     scope.activeIdx = focusFirst ? 0 : -1;
40140                     isNoResultsSetter(originalScope, false);
40141                     scope.matches.length = 0;
40142
40143                     //transform labels
40144                     for (var i = 0; i < matches.length; i++) {
40145                       locals[parserResult.itemName] = matches[i];
40146                       scope.matches.push({
40147                         id: getMatchId(i),
40148                         label: parserResult.viewMapper(scope, locals),
40149                         model: matches[i]
40150                       });
40151                     }
40152
40153                     scope.query = inputValue;
40154                     //position pop-up with matches - we need to re-calculate its position each time we are opening a window
40155                     //with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
40156                     //due to other elements being rendered
40157                     recalculatePosition();
40158
40159                     element.attr('aria-expanded', true);
40160
40161                     //Select the single remaining option if user input matches
40162                     if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
40163                       if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40164                         $$debounce(function() {
40165                           scope.select(0, evt);
40166                         }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40167                       } else {
40168                         scope.select(0, evt);
40169                       }
40170                     }
40171
40172                     if (showHint) {
40173                       var firstLabel = scope.matches[0].label;
40174                       if (inputValue.length > 0 && firstLabel.slice(0, inputValue.length).toUpperCase() === inputValue.toUpperCase()) {
40175                         hintInputElem.val(inputValue + firstLabel.slice(inputValue.length));
40176                       }
40177                       else {
40178                         hintInputElem.val('');
40179                       }
40180                     }
40181                   } else {
40182                     resetMatches();
40183                     isNoResultsSetter(originalScope, true);
40184                   }
40185                 }
40186                 if (onCurrentRequest) {
40187                   isLoadingSetter(originalScope, false);
40188                 }
40189               }, function() {
40190                 resetMatches();
40191                 isLoadingSetter(originalScope, false);
40192                 isNoResultsSetter(originalScope, true);
40193               });
40194             };
40195
40196             // bind events only if appendToBody params exist - performance feature
40197             if (appendToBody) {
40198               angular.element($window).on('resize', fireRecalculating);
40199               $document.find('body').on('scroll', fireRecalculating);
40200             }
40201
40202             // Declare the debounced function outside recalculating for
40203             // proper debouncing
40204             var debouncedRecalculate = $$debounce(function() {
40205               // if popup is visible
40206               if (scope.matches.length) {
40207                 recalculatePosition();
40208               }
40209
40210               scope.moveInProgress = false;
40211             }, eventDebounceTime);
40212
40213             // Default progress type
40214             scope.moveInProgress = false;
40215
40216             function fireRecalculating() {
40217               if (!scope.moveInProgress) {
40218                 scope.moveInProgress = true;
40219                 scope.$digest();
40220               }
40221
40222               debouncedRecalculate();
40223             }
40224
40225             // recalculate actual position and set new values to scope
40226             // after digest loop is popup in right position
40227             function recalculatePosition() {
40228               scope.position = appendToBody ? $position.offset(element) : $position.position(element);
40229               scope.position.top += element.prop('offsetHeight');
40230             }
40231
40232             //we need to propagate user's query so we can higlight matches
40233             scope.query = undefined;
40234
40235             //Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
40236             var timeoutPromise;
40237
40238             var scheduleSearchWithTimeout = function(inputValue) {
40239               timeoutPromise = $timeout(function() {
40240                 getMatchesAsync(inputValue);
40241               }, waitTime);
40242             };
40243
40244             var cancelPreviousTimeout = function() {
40245               if (timeoutPromise) {
40246                 $timeout.cancel(timeoutPromise);
40247               }
40248             };
40249
40250             resetMatches();
40251
40252             scope.assignIsOpen = function (isOpen) {
40253               isOpenSetter(originalScope, isOpen);
40254             };
40255
40256             scope.select = function(activeIdx, evt) {
40257               //called from within the $digest() cycle
40258               var locals = {};
40259               var model, item;
40260
40261               selected = true;
40262               locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
40263               model = parserResult.modelMapper(originalScope, locals);
40264               $setModelValue(originalScope, model);
40265               modelCtrl.$setValidity('editable', true);
40266               modelCtrl.$setValidity('parse', true);
40267
40268               onSelectCallback(originalScope, {
40269                 $item: item,
40270                 $model: model,
40271                 $label: parserResult.viewMapper(originalScope, locals),
40272                 $event: evt
40273               });
40274
40275               resetMatches();
40276
40277               //return focus to the input element if a match was selected via a mouse click event
40278               // use timeout to avoid $rootScope:inprog error
40279               if (scope.$eval(attrs.typeaheadFocusOnSelect) !== false) {
40280                 $timeout(function() { element[0].focus(); }, 0, false);
40281               }
40282             };
40283
40284             //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
40285             element.on('keydown', function(evt) {
40286               //typeahead is open and an "interesting" key was pressed
40287               if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
40288                 return;
40289               }
40290
40291               // if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
40292               if (scope.activeIdx === -1 && (evt.which === 9 || evt.which === 13)) {
40293                 resetMatches();
40294                 scope.$digest();
40295                 return;
40296               }
40297
40298               evt.preventDefault();
40299
40300               switch (evt.which) {
40301                 case 9:
40302                 case 13:
40303                   scope.$apply(function () {
40304                     if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
40305                       $$debounce(function() {
40306                         scope.select(scope.activeIdx, evt);
40307                       }, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
40308                     } else {
40309                       scope.select(scope.activeIdx, evt);
40310                     }
40311                   });
40312                   break;
40313                 case 27:
40314                   evt.stopPropagation();
40315
40316                   resetMatches();
40317                   scope.$digest();
40318                   break;
40319                 case 38:
40320                   scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
40321                   scope.$digest();
40322                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40323                   break;
40324                 case 40:
40325                   scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
40326                   scope.$digest();
40327                   popUpEl.find('li')[scope.activeIdx].scrollIntoView(false);
40328                   break;
40329               }
40330             });
40331
40332             element.bind('focus', function (evt) {
40333               hasFocus = true;
40334               if (minLength === 0 && !modelCtrl.$viewValue) {
40335                 $timeout(function() {
40336                   getMatchesAsync(modelCtrl.$viewValue, evt);
40337                 }, 0);
40338               }
40339             });
40340
40341             element.bind('blur', function(evt) {
40342               if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
40343                 selected = true;
40344                 scope.$apply(function() {
40345                   if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
40346                     $$debounce(function() {
40347                       scope.select(scope.activeIdx, evt);
40348                     }, scope.debounceUpdate.blur);
40349                   } else {
40350                     scope.select(scope.activeIdx, evt);
40351                   }
40352                 });
40353               }
40354               if (!isEditable && modelCtrl.$error.editable) {
40355                 modelCtrl.$viewValue = '';
40356                 element.val('');
40357               }
40358               hasFocus = false;
40359               selected = false;
40360             });
40361
40362             // Keep reference to click handler to unbind it.
40363             var dismissClickHandler = function(evt) {
40364               // Issue #3973
40365               // Firefox treats right click as a click on document
40366               if (element[0] !== evt.target && evt.which !== 3 && scope.matches.length !== 0) {
40367                 resetMatches();
40368                 if (!$rootScope.$$phase) {
40369                   scope.$digest();
40370                 }
40371               }
40372             };
40373
40374             $document.on('click', dismissClickHandler);
40375
40376             originalScope.$on('$destroy', function() {
40377               $document.off('click', dismissClickHandler);
40378               if (appendToBody || appendTo) {
40379                 $popup.remove();
40380               }
40381
40382               if (appendToBody) {
40383                 angular.element($window).off('resize', fireRecalculating);
40384                 $document.find('body').off('scroll', fireRecalculating);
40385               }
40386               // Prevent jQuery cache memory leak
40387               popUpEl.remove();
40388
40389               if (showHint) {
40390                   inputsContainer.remove();
40391               }
40392             });
40393
40394             var $popup = $compile(popUpEl)(scope);
40395
40396             if (appendToBody) {
40397               $document.find('body').append($popup);
40398             } else if (appendTo) {
40399               angular.element(appendTo).eq(0).append($popup);
40400             } else {
40401               element.after($popup);
40402             }
40403
40404             this.init = function(_modelCtrl, _ngModelOptions) {
40405               modelCtrl = _modelCtrl;
40406               ngModelOptions = _ngModelOptions;
40407
40408               scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
40409
40410               //plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
40411               //$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
40412               modelCtrl.$parsers.unshift(function(inputValue) {
40413                 hasFocus = true;
40414
40415                 if (minLength === 0 || inputValue && inputValue.length >= minLength) {
40416                   if (waitTime > 0) {
40417                     cancelPreviousTimeout();
40418                     scheduleSearchWithTimeout(inputValue);
40419                   } else {
40420                     getMatchesAsync(inputValue);
40421                   }
40422                 } else {
40423                   isLoadingSetter(originalScope, false);
40424                   cancelPreviousTimeout();
40425                   resetMatches();
40426                 }
40427
40428                 if (isEditable) {
40429                   return inputValue;
40430                 }
40431
40432                 if (!inputValue) {
40433                   // Reset in case user had typed something previously.
40434                   modelCtrl.$setValidity('editable', true);
40435                   return null;
40436                 }
40437
40438                 modelCtrl.$setValidity('editable', false);
40439                 return undefined;
40440               });
40441
40442               modelCtrl.$formatters.push(function(modelValue) {
40443                 var candidateViewValue, emptyViewValue;
40444                 var locals = {};
40445
40446                 // The validity may be set to false via $parsers (see above) if
40447                 // the model is restricted to selected values. If the model
40448                 // is set manually it is considered to be valid.
40449                 if (!isEditable) {
40450                   modelCtrl.$setValidity('editable', true);
40451                 }
40452
40453                 if (inputFormatter) {
40454                   locals.$model = modelValue;
40455                   return inputFormatter(originalScope, locals);
40456                 }
40457
40458                 //it might happen that we don't have enough info to properly render input value
40459                 //we need to check for this situation and simply return model value if we can't apply custom formatting
40460                 locals[parserResult.itemName] = modelValue;
40461                 candidateViewValue = parserResult.viewMapper(originalScope, locals);
40462                 locals[parserResult.itemName] = undefined;
40463                 emptyViewValue = parserResult.viewMapper(originalScope, locals);
40464
40465                 return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
40466               });
40467             };
40468           }])
40469
40470           .directive('uibTypeahead', function() {
40471             return {
40472               controller: 'UibTypeaheadController',
40473               require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
40474               link: function(originalScope, element, attrs, ctrls) {
40475                 ctrls[2].init(ctrls[0], ctrls[1]);
40476               }
40477             };
40478           })
40479
40480           .directive('uibTypeaheadPopup', ['$$debounce', function($$debounce) {
40481             return {
40482               scope: {
40483                 matches: '=',
40484                 query: '=',
40485                 active: '=',
40486                 position: '&',
40487                 moveInProgress: '=',
40488                 select: '&',
40489                 assignIsOpen: '&',
40490                 debounce: '&'
40491               },
40492               replace: true,
40493               templateUrl: function(element, attrs) {
40494                 return attrs.popupTemplateUrl || 'uib/template/typeahead/typeahead-popup.html';
40495               },
40496               link: function(scope, element, attrs) {
40497                 scope.templateUrl = attrs.templateUrl;
40498
40499                 scope.isOpen = function() {
40500                   var isDropdownOpen = scope.matches.length > 0;
40501                   scope.assignIsOpen({ isOpen: isDropdownOpen });
40502                   return isDropdownOpen;
40503                 };
40504
40505                 scope.isActive = function(matchIdx) {
40506                   return scope.active === matchIdx;
40507                 };
40508
40509                 scope.selectActive = function(matchIdx) {
40510                   scope.active = matchIdx;
40511                 };
40512
40513                 scope.selectMatch = function(activeIdx, evt) {
40514                   var debounce = scope.debounce();
40515                   if (angular.isNumber(debounce) || angular.isObject(debounce)) {
40516                     $$debounce(function() {
40517                       scope.select({activeIdx: activeIdx, evt: evt});
40518                     }, angular.isNumber(debounce) ? debounce : debounce['default']);
40519                   } else {
40520                     scope.select({activeIdx: activeIdx, evt: evt});
40521                   }
40522                 };
40523               }
40524             };
40525           }])
40526
40527           .directive('uibTypeaheadMatch', ['$templateRequest', '$compile', '$parse', function($templateRequest, $compile, $parse) {
40528             return {
40529               scope: {
40530                 index: '=',
40531                 match: '=',
40532                 query: '='
40533               },
40534               link: function(scope, element, attrs) {
40535                 var tplUrl = $parse(attrs.templateUrl)(scope.$parent) || 'uib/template/typeahead/typeahead-match.html';
40536                 $templateRequest(tplUrl).then(function(tplContent) {
40537                   var tplEl = angular.element(tplContent.trim());
40538                   element.replaceWith(tplEl);
40539                   $compile(tplEl)(scope);
40540                 });
40541               }
40542             };
40543           }])
40544
40545           .filter('uibTypeaheadHighlight', ['$sce', '$injector', '$log', function($sce, $injector, $log) {
40546             var isSanitizePresent;
40547             isSanitizePresent = $injector.has('$sanitize');
40548
40549             function escapeRegexp(queryToEscape) {
40550               // Regex: capture the whole query string and replace it with the string that will be used to match
40551               // the results, for example if the capture is "a" the result will be \a
40552               return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
40553             }
40554
40555             function containsHtml(matchItem) {
40556               return /<.*>/g.test(matchItem);
40557             }
40558
40559             return function(matchItem, query) {
40560               if (!isSanitizePresent && containsHtml(matchItem)) {
40561                 $log.warn('Unsafe use of typeahead please use ngSanitize'); // Warn the user about the danger
40562               }
40563               matchItem = query ? ('' + matchItem).replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : matchItem; // Replaces the capture string with a the same string inside of a "strong" tag
40564               if (!isSanitizePresent) {
40565                 matchItem = $sce.trustAsHtml(matchItem); // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
40566               }
40567               return matchItem;
40568             };
40569           }]);
40570
40571         angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
40572           $templateCache.put("uib/template/accordion/accordion-group.html",
40573             "<div class=\"panel\" ng-class=\"panelClass || 'panel-default'\">\n" +
40574             "  <div class=\"panel-heading\" ng-keypress=\"toggleOpen($event)\">\n" +
40575             "    <h4 class=\"panel-title\">\n" +
40576             "      <div tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></div>\n" +
40577             "    </h4>\n" +
40578             "  </div>\n" +
40579             "  <div class=\"panel-collapse collapse\" uib-collapse=\"!isOpen\">\n" +
40580             "     <div class=\"panel-body\" ng-transclude></div>\n" +
40581             "  </div>\n" +
40582             "</div>\n" +
40583             "");
40584         }]);
40585
40586         angular.module("uib/template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
40587           $templateCache.put("uib/template/accordion/accordion.html",
40588             "<div class=\"panel-group\" ng-transclude></div>");
40589         }]);
40590
40591         angular.module("uib/template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
40592           $templateCache.put("uib/template/alert/alert.html",
40593             "<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]\" role=\"alert\">\n" +
40594             "    <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close({$event: $event})\">\n" +
40595             "        <span aria-hidden=\"true\">&times;</span>\n" +
40596             "        <span class=\"sr-only\">Close</span>\n" +
40597             "    </button>\n" +
40598             "    <div ng-transclude></div>\n" +
40599             "</div>\n" +
40600             "");
40601         }]);
40602
40603         angular.module("uib/template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
40604           $templateCache.put("uib/template/carousel/carousel.html",
40605             "<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
40606             "  <div class=\"carousel-inner\" ng-transclude></div>\n" +
40607             "  <a role=\"button\" href class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\">\n" +
40608             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-left\"></span>\n" +
40609             "    <span class=\"sr-only\">previous</span>\n" +
40610             "  </a>\n" +
40611             "  <a role=\"button\" href class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\">\n" +
40612             "    <span aria-hidden=\"true\" class=\"glyphicon glyphicon-chevron-right\"></span>\n" +
40613             "    <span class=\"sr-only\">next</span>\n" +
40614             "  </a>\n" +
40615             "  <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
40616             "    <li ng-repeat=\"slide in slides | orderBy:indexOfSlide track by $index\" ng-class=\"{ active: isActive(slide) }\" ng-click=\"select(slide)\">\n" +
40617             "      <span class=\"sr-only\">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if=\"isActive(slide)\">, currently active</span></span>\n" +
40618             "    </li>\n" +
40619             "  </ol>\n" +
40620             "</div>");
40621         }]);
40622
40623         angular.module("uib/template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
40624           $templateCache.put("uib/template/carousel/slide.html",
40625             "<div ng-class=\"{\n" +
40626             "    'active': active\n" +
40627             "  }\" class=\"item text-center\" ng-transclude></div>\n" +
40628             "");
40629         }]);
40630
40631         angular.module("uib/template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
40632           $templateCache.put("uib/template/datepicker/datepicker.html",
40633             "<div class=\"uib-datepicker\" ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
40634             "  <uib-daypicker ng-switch-when=\"day\" tabindex=\"0\"></uib-daypicker>\n" +
40635             "  <uib-monthpicker ng-switch-when=\"month\" tabindex=\"0\"></uib-monthpicker>\n" +
40636             "  <uib-yearpicker ng-switch-when=\"year\" tabindex=\"0\"></uib-yearpicker>\n" +
40637             "</div>");
40638         }]);
40639
40640         angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) {
40641           $templateCache.put("uib/template/datepicker/day.html",
40642             "<table class=\"uib-daypicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40643             "  <thead>\n" +
40644             "    <tr>\n" +
40645             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
40646             "      <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
40647             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
40648             "    </tr>\n" +
40649             "    <tr>\n" +
40650             "      <th ng-if=\"showWeeks\" class=\"text-center\"></th>\n" +
40651             "      <th ng-repeat=\"label in ::labels track by $index\" class=\"text-center\"><small aria-label=\"{{::label.full}}\">{{::label.abbr}}</small></th>\n" +
40652             "    </tr>\n" +
40653             "  </thead>\n" +
40654             "  <tbody>\n" +
40655             "    <tr class=\"uib-weeks\" ng-repeat=\"row in rows track by $index\">\n" +
40656             "      <td ng-if=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
40657             "      <td ng-repeat=\"dt in row\" class=\"uib-day text-center\" role=\"gridcell\"\n" +
40658             "        id=\"{{::dt.uid}}\"\n" +
40659             "        ng-class=\"::dt.customClass\">\n" +
40660             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\"\n" +
40661             "          uib-is-class=\"\n" +
40662             "            'btn-info' for selectedDt,\n" +
40663             "            'active' for activeDt\n" +
40664             "            on dt\"\n" +
40665             "          ng-click=\"select(dt.date)\"\n" +
40666             "          ng-disabled=\"::dt.disabled\"\n" +
40667             "          tabindex=\"-1\"><span ng-class=\"::{'text-muted': dt.secondary, 'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40668             "      </td>\n" +
40669             "    </tr>\n" +
40670             "  </tbody>\n" +
40671             "</table>\n" +
40672             "");
40673         }]);
40674
40675         angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) {
40676           $templateCache.put("uib/template/datepicker/month.html",
40677             "<table class=\"uib-monthpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40678             "  <thead>\n" +
40679             "    <tr>\n" +
40680             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
40681             "      <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
40682             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
40683             "    </tr>\n" +
40684             "  </thead>\n" +
40685             "  <tbody>\n" +
40686             "    <tr class=\"uib-months\" ng-repeat=\"row in rows track by $index\">\n" +
40687             "      <td ng-repeat=\"dt in row\" class=\"uib-month text-center\" role=\"gridcell\"\n" +
40688             "        id=\"{{::dt.uid}}\"\n" +
40689             "        ng-class=\"::dt.customClass\">\n" +
40690             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40691             "          uib-is-class=\"\n" +
40692             "            'btn-info' for selectedDt,\n" +
40693             "            'active' for activeDt\n" +
40694             "            on dt\"\n" +
40695             "          ng-click=\"select(dt.date)\"\n" +
40696             "          ng-disabled=\"::dt.disabled\"\n" +
40697             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40698             "      </td>\n" +
40699             "    </tr>\n" +
40700             "  </tbody>\n" +
40701             "</table>\n" +
40702             "");
40703         }]);
40704
40705         angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) {
40706           $templateCache.put("uib/template/datepicker/popup.html",
40707             "<ul class=\"uib-datepicker-popup dropdown-menu\" dropdown-nested ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" +
40708             "   <li ng-transclude></li>\n" +
40709             "   <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\" class=\"uib-button-bar\">\n" +
40710             "           <span class=\"btn-group pull-left\">\n" +
40711             "                   <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" +
40712             "                   <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
40713             "           </span>\n" +
40714             "           <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
40715             "   </li>\n" +
40716             "</ul>\n" +
40717             "");
40718         }]);
40719
40720         angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) {
40721           $templateCache.put("uib/template/datepicker/year.html",
40722             "<table class=\"uib-yearpicker\" role=\"grid\" aria-labelledby=\"{{::uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
40723             "  <thead>\n" +
40724             "    <tr>\n" +
40725             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
40726             "      <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
40727             "      <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
40728             "    </tr>\n" +
40729             "  </thead>\n" +
40730             "  <tbody>\n" +
40731             "    <tr class=\"uib-years\" ng-repeat=\"row in rows track by $index\">\n" +
40732             "      <td ng-repeat=\"dt in row\" class=\"uib-year text-center\" role=\"gridcell\"\n" +
40733             "        id=\"{{::dt.uid}}\"\n" +
40734             "        ng-class=\"::dt.customClass\">\n" +
40735             "        <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" +
40736             "          uib-is-class=\"\n" +
40737             "            'btn-info' for selectedDt,\n" +
40738             "            'active' for activeDt\n" +
40739             "            on dt\"\n" +
40740             "          ng-click=\"select(dt.date)\"\n" +
40741             "          ng-disabled=\"::dt.disabled\"\n" +
40742             "          tabindex=\"-1\"><span ng-class=\"::{'text-info': dt.current}\">{{::dt.label}}</span></button>\n" +
40743             "      </td>\n" +
40744             "    </tr>\n" +
40745             "  </tbody>\n" +
40746             "</table>\n" +
40747             "");
40748         }]);
40749
40750         angular.module("uib/template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
40751           $templateCache.put("uib/template/modal/backdrop.html",
40752             "<div class=\"modal-backdrop\"\n" +
40753             "     uib-modal-animation-class=\"fade\"\n" +
40754             "     modal-in-class=\"in\"\n" +
40755             "     ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
40756             "></div>\n" +
40757             "");
40758         }]);
40759
40760         angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) {
40761           $templateCache.put("uib/template/modal/window.html",
40762             "<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
40763             "    uib-modal-animation-class=\"fade\"\n" +
40764             "    modal-in-class=\"in\"\n" +
40765             "    ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" +
40766             "    <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" +
40767             "</div>\n" +
40768             "");
40769         }]);
40770
40771         angular.module("uib/template/pager/pager.html", []).run(["$templateCache", function($templateCache) {
40772           $templateCache.put("uib/template/pager/pager.html",
40773             "<ul class=\"pager\">\n" +
40774             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40775             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40776             "</ul>\n" +
40777             "");
40778         }]);
40779
40780         angular.module("uib/template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
40781           $templateCache.put("uib/template/pagination/pager.html",
40782             "<ul class=\"pager\">\n" +
40783             "  <li ng-class=\"{disabled: noPrevious()||ngDisabled, previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40784             "  <li ng-class=\"{disabled: noNext()||ngDisabled, next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40785             "</ul>\n" +
40786             "");
40787         }]);
40788
40789         angular.module("uib/template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
40790           $templateCache.put("uib/template/pagination/pagination.html",
40791             "<ul class=\"pagination\">\n" +
40792             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-first\"><a href ng-click=\"selectPage(1, $event)\">{{::getText('first')}}</a></li>\n" +
40793             "  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noPrevious()||ngDisabled}\" class=\"pagination-prev\"><a href ng-click=\"selectPage(page - 1, $event)\">{{::getText('previous')}}</a></li>\n" +
40794             "  <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active,disabled: ngDisabled&&!page.active}\" class=\"pagination-page\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
40795             "  <li ng-if=\"::directionLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-next\"><a href ng-click=\"selectPage(page + 1, $event)\">{{::getText('next')}}</a></li>\n" +
40796             "  <li ng-if=\"::boundaryLinks\" ng-class=\"{disabled: noNext()||ngDisabled}\" class=\"pagination-last\"><a href ng-click=\"selectPage(totalPages, $event)\">{{::getText('last')}}</a></li>\n" +
40797             "</ul>\n" +
40798             "");
40799         }]);
40800
40801         angular.module("uib/template/tooltip/tooltip-html-popup.html", []).run(["$templateCache", function($templateCache) {
40802           $templateCache.put("uib/template/tooltip/tooltip-html-popup.html",
40803             "<div class=\"tooltip\"\n" +
40804             "  tooltip-animation-class=\"fade\"\n" +
40805             "  uib-tooltip-classes\n" +
40806             "  ng-class=\"{ in: isOpen() }\">\n" +
40807             "  <div class=\"tooltip-arrow\"></div>\n" +
40808             "  <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
40809             "</div>\n" +
40810             "");
40811         }]);
40812
40813         angular.module("uib/template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
40814           $templateCache.put("uib/template/tooltip/tooltip-popup.html",
40815             "<div class=\"tooltip\"\n" +
40816             "  tooltip-animation-class=\"fade\"\n" +
40817             "  uib-tooltip-classes\n" +
40818             "  ng-class=\"{ in: isOpen() }\">\n" +
40819             "  <div class=\"tooltip-arrow\"></div>\n" +
40820             "  <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
40821             "</div>\n" +
40822             "");
40823         }]);
40824
40825         angular.module("uib/template/tooltip/tooltip-template-popup.html", []).run(["$templateCache", function($templateCache) {
40826           $templateCache.put("uib/template/tooltip/tooltip-template-popup.html",
40827             "<div class=\"tooltip\"\n" +
40828             "  tooltip-animation-class=\"fade\"\n" +
40829             "  uib-tooltip-classes\n" +
40830             "  ng-class=\"{ in: isOpen() }\">\n" +
40831             "  <div class=\"tooltip-arrow\"></div>\n" +
40832             "  <div class=\"tooltip-inner\"\n" +
40833             "    uib-tooltip-template-transclude=\"contentExp()\"\n" +
40834             "    tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40835             "</div>\n" +
40836             "");
40837         }]);
40838
40839         angular.module("uib/template/popover/popover-html.html", []).run(["$templateCache", function($templateCache) {
40840           $templateCache.put("uib/template/popover/popover-html.html",
40841             "<div class=\"popover\"\n" +
40842             "  tooltip-animation-class=\"fade\"\n" +
40843             "  uib-tooltip-classes\n" +
40844             "  ng-class=\"{ in: isOpen() }\">\n" +
40845             "  <div class=\"arrow\"></div>\n" +
40846             "\n" +
40847             "  <div class=\"popover-inner\">\n" +
40848             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40849             "      <div class=\"popover-content\" ng-bind-html=\"contentExp()\"></div>\n" +
40850             "  </div>\n" +
40851             "</div>\n" +
40852             "");
40853         }]);
40854
40855         angular.module("uib/template/popover/popover-template.html", []).run(["$templateCache", function($templateCache) {
40856           $templateCache.put("uib/template/popover/popover-template.html",
40857             "<div class=\"popover\"\n" +
40858             "  tooltip-animation-class=\"fade\"\n" +
40859             "  uib-tooltip-classes\n" +
40860             "  ng-class=\"{ in: isOpen() }\">\n" +
40861             "  <div class=\"arrow\"></div>\n" +
40862             "\n" +
40863             "  <div class=\"popover-inner\">\n" +
40864             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40865             "      <div class=\"popover-content\"\n" +
40866             "        uib-tooltip-template-transclude=\"contentExp()\"\n" +
40867             "        tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
40868             "  </div>\n" +
40869             "</div>\n" +
40870             "");
40871         }]);
40872
40873         angular.module("uib/template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
40874           $templateCache.put("uib/template/popover/popover.html",
40875             "<div class=\"popover\"\n" +
40876             "  tooltip-animation-class=\"fade\"\n" +
40877             "  uib-tooltip-classes\n" +
40878             "  ng-class=\"{ in: isOpen() }\">\n" +
40879             "  <div class=\"arrow\"></div>\n" +
40880             "\n" +
40881             "  <div class=\"popover-inner\">\n" +
40882             "      <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
40883             "      <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
40884             "  </div>\n" +
40885             "</div>\n" +
40886             "");
40887         }]);
40888
40889         angular.module("uib/template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
40890           $templateCache.put("uib/template/progressbar/bar.html",
40891             "<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
40892             "");
40893         }]);
40894
40895         angular.module("uib/template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
40896           $templateCache.put("uib/template/progressbar/progress.html",
40897             "<div class=\"progress\" ng-transclude aria-labelledby=\"{{::title}}\"></div>");
40898         }]);
40899
40900         angular.module("uib/template/progressbar/progressbar.html", []).run(["$templateCache", function($templateCache) {
40901           $templateCache.put("uib/template/progressbar/progressbar.html",
40902             "<div class=\"progress\">\n" +
40903             "  <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" aria-labelledby=\"{{::title}}\" ng-transclude></div>\n" +
40904             "</div>\n" +
40905             "");
40906         }]);
40907
40908         angular.module("uib/template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
40909           $templateCache.put("uib/template/rating/rating.html",
40910             "<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
40911             "    <span ng-repeat-start=\"r in range track by $index\" class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
40912             "    <i ng-repeat-end ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\" ng-attr-title=\"{{r.title}}\" aria-valuetext=\"{{r.title}}\"></i>\n" +
40913             "</span>\n" +
40914             "");
40915         }]);
40916
40917         angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
40918           $templateCache.put("uib/template/tabs/tab.html",
40919             "<li ng-class=\"{active: active, disabled: disabled}\" class=\"uib-tab\">\n" +
40920             "  <div ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</div>\n" +
40921             "</li>\n" +
40922             "");
40923         }]);
40924
40925         angular.module("uib/template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {
40926           $templateCache.put("uib/template/tabs/tabset.html",
40927             "<div>\n" +
40928             "  <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
40929             "  <div class=\"tab-content\">\n" +
40930             "    <div class=\"tab-pane\" \n" +
40931             "         ng-repeat=\"tab in tabs\" \n" +
40932             "         ng-class=\"{active: tab.active}\"\n" +
40933             "         uib-tab-content-transclude=\"tab\">\n" +
40934             "    </div>\n" +
40935             "  </div>\n" +
40936             "</div>\n" +
40937             "");
40938         }]);
40939
40940         angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
40941           $templateCache.put("uib/template/timepicker/timepicker.html",
40942             "<table class=\"uib-timepicker\">\n" +
40943             "  <tbody>\n" +
40944             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40945             "      <td class=\"uib-increment hours\"><a ng-click=\"incrementHours()\" ng-class=\"{disabled: noIncrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
40946             "      <td>&nbsp;</td>\n" +
40947             "      <td class=\"uib-increment minutes\"><a ng-click=\"incrementMinutes()\" ng-class=\"{disabled: noIncrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
40948             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40949             "      <td ng-show=\"showSeconds\" class=\"uib-increment seconds\"><a ng-click=\"incrementSeconds()\" ng-class=\"{disabled: noIncrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noIncrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
40950             "      <td ng-show=\"showMeridian\"></td>\n" +
40951             "    </tr>\n" +
40952             "    <tr>\n" +
40953             "      <td class=\"form-group uib-time hours\" ng-class=\"{'has-error': invalidHours}\">\n" +
40954             "        <input style=\"width:50px;\" type=\"text\" placeholder=\"HH\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
40955             "      </td>\n" +
40956             "      <td class=\"uib-separator\">:</td>\n" +
40957             "      <td class=\"form-group uib-time minutes\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
40958             "        <input style=\"width:50px;\" type=\"text\" placeholder=\"MM\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"::readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
40959             "      </td>\n" +
40960             "      <td ng-show=\"showSeconds\" class=\"uib-separator\">:</td>\n" +
40961             "      <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" +
40962             "        <input style=\"width:50px;\" type=\"text\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"disabled\" ng-blur=\"blur()\">\n" +
40963             "      </td>\n" +
40964             "      <td ng-show=\"showMeridian\" class=\"uib-time am-pm\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" +
40965             "    </tr>\n" +
40966             "    <tr class=\"text-center\" ng-show=\"::showSpinners\">\n" +
40967             "      <td class=\"uib-decrement hours\"><a ng-click=\"decrementHours()\" ng-class=\"{disabled: noDecrementHours()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementHours()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
40968             "      <td>&nbsp;</td>\n" +
40969             "      <td class=\"uib-decrement minutes\"><a ng-click=\"decrementMinutes()\" ng-class=\"{disabled: noDecrementMinutes()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementMinutes()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
40970             "      <td ng-show=\"showSeconds\">&nbsp;</td>\n" +
40971             "      <td ng-show=\"showSeconds\" class=\"uib-decrement seconds\"><a ng-click=\"decrementSeconds()\" ng-class=\"{disabled: noDecrementSeconds()}\" class=\"btn btn-link\" ng-disabled=\"noDecrementSeconds()\" tabindex=\"{{::tabindex}}\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
40972             "      <td ng-show=\"showMeridian\"></td>\n" +
40973             "    </tr>\n" +
40974             "  </tbody>\n" +
40975             "</table>\n" +
40976             "");
40977         }]);
40978
40979         angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {
40980           $templateCache.put("uib/template/typeahead/typeahead-match.html",
40981             "<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" +
40982             "");
40983         }]);
40984
40985         angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) {
40986           $templateCache.put("uib/template/typeahead/typeahead-popup.html",
40987             "<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
40988             "    <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" +
40989             "        <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
40990             "    </li>\n" +
40991             "</ul>\n" +
40992             "");
40993         }]);
40994         angular.module('ui.bootstrap.carousel').run(function() {!angular.$$csp() && angular.element(document).find('head').prepend('<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>'); })
40995
40996 /***/ },
40997 /* 8 */
40998 /***/ function(module, exports) {
40999
41000         var app;
41001         (function (app) {
41002             var declares;
41003             (function (declares) {
41004                 var CommandInfo = (function () {
41005                     function CommandInfo(name) {
41006                         this.name = name;
41007                     }
41008                     return CommandInfo;
41009                 })();
41010                 declares.CommandInfo = CommandInfo;
41011             })(declares = app.declares || (app.declares = {}));
41012         })(app || (app = {}));
41013         var app;
41014         (function (app) {
41015             var services;
41016             (function (services) {
41017                 var APIEndPoint = (function () {
41018                     function APIEndPoint($resource) {
41019                         this.$resource = $resource;
41020                     }
41021                     APIEndPoint.prototype.resource = function () {
41022                         var getOption = {
41023                             method: 'GET',
41024                             isArray: false
41025                         };
41026                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
41027                         return this.$resource(apiUrl, {}, { getOption: getOption });
41028                     };
41029                     APIEndPoint.prototype.getOptionControlFile = function () {
41030                         return this.resource().query();
41031                     };
41032                     return APIEndPoint;
41033                 })();
41034                 services.APIEndPoint = APIEndPoint;
41035             })(services = app.services || (app.services = {}));
41036         })(app || (app = {}));
41037         var app;
41038         (function (app) {
41039             var services;
41040             (function (services) {
41041                 var MyModal = (function () {
41042                     function MyModal($uibModal) {
41043                         this.$uibModal = $uibModal;
41044                         this.modalOption = {
41045                             backdrop: true,
41046                             controller: null,
41047                             templateUrl: null,
41048                             size: null
41049                         };
41050                     }
41051                     MyModal.prototype.open = function (modalName) {
41052                         if (modalName === 'SelectCommand') {
41053                             this.modalOption.templateUrl = 'templates/select-command.html';
41054                             this.modalOption.size = 'lg';
41055                         }
41056                         return this.$uibModal.open(this.modalOption);
41057                     };
41058                     return MyModal;
41059                 })();
41060                 services.MyModal = MyModal;
41061             })(services = app.services || (app.services = {}));
41062         })(app || (app = {}));
41063         var app;
41064         (function (app) {
41065             var directives;
41066             (function (directives) {
41067                 var Command = (function () {
41068                     function Command() {
41069                         this.restrict = 'E';
41070                         this.replace = true;
41071                         this.scope = true;
41072                         this.controller = 'commandController';
41073                         this.controllerAs = 'ctrl';
41074                         this.bindToController = {
41075                             index: '=',
41076                             name: '=',
41077                             remove: '&',
41078                             list: '='
41079                         };
41080                         this.templateUrl = 'templates/command.html';
41081                     }
41082                     Command.Factory = function () {
41083                         var directive = function () {
41084                             return new Command();
41085                         };
41086                         directive.$inject = [];
41087                         return directive;
41088                     };
41089                     return Command;
41090                 })();
41091                 directives.Command = Command;
41092                 var CommandController = (function () {
41093                     function CommandController(APIEndPoint, $scope) {
41094                         this.APIEndPoint = APIEndPoint;
41095                         this.$scope = $scope;
41096                         var controller = this;
41097                         this.APIEndPoint
41098                             .getOptionControlFile()
41099                             .$promise
41100                             .then(function (result) {
41101                             controller.options = result;
41102                         });
41103                         this.files = ['a.file', 'b.file', 'c.file'];
41104                         this.heading = "[" + this.index + "]: dcdFilePring";
41105                         this.isOpen = true;
41106                         this.$scope.$on('close', function () {
41107                             controller.isOpen = false;
41108                         });
41109                     }
41110                     CommandController.prototype.submit = function () {
41111                         var params = {};
41112                         angular.forEach(this.options, function (option) {
41113                             var inputs = [];
41114                             angular.forEach(option.arg, function (arg) {
41115                                 if (arg.input) {
41116                                     inputs.push(arg.input);
41117                                 }
41118                             });
41119                             if (inputs.length > 0) {
41120                                 params[option.option] = inputs;
41121                             }
41122                         });
41123                         console.log(params);
41124                     };
41125                     CommandController.prototype.removeMySelf = function (index) {
41126                         this.remove()(index, this.list);
41127                     };
41128                     CommandController.$inject = ['APIEndPoint', '$scope'];
41129                     return CommandController;
41130                 })();
41131                 directives.CommandController = CommandController;
41132             })(directives = app.directives || (app.directives = {}));
41133         })(app || (app = {}));
41134         var app;
41135         (function (app) {
41136             var directives;
41137             (function (directives) {
41138                 var HeaderMenu = (function () {
41139                     function HeaderMenu() {
41140                         this.restrict = 'E';
41141                         this.replace = true;
41142                         this.templateUrl = 'templates/header-menu.html';
41143                     }
41144                     HeaderMenu.Factory = function () {
41145                         var directive = function () {
41146                             return new HeaderMenu();
41147                         };
41148                         return directive;
41149                     };
41150                     return HeaderMenu;
41151                 })();
41152                 directives.HeaderMenu = HeaderMenu;
41153             })(directives = app.directives || (app.directives = {}));
41154         })(app || (app = {}));
41155         var app;
41156         (function (app) {
41157             var directives;
41158             (function (directives) {
41159                 var Option = (function () {
41160                     function Option() {
41161                         this.restrict = 'E';
41162                         this.replace = true;
41163                         this.controller = 'optionController';
41164                         this.bindToController = {
41165                             info: '=',
41166                             files: '='
41167                         };
41168                         this.scope = true;
41169                         this.templateUrl = 'templates/option.html';
41170                         this.controllerAs = 'ctrl';
41171                     }
41172                     Option.Factory = function () {
41173                         var directive = function () {
41174                             return new Option();
41175                         };
41176                         directive.$inject = [];
41177                         return directive;
41178                     };
41179                     return Option;
41180                 })();
41181                 directives.Option = Option;
41182                 var OptionController = (function () {
41183                     function OptionController() {
41184                         var controller = this;
41185                         angular.forEach(controller.info.arg, function (arg) {
41186                             if (arg.initialValue) {
41187                                 if (arg.formType === 'number') {
41188                                     arg.input = parseInt(arg.initialValue);
41189                                 }
41190                                 else {
41191                                     arg.input = arg.initialValue;
41192                                 }
41193                             }
41194                         });
41195                     }
41196                     OptionController.$inject = [];
41197                     return OptionController;
41198                 })();
41199                 directives.OptionController = OptionController;
41200             })(directives = app.directives || (app.directives = {}));
41201         })(app || (app = {}));
41202         var app;
41203         (function (app) {
41204             var controllers;
41205             (function (controllers) {
41206                 var Execution = (function () {
41207                     function Execution(MyModal, $scope) {
41208                         this.MyModal = MyModal;
41209                         this.$scope = $scope;
41210                         this.commandInfoList = [];
41211                     }
41212                     ;
41213                     Execution.prototype.add = function () {
41214                         this.$scope.$broadcast('close');
41215                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
41216                     };
41217                     Execution.prototype.open = function () {
41218                         var result = this.MyModal.open('SelectCommand');
41219                         console.log(result);
41220                     };
41221                     Execution.prototype.remove = function (index, list) {
41222                         list.splice(index, 1);
41223                     };
41224                     Execution.prototype.close = function () {
41225                         console.log("close");
41226                     };
41227                     Execution.$inject = ['MyModal', '$scope'];
41228                     return Execution;
41229                 })();
41230                 controllers.Execution = Execution;
41231             })(controllers = app.controllers || (app.controllers = {}));
41232         })(app || (app = {}));
41233         var app;
41234         (function (app) {
41235             var controllers;
41236             (function (controllers) {
41237                 var Files = (function () {
41238                     function Files($scope) {
41239                         this.page = "ManageFiles";
41240                     }
41241                     Files.$inject = ['$scope'];
41242                     return Files;
41243                 })();
41244                 controllers.Files = Files;
41245             })(controllers = app.controllers || (app.controllers = {}));
41246         })(app || (app = {}));
41247         var app;
41248         (function (app) {
41249             var controllers;
41250             (function (controllers) {
41251                 var History = (function () {
41252                     function History($scope) {
41253                         this.page = "History";
41254                     }
41255                     History.$inject = ['$scope'];
41256                     return History;
41257                 })();
41258                 controllers.History = History;
41259             })(controllers = app.controllers || (app.controllers = {}));
41260         })(app || (app = {}));
41261         var app;
41262         (function (app) {
41263             'use strict';
41264             var appName = 'zephyr';
41265             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41266             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41267                 $urlRouterProvider.otherwise('/execution');
41268                 $locationProvider.html5Mode({
41269                     enabled: true,
41270                     requireBase: false
41271                 });
41272                 $stateProvider
41273                     .state('execution', {
41274                     url: '/execution',
41275                     templateUrl: 'templates/execution.html',
41276                     controller: 'executionController',
41277                     controllerAs: 'c'
41278                 })
41279                     .state('files', {
41280                     url: '/files',
41281                     templateUrl: 'templates/files.html',
41282                     controller: 'filesController',
41283                     controllerAs: 'c'
41284                 })
41285                     .state('history', {
41286                     url: '/history',
41287                     templateUrl: 'templates/history.html',
41288                     controller: 'historyController',
41289                     controllerAs: 'c'
41290                 });
41291             });
41292             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41293             app.zephyr.service('MyModal', app.services.MyModal);
41294             app.zephyr.controller('executionController', app.controllers.Execution);
41295             app.zephyr.controller('filesController', app.controllers.Files);
41296             app.zephyr.controller('historyController', app.controllers.History);
41297             app.zephyr.controller('commandController', app.directives.CommandController);
41298             app.zephyr.controller('optionController', app.directives.OptionController);
41299             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41300             app.zephyr.directive('command', app.directives.Command.Factory());
41301             app.zephyr.directive('option', app.directives.Option.Factory());
41302         })(app || (app = {}));
41303
41304
41305 /***/ },
41306 /* 9 */
41307 /***/ function(module, exports) {
41308
41309         var app;
41310         (function (app) {
41311             var declares;
41312             (function (declares) {
41313                 var CommandInfo = (function () {
41314                     function CommandInfo(name) {
41315                         this.name = name;
41316                     }
41317                     return CommandInfo;
41318                 })();
41319                 declares.CommandInfo = CommandInfo;
41320             })(declares = app.declares || (app.declares = {}));
41321         })(app || (app = {}));
41322         var app;
41323         (function (app) {
41324             var services;
41325             (function (services) {
41326                 var APIEndPoint = (function () {
41327                     function APIEndPoint($resource) {
41328                         this.$resource = $resource;
41329                     }
41330                     APIEndPoint.prototype.resource = function () {
41331                         var getOption = {
41332                             method: 'GET',
41333                             isArray: false
41334                         };
41335                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
41336                         return this.$resource(apiUrl, {}, { getOption: getOption });
41337                     };
41338                     APIEndPoint.prototype.getOptionControlFile = function () {
41339                         return this.resource().query();
41340                     };
41341                     return APIEndPoint;
41342                 })();
41343                 services.APIEndPoint = APIEndPoint;
41344             })(services = app.services || (app.services = {}));
41345         })(app || (app = {}));
41346         var app;
41347         (function (app) {
41348             var services;
41349             (function (services) {
41350                 var MyModal = (function () {
41351                     function MyModal($uibModal) {
41352                         this.$uibModal = $uibModal;
41353                         this.modalOption = {
41354                             backdrop: true,
41355                             controller: null,
41356                             templateUrl: null,
41357                             size: null
41358                         };
41359                     }
41360                     MyModal.prototype.open = function (modalName) {
41361                         if (modalName === 'SelectCommand') {
41362                             this.modalOption.templateUrl = 'templates/select-command.html';
41363                             this.modalOption.size = 'lg';
41364                         }
41365                         return this.$uibModal.open(this.modalOption);
41366                     };
41367                     return MyModal;
41368                 })();
41369                 services.MyModal = MyModal;
41370             })(services = app.services || (app.services = {}));
41371         })(app || (app = {}));
41372         var app;
41373         (function (app) {
41374             var directives;
41375             (function (directives) {
41376                 var Command = (function () {
41377                     function Command() {
41378                         this.restrict = 'E';
41379                         this.replace = true;
41380                         this.scope = true;
41381                         this.controller = 'commandController';
41382                         this.controllerAs = 'ctrl';
41383                         this.bindToController = {
41384                             index: '=',
41385                             name: '=',
41386                             remove: '&',
41387                             list: '='
41388                         };
41389                         this.templateUrl = 'templates/command.html';
41390                     }
41391                     Command.Factory = function () {
41392                         var directive = function () {
41393                             return new Command();
41394                         };
41395                         directive.$inject = [];
41396                         return directive;
41397                     };
41398                     return Command;
41399                 })();
41400                 directives.Command = Command;
41401                 var CommandController = (function () {
41402                     function CommandController(APIEndPoint, $scope) {
41403                         this.APIEndPoint = APIEndPoint;
41404                         this.$scope = $scope;
41405                         var controller = this;
41406                         this.APIEndPoint
41407                             .getOptionControlFile()
41408                             .$promise
41409                             .then(function (result) {
41410                             controller.options = result;
41411                         });
41412                         this.files = ['a.file', 'b.file', 'c.file'];
41413                         this.heading = "[" + this.index + "]: dcdFilePring";
41414                         this.isOpen = true;
41415                         this.$scope.$on('close', function () {
41416                             controller.isOpen = false;
41417                         });
41418                     }
41419                     CommandController.prototype.submit = function () {
41420                         var params = {};
41421                         angular.forEach(this.options, function (option) {
41422                             var inputs = [];
41423                             angular.forEach(option.arg, function (arg) {
41424                                 if (arg.input) {
41425                                     inputs.push(arg.input);
41426                                 }
41427                             });
41428                             if (inputs.length > 0) {
41429                                 params[option.option] = inputs;
41430                             }
41431                         });
41432                         console.log(params);
41433                     };
41434                     CommandController.prototype.removeMySelf = function (index) {
41435                         this.remove()(index, this.list);
41436                     };
41437                     CommandController.$inject = ['APIEndPoint', '$scope'];
41438                     return CommandController;
41439                 })();
41440                 directives.CommandController = CommandController;
41441             })(directives = app.directives || (app.directives = {}));
41442         })(app || (app = {}));
41443         var app;
41444         (function (app) {
41445             var directives;
41446             (function (directives) {
41447                 var HeaderMenu = (function () {
41448                     function HeaderMenu() {
41449                         this.restrict = 'E';
41450                         this.replace = true;
41451                         this.templateUrl = 'templates/header-menu.html';
41452                     }
41453                     HeaderMenu.Factory = function () {
41454                         var directive = function () {
41455                             return new HeaderMenu();
41456                         };
41457                         return directive;
41458                     };
41459                     return HeaderMenu;
41460                 })();
41461                 directives.HeaderMenu = HeaderMenu;
41462             })(directives = app.directives || (app.directives = {}));
41463         })(app || (app = {}));
41464         var app;
41465         (function (app) {
41466             var directives;
41467             (function (directives) {
41468                 var Option = (function () {
41469                     function Option() {
41470                         this.restrict = 'E';
41471                         this.replace = true;
41472                         this.controller = 'optionController';
41473                         this.bindToController = {
41474                             info: '=',
41475                             files: '='
41476                         };
41477                         this.scope = true;
41478                         this.templateUrl = 'templates/option.html';
41479                         this.controllerAs = 'ctrl';
41480                     }
41481                     Option.Factory = function () {
41482                         var directive = function () {
41483                             return new Option();
41484                         };
41485                         directive.$inject = [];
41486                         return directive;
41487                     };
41488                     return Option;
41489                 })();
41490                 directives.Option = Option;
41491                 var OptionController = (function () {
41492                     function OptionController() {
41493                         var controller = this;
41494                         angular.forEach(controller.info.arg, function (arg) {
41495                             if (arg.initialValue) {
41496                                 if (arg.formType === 'number') {
41497                                     arg.input = parseInt(arg.initialValue);
41498                                 }
41499                                 else {
41500                                     arg.input = arg.initialValue;
41501                                 }
41502                             }
41503                         });
41504                     }
41505                     OptionController.$inject = [];
41506                     return OptionController;
41507                 })();
41508                 directives.OptionController = OptionController;
41509             })(directives = app.directives || (app.directives = {}));
41510         })(app || (app = {}));
41511         var app;
41512         (function (app) {
41513             var controllers;
41514             (function (controllers) {
41515                 var Execution = (function () {
41516                     function Execution(MyModal, $scope) {
41517                         this.MyModal = MyModal;
41518                         this.$scope = $scope;
41519                         this.commandInfoList = [];
41520                     }
41521                     ;
41522                     Execution.prototype.add = function () {
41523                         this.$scope.$broadcast('close');
41524                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
41525                     };
41526                     Execution.prototype.open = function () {
41527                         var result = this.MyModal.open('SelectCommand');
41528                         console.log(result);
41529                     };
41530                     Execution.prototype.remove = function (index, list) {
41531                         list.splice(index, 1);
41532                     };
41533                     Execution.prototype.close = function () {
41534                         console.log("close");
41535                     };
41536                     Execution.$inject = ['MyModal', '$scope'];
41537                     return Execution;
41538                 })();
41539                 controllers.Execution = Execution;
41540             })(controllers = app.controllers || (app.controllers = {}));
41541         })(app || (app = {}));
41542         var app;
41543         (function (app) {
41544             var controllers;
41545             (function (controllers) {
41546                 var Files = (function () {
41547                     function Files($scope) {
41548                         this.page = "ManageFiles";
41549                     }
41550                     Files.$inject = ['$scope'];
41551                     return Files;
41552                 })();
41553                 controllers.Files = Files;
41554             })(controllers = app.controllers || (app.controllers = {}));
41555         })(app || (app = {}));
41556         var app;
41557         (function (app) {
41558             var controllers;
41559             (function (controllers) {
41560                 var History = (function () {
41561                     function History($scope) {
41562                         this.page = "History";
41563                     }
41564                     History.$inject = ['$scope'];
41565                     return History;
41566                 })();
41567                 controllers.History = History;
41568             })(controllers = app.controllers || (app.controllers = {}));
41569         })(app || (app = {}));
41570         var app;
41571         (function (app) {
41572             'use strict';
41573             var appName = 'zephyr';
41574             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41575             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41576                 $urlRouterProvider.otherwise('/execution');
41577                 $locationProvider.html5Mode({
41578                     enabled: true,
41579                     requireBase: false
41580                 });
41581                 $stateProvider
41582                     .state('execution', {
41583                     url: '/execution',
41584                     templateUrl: 'templates/execution.html',
41585                     controller: 'executionController',
41586                     controllerAs: 'c'
41587                 })
41588                     .state('files', {
41589                     url: '/files',
41590                     templateUrl: 'templates/files.html',
41591                     controller: 'filesController',
41592                     controllerAs: 'c'
41593                 })
41594                     .state('history', {
41595                     url: '/history',
41596                     templateUrl: 'templates/history.html',
41597                     controller: 'historyController',
41598                     controllerAs: 'c'
41599                 });
41600             });
41601             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41602             app.zephyr.service('MyModal', app.services.MyModal);
41603             app.zephyr.controller('executionController', app.controllers.Execution);
41604             app.zephyr.controller('filesController', app.controllers.Files);
41605             app.zephyr.controller('historyController', app.controllers.History);
41606             app.zephyr.controller('commandController', app.directives.CommandController);
41607             app.zephyr.controller('optionController', app.directives.OptionController);
41608             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41609             app.zephyr.directive('command', app.directives.Command.Factory());
41610             app.zephyr.directive('option', app.directives.Option.Factory());
41611         })(app || (app = {}));
41612
41613
41614 /***/ },
41615 /* 10 */
41616 /***/ function(module, exports) {
41617
41618         var app;
41619         (function (app) {
41620             var declares;
41621             (function (declares) {
41622                 var CommandInfo = (function () {
41623                     function CommandInfo(name) {
41624                         this.name = name;
41625                     }
41626                     return CommandInfo;
41627                 })();
41628                 declares.CommandInfo = CommandInfo;
41629             })(declares = app.declares || (app.declares = {}));
41630         })(app || (app = {}));
41631         var app;
41632         (function (app) {
41633             var services;
41634             (function (services) {
41635                 var APIEndPoint = (function () {
41636                     function APIEndPoint($resource) {
41637                         this.$resource = $resource;
41638                     }
41639                     APIEndPoint.prototype.resource = function () {
41640                         var getOption = {
41641                             method: 'GET',
41642                             isArray: false
41643                         };
41644                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
41645                         return this.$resource(apiUrl, {}, { getOption: getOption });
41646                     };
41647                     APIEndPoint.prototype.getOptionControlFile = function () {
41648                         return this.resource().query();
41649                     };
41650                     return APIEndPoint;
41651                 })();
41652                 services.APIEndPoint = APIEndPoint;
41653             })(services = app.services || (app.services = {}));
41654         })(app || (app = {}));
41655         var app;
41656         (function (app) {
41657             var services;
41658             (function (services) {
41659                 var MyModal = (function () {
41660                     function MyModal($uibModal) {
41661                         this.$uibModal = $uibModal;
41662                         this.modalOption = {
41663                             backdrop: true,
41664                             controller: null,
41665                             templateUrl: null,
41666                             size: null
41667                         };
41668                     }
41669                     MyModal.prototype.open = function (modalName) {
41670                         if (modalName === 'SelectCommand') {
41671                             this.modalOption.templateUrl = 'templates/select-command.html';
41672                             this.modalOption.size = 'lg';
41673                         }
41674                         return this.$uibModal.open(this.modalOption);
41675                     };
41676                     return MyModal;
41677                 })();
41678                 services.MyModal = MyModal;
41679             })(services = app.services || (app.services = {}));
41680         })(app || (app = {}));
41681         var app;
41682         (function (app) {
41683             var directives;
41684             (function (directives) {
41685                 var Command = (function () {
41686                     function Command() {
41687                         this.restrict = 'E';
41688                         this.replace = true;
41689                         this.scope = true;
41690                         this.controller = 'commandController';
41691                         this.controllerAs = 'ctrl';
41692                         this.bindToController = {
41693                             index: '=',
41694                             name: '=',
41695                             remove: '&',
41696                             list: '='
41697                         };
41698                         this.templateUrl = 'templates/command.html';
41699                     }
41700                     Command.Factory = function () {
41701                         var directive = function () {
41702                             return new Command();
41703                         };
41704                         directive.$inject = [];
41705                         return directive;
41706                     };
41707                     return Command;
41708                 })();
41709                 directives.Command = Command;
41710                 var CommandController = (function () {
41711                     function CommandController(APIEndPoint, $scope) {
41712                         this.APIEndPoint = APIEndPoint;
41713                         this.$scope = $scope;
41714                         var controller = this;
41715                         this.APIEndPoint
41716                             .getOptionControlFile()
41717                             .$promise
41718                             .then(function (result) {
41719                             controller.options = result;
41720                         });
41721                         this.files = ['a.file', 'b.file', 'c.file'];
41722                         this.heading = "[" + this.index + "]: dcdFilePring";
41723                         this.isOpen = true;
41724                         this.$scope.$on('close', function () {
41725                             controller.isOpen = false;
41726                         });
41727                     }
41728                     CommandController.prototype.submit = function () {
41729                         var params = {};
41730                         angular.forEach(this.options, function (option) {
41731                             var inputs = [];
41732                             angular.forEach(option.arg, function (arg) {
41733                                 if (arg.input) {
41734                                     inputs.push(arg.input);
41735                                 }
41736                             });
41737                             if (inputs.length > 0) {
41738                                 params[option.option] = inputs;
41739                             }
41740                         });
41741                         console.log(params);
41742                     };
41743                     CommandController.prototype.removeMySelf = function (index) {
41744                         this.remove()(index, this.list);
41745                     };
41746                     CommandController.$inject = ['APIEndPoint', '$scope'];
41747                     return CommandController;
41748                 })();
41749                 directives.CommandController = CommandController;
41750             })(directives = app.directives || (app.directives = {}));
41751         })(app || (app = {}));
41752         var app;
41753         (function (app) {
41754             var directives;
41755             (function (directives) {
41756                 var HeaderMenu = (function () {
41757                     function HeaderMenu() {
41758                         this.restrict = 'E';
41759                         this.replace = true;
41760                         this.templateUrl = 'templates/header-menu.html';
41761                     }
41762                     HeaderMenu.Factory = function () {
41763                         var directive = function () {
41764                             return new HeaderMenu();
41765                         };
41766                         return directive;
41767                     };
41768                     return HeaderMenu;
41769                 })();
41770                 directives.HeaderMenu = HeaderMenu;
41771             })(directives = app.directives || (app.directives = {}));
41772         })(app || (app = {}));
41773         var app;
41774         (function (app) {
41775             var directives;
41776             (function (directives) {
41777                 var Option = (function () {
41778                     function Option() {
41779                         this.restrict = 'E';
41780                         this.replace = true;
41781                         this.controller = 'optionController';
41782                         this.bindToController = {
41783                             info: '=',
41784                             files: '='
41785                         };
41786                         this.scope = true;
41787                         this.templateUrl = 'templates/option.html';
41788                         this.controllerAs = 'ctrl';
41789                     }
41790                     Option.Factory = function () {
41791                         var directive = function () {
41792                             return new Option();
41793                         };
41794                         directive.$inject = [];
41795                         return directive;
41796                     };
41797                     return Option;
41798                 })();
41799                 directives.Option = Option;
41800                 var OptionController = (function () {
41801                     function OptionController() {
41802                         var controller = this;
41803                         angular.forEach(controller.info.arg, function (arg) {
41804                             if (arg.initialValue) {
41805                                 if (arg.formType === 'number') {
41806                                     arg.input = parseInt(arg.initialValue);
41807                                 }
41808                                 else {
41809                                     arg.input = arg.initialValue;
41810                                 }
41811                             }
41812                         });
41813                     }
41814                     OptionController.$inject = [];
41815                     return OptionController;
41816                 })();
41817                 directives.OptionController = OptionController;
41818             })(directives = app.directives || (app.directives = {}));
41819         })(app || (app = {}));
41820         var app;
41821         (function (app) {
41822             var controllers;
41823             (function (controllers) {
41824                 var Execution = (function () {
41825                     function Execution(MyModal, $scope) {
41826                         this.MyModal = MyModal;
41827                         this.$scope = $scope;
41828                         this.commandInfoList = [];
41829                     }
41830                     ;
41831                     Execution.prototype.add = function () {
41832                         this.$scope.$broadcast('close');
41833                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
41834                     };
41835                     Execution.prototype.open = function () {
41836                         var result = this.MyModal.open('SelectCommand');
41837                         console.log(result);
41838                     };
41839                     Execution.prototype.remove = function (index, list) {
41840                         list.splice(index, 1);
41841                     };
41842                     Execution.prototype.close = function () {
41843                         console.log("close");
41844                     };
41845                     Execution.$inject = ['MyModal', '$scope'];
41846                     return Execution;
41847                 })();
41848                 controllers.Execution = Execution;
41849             })(controllers = app.controllers || (app.controllers = {}));
41850         })(app || (app = {}));
41851         var app;
41852         (function (app) {
41853             var controllers;
41854             (function (controllers) {
41855                 var Files = (function () {
41856                     function Files($scope) {
41857                         this.page = "ManageFiles";
41858                     }
41859                     Files.$inject = ['$scope'];
41860                     return Files;
41861                 })();
41862                 controllers.Files = Files;
41863             })(controllers = app.controllers || (app.controllers = {}));
41864         })(app || (app = {}));
41865         var app;
41866         (function (app) {
41867             var controllers;
41868             (function (controllers) {
41869                 var History = (function () {
41870                     function History($scope) {
41871                         this.page = "History";
41872                     }
41873                     History.$inject = ['$scope'];
41874                     return History;
41875                 })();
41876                 controllers.History = History;
41877             })(controllers = app.controllers || (app.controllers = {}));
41878         })(app || (app = {}));
41879         var app;
41880         (function (app) {
41881             'use strict';
41882             var appName = 'zephyr';
41883             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
41884             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
41885                 $urlRouterProvider.otherwise('/execution');
41886                 $locationProvider.html5Mode({
41887                     enabled: true,
41888                     requireBase: false
41889                 });
41890                 $stateProvider
41891                     .state('execution', {
41892                     url: '/execution',
41893                     templateUrl: 'templates/execution.html',
41894                     controller: 'executionController',
41895                     controllerAs: 'c'
41896                 })
41897                     .state('files', {
41898                     url: '/files',
41899                     templateUrl: 'templates/files.html',
41900                     controller: 'filesController',
41901                     controllerAs: 'c'
41902                 })
41903                     .state('history', {
41904                     url: '/history',
41905                     templateUrl: 'templates/history.html',
41906                     controller: 'historyController',
41907                     controllerAs: 'c'
41908                 });
41909             });
41910             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
41911             app.zephyr.service('MyModal', app.services.MyModal);
41912             app.zephyr.controller('executionController', app.controllers.Execution);
41913             app.zephyr.controller('filesController', app.controllers.Files);
41914             app.zephyr.controller('historyController', app.controllers.History);
41915             app.zephyr.controller('commandController', app.directives.CommandController);
41916             app.zephyr.controller('optionController', app.directives.OptionController);
41917             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
41918             app.zephyr.directive('command', app.directives.Command.Factory());
41919             app.zephyr.directive('option', app.directives.Option.Factory());
41920         })(app || (app = {}));
41921
41922
41923 /***/ },
41924 /* 11 */
41925 /***/ function(module, exports) {
41926
41927         var app;
41928         (function (app) {
41929             var declares;
41930             (function (declares) {
41931                 var CommandInfo = (function () {
41932                     function CommandInfo(name) {
41933                         this.name = name;
41934                     }
41935                     return CommandInfo;
41936                 })();
41937                 declares.CommandInfo = CommandInfo;
41938             })(declares = app.declares || (app.declares = {}));
41939         })(app || (app = {}));
41940         var app;
41941         (function (app) {
41942             var services;
41943             (function (services) {
41944                 var APIEndPoint = (function () {
41945                     function APIEndPoint($resource) {
41946                         this.$resource = $resource;
41947                     }
41948                     APIEndPoint.prototype.resource = function () {
41949                         var getOption = {
41950                             method: 'GET',
41951                             isArray: false
41952                         };
41953                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
41954                         return this.$resource(apiUrl, {}, { getOption: getOption });
41955                     };
41956                     APIEndPoint.prototype.getOptionControlFile = function () {
41957                         return this.resource().query();
41958                     };
41959                     return APIEndPoint;
41960                 })();
41961                 services.APIEndPoint = APIEndPoint;
41962             })(services = app.services || (app.services = {}));
41963         })(app || (app = {}));
41964         var app;
41965         (function (app) {
41966             var services;
41967             (function (services) {
41968                 var MyModal = (function () {
41969                     function MyModal($uibModal) {
41970                         this.$uibModal = $uibModal;
41971                         this.modalOption = {
41972                             backdrop: true,
41973                             controller: null,
41974                             templateUrl: null,
41975                             size: null
41976                         };
41977                     }
41978                     MyModal.prototype.open = function (modalName) {
41979                         if (modalName === 'SelectCommand') {
41980                             this.modalOption.templateUrl = 'templates/select-command.html';
41981                             this.modalOption.size = 'lg';
41982                         }
41983                         return this.$uibModal.open(this.modalOption);
41984                     };
41985                     return MyModal;
41986                 })();
41987                 services.MyModal = MyModal;
41988             })(services = app.services || (app.services = {}));
41989         })(app || (app = {}));
41990         var app;
41991         (function (app) {
41992             var directives;
41993             (function (directives) {
41994                 var Command = (function () {
41995                     function Command() {
41996                         this.restrict = 'E';
41997                         this.replace = true;
41998                         this.scope = true;
41999                         this.controller = 'commandController';
42000                         this.controllerAs = 'ctrl';
42001                         this.bindToController = {
42002                             index: '=',
42003                             name: '=',
42004                             remove: '&',
42005                             list: '='
42006                         };
42007                         this.templateUrl = 'templates/command.html';
42008                     }
42009                     Command.Factory = function () {
42010                         var directive = function () {
42011                             return new Command();
42012                         };
42013                         directive.$inject = [];
42014                         return directive;
42015                     };
42016                     return Command;
42017                 })();
42018                 directives.Command = Command;
42019                 var CommandController = (function () {
42020                     function CommandController(APIEndPoint, $scope) {
42021                         this.APIEndPoint = APIEndPoint;
42022                         this.$scope = $scope;
42023                         var controller = this;
42024                         this.APIEndPoint
42025                             .getOptionControlFile()
42026                             .$promise
42027                             .then(function (result) {
42028                             controller.options = result;
42029                         });
42030                         this.files = ['a.file', 'b.file', 'c.file'];
42031                         this.heading = "[" + this.index + "]: dcdFilePring";
42032                         this.isOpen = true;
42033                         this.$scope.$on('close', function () {
42034                             controller.isOpen = false;
42035                         });
42036                     }
42037                     CommandController.prototype.submit = function () {
42038                         var params = {};
42039                         angular.forEach(this.options, function (option) {
42040                             var inputs = [];
42041                             angular.forEach(option.arg, function (arg) {
42042                                 if (arg.input) {
42043                                     inputs.push(arg.input);
42044                                 }
42045                             });
42046                             if (inputs.length > 0) {
42047                                 params[option.option] = inputs;
42048                             }
42049                         });
42050                         console.log(params);
42051                     };
42052                     CommandController.prototype.removeMySelf = function (index) {
42053                         this.remove()(index, this.list);
42054                     };
42055                     CommandController.$inject = ['APIEndPoint', '$scope'];
42056                     return CommandController;
42057                 })();
42058                 directives.CommandController = CommandController;
42059             })(directives = app.directives || (app.directives = {}));
42060         })(app || (app = {}));
42061         var app;
42062         (function (app) {
42063             var directives;
42064             (function (directives) {
42065                 var HeaderMenu = (function () {
42066                     function HeaderMenu() {
42067                         this.restrict = 'E';
42068                         this.replace = true;
42069                         this.templateUrl = 'templates/header-menu.html';
42070                     }
42071                     HeaderMenu.Factory = function () {
42072                         var directive = function () {
42073                             return new HeaderMenu();
42074                         };
42075                         return directive;
42076                     };
42077                     return HeaderMenu;
42078                 })();
42079                 directives.HeaderMenu = HeaderMenu;
42080             })(directives = app.directives || (app.directives = {}));
42081         })(app || (app = {}));
42082         var app;
42083         (function (app) {
42084             var directives;
42085             (function (directives) {
42086                 var Option = (function () {
42087                     function Option() {
42088                         this.restrict = 'E';
42089                         this.replace = true;
42090                         this.controller = 'optionController';
42091                         this.bindToController = {
42092                             info: '=',
42093                             files: '='
42094                         };
42095                         this.scope = true;
42096                         this.templateUrl = 'templates/option.html';
42097                         this.controllerAs = 'ctrl';
42098                     }
42099                     Option.Factory = function () {
42100                         var directive = function () {
42101                             return new Option();
42102                         };
42103                         directive.$inject = [];
42104                         return directive;
42105                     };
42106                     return Option;
42107                 })();
42108                 directives.Option = Option;
42109                 var OptionController = (function () {
42110                     function OptionController() {
42111                         var controller = this;
42112                         angular.forEach(controller.info.arg, function (arg) {
42113                             if (arg.initialValue) {
42114                                 if (arg.formType === 'number') {
42115                                     arg.input = parseInt(arg.initialValue);
42116                                 }
42117                                 else {
42118                                     arg.input = arg.initialValue;
42119                                 }
42120                             }
42121                         });
42122                     }
42123                     OptionController.$inject = [];
42124                     return OptionController;
42125                 })();
42126                 directives.OptionController = OptionController;
42127             })(directives = app.directives || (app.directives = {}));
42128         })(app || (app = {}));
42129         var app;
42130         (function (app) {
42131             var controllers;
42132             (function (controllers) {
42133                 var Execution = (function () {
42134                     function Execution(MyModal, $scope) {
42135                         this.MyModal = MyModal;
42136                         this.$scope = $scope;
42137                         this.commandInfoList = [];
42138                     }
42139                     ;
42140                     Execution.prototype.add = function () {
42141                         this.$scope.$broadcast('close');
42142                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
42143                     };
42144                     Execution.prototype.open = function () {
42145                         var result = this.MyModal.open('SelectCommand');
42146                         console.log(result);
42147                     };
42148                     Execution.prototype.remove = function (index, list) {
42149                         list.splice(index, 1);
42150                     };
42151                     Execution.prototype.close = function () {
42152                         console.log("close");
42153                     };
42154                     Execution.$inject = ['MyModal', '$scope'];
42155                     return Execution;
42156                 })();
42157                 controllers.Execution = Execution;
42158             })(controllers = app.controllers || (app.controllers = {}));
42159         })(app || (app = {}));
42160         var app;
42161         (function (app) {
42162             var controllers;
42163             (function (controllers) {
42164                 var Files = (function () {
42165                     function Files($scope) {
42166                         this.page = "ManageFiles";
42167                     }
42168                     Files.$inject = ['$scope'];
42169                     return Files;
42170                 })();
42171                 controllers.Files = Files;
42172             })(controllers = app.controllers || (app.controllers = {}));
42173         })(app || (app = {}));
42174         var app;
42175         (function (app) {
42176             var controllers;
42177             (function (controllers) {
42178                 var History = (function () {
42179                     function History($scope) {
42180                         this.page = "History";
42181                     }
42182                     History.$inject = ['$scope'];
42183                     return History;
42184                 })();
42185                 controllers.History = History;
42186             })(controllers = app.controllers || (app.controllers = {}));
42187         })(app || (app = {}));
42188         var app;
42189         (function (app) {
42190             'use strict';
42191             var appName = 'zephyr';
42192             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42193             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42194                 $urlRouterProvider.otherwise('/execution');
42195                 $locationProvider.html5Mode({
42196                     enabled: true,
42197                     requireBase: false
42198                 });
42199                 $stateProvider
42200                     .state('execution', {
42201                     url: '/execution',
42202                     templateUrl: 'templates/execution.html',
42203                     controller: 'executionController',
42204                     controllerAs: 'c'
42205                 })
42206                     .state('files', {
42207                     url: '/files',
42208                     templateUrl: 'templates/files.html',
42209                     controller: 'filesController',
42210                     controllerAs: 'c'
42211                 })
42212                     .state('history', {
42213                     url: '/history',
42214                     templateUrl: 'templates/history.html',
42215                     controller: 'historyController',
42216                     controllerAs: 'c'
42217                 });
42218             });
42219             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42220             app.zephyr.service('MyModal', app.services.MyModal);
42221             app.zephyr.controller('executionController', app.controllers.Execution);
42222             app.zephyr.controller('filesController', app.controllers.Files);
42223             app.zephyr.controller('historyController', app.controllers.History);
42224             app.zephyr.controller('commandController', app.directives.CommandController);
42225             app.zephyr.controller('optionController', app.directives.OptionController);
42226             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42227             app.zephyr.directive('command', app.directives.Command.Factory());
42228             app.zephyr.directive('option', app.directives.Option.Factory());
42229         })(app || (app = {}));
42230
42231
42232 /***/ },
42233 /* 12 */
42234 /***/ function(module, exports) {
42235
42236         var app;
42237         (function (app) {
42238             var declares;
42239             (function (declares) {
42240                 var CommandInfo = (function () {
42241                     function CommandInfo(name) {
42242                         this.name = name;
42243                     }
42244                     return CommandInfo;
42245                 })();
42246                 declares.CommandInfo = CommandInfo;
42247             })(declares = app.declares || (app.declares = {}));
42248         })(app || (app = {}));
42249         var app;
42250         (function (app) {
42251             var services;
42252             (function (services) {
42253                 var APIEndPoint = (function () {
42254                     function APIEndPoint($resource) {
42255                         this.$resource = $resource;
42256                     }
42257                     APIEndPoint.prototype.resource = function () {
42258                         var getOption = {
42259                             method: 'GET',
42260                             isArray: false
42261                         };
42262                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
42263                         return this.$resource(apiUrl, {}, { getOption: getOption });
42264                     };
42265                     APIEndPoint.prototype.getOptionControlFile = function () {
42266                         return this.resource().query();
42267                     };
42268                     return APIEndPoint;
42269                 })();
42270                 services.APIEndPoint = APIEndPoint;
42271             })(services = app.services || (app.services = {}));
42272         })(app || (app = {}));
42273         var app;
42274         (function (app) {
42275             var services;
42276             (function (services) {
42277                 var MyModal = (function () {
42278                     function MyModal($uibModal) {
42279                         this.$uibModal = $uibModal;
42280                         this.modalOption = {
42281                             backdrop: true,
42282                             controller: null,
42283                             templateUrl: null,
42284                             size: null
42285                         };
42286                     }
42287                     MyModal.prototype.open = function (modalName) {
42288                         if (modalName === 'SelectCommand') {
42289                             this.modalOption.templateUrl = 'templates/select-command.html';
42290                             this.modalOption.size = 'lg';
42291                         }
42292                         return this.$uibModal.open(this.modalOption);
42293                     };
42294                     return MyModal;
42295                 })();
42296                 services.MyModal = MyModal;
42297             })(services = app.services || (app.services = {}));
42298         })(app || (app = {}));
42299         var app;
42300         (function (app) {
42301             var directives;
42302             (function (directives) {
42303                 var Command = (function () {
42304                     function Command() {
42305                         this.restrict = 'E';
42306                         this.replace = true;
42307                         this.scope = true;
42308                         this.controller = 'commandController';
42309                         this.controllerAs = 'ctrl';
42310                         this.bindToController = {
42311                             index: '=',
42312                             name: '=',
42313                             remove: '&',
42314                             list: '='
42315                         };
42316                         this.templateUrl = 'templates/command.html';
42317                     }
42318                     Command.Factory = function () {
42319                         var directive = function () {
42320                             return new Command();
42321                         };
42322                         directive.$inject = [];
42323                         return directive;
42324                     };
42325                     return Command;
42326                 })();
42327                 directives.Command = Command;
42328                 var CommandController = (function () {
42329                     function CommandController(APIEndPoint, $scope) {
42330                         this.APIEndPoint = APIEndPoint;
42331                         this.$scope = $scope;
42332                         var controller = this;
42333                         this.APIEndPoint
42334                             .getOptionControlFile()
42335                             .$promise
42336                             .then(function (result) {
42337                             controller.options = result;
42338                         });
42339                         this.files = ['a.file', 'b.file', 'c.file'];
42340                         this.heading = "[" + this.index + "]: dcdFilePring";
42341                         this.isOpen = true;
42342                         this.$scope.$on('close', function () {
42343                             controller.isOpen = false;
42344                         });
42345                     }
42346                     CommandController.prototype.submit = function () {
42347                         var params = {};
42348                         angular.forEach(this.options, function (option) {
42349                             var inputs = [];
42350                             angular.forEach(option.arg, function (arg) {
42351                                 if (arg.input) {
42352                                     inputs.push(arg.input);
42353                                 }
42354                             });
42355                             if (inputs.length > 0) {
42356                                 params[option.option] = inputs;
42357                             }
42358                         });
42359                         console.log(params);
42360                     };
42361                     CommandController.prototype.removeMySelf = function (index) {
42362                         this.remove()(index, this.list);
42363                     };
42364                     CommandController.$inject = ['APIEndPoint', '$scope'];
42365                     return CommandController;
42366                 })();
42367                 directives.CommandController = CommandController;
42368             })(directives = app.directives || (app.directives = {}));
42369         })(app || (app = {}));
42370         var app;
42371         (function (app) {
42372             var directives;
42373             (function (directives) {
42374                 var HeaderMenu = (function () {
42375                     function HeaderMenu() {
42376                         this.restrict = 'E';
42377                         this.replace = true;
42378                         this.templateUrl = 'templates/header-menu.html';
42379                     }
42380                     HeaderMenu.Factory = function () {
42381                         var directive = function () {
42382                             return new HeaderMenu();
42383                         };
42384                         return directive;
42385                     };
42386                     return HeaderMenu;
42387                 })();
42388                 directives.HeaderMenu = HeaderMenu;
42389             })(directives = app.directives || (app.directives = {}));
42390         })(app || (app = {}));
42391         var app;
42392         (function (app) {
42393             var directives;
42394             (function (directives) {
42395                 var Option = (function () {
42396                     function Option() {
42397                         this.restrict = 'E';
42398                         this.replace = true;
42399                         this.controller = 'optionController';
42400                         this.bindToController = {
42401                             info: '=',
42402                             files: '='
42403                         };
42404                         this.scope = true;
42405                         this.templateUrl = 'templates/option.html';
42406                         this.controllerAs = 'ctrl';
42407                     }
42408                     Option.Factory = function () {
42409                         var directive = function () {
42410                             return new Option();
42411                         };
42412                         directive.$inject = [];
42413                         return directive;
42414                     };
42415                     return Option;
42416                 })();
42417                 directives.Option = Option;
42418                 var OptionController = (function () {
42419                     function OptionController() {
42420                         var controller = this;
42421                         angular.forEach(controller.info.arg, function (arg) {
42422                             if (arg.initialValue) {
42423                                 if (arg.formType === 'number') {
42424                                     arg.input = parseInt(arg.initialValue);
42425                                 }
42426                                 else {
42427                                     arg.input = arg.initialValue;
42428                                 }
42429                             }
42430                         });
42431                     }
42432                     OptionController.$inject = [];
42433                     return OptionController;
42434                 })();
42435                 directives.OptionController = OptionController;
42436             })(directives = app.directives || (app.directives = {}));
42437         })(app || (app = {}));
42438         var app;
42439         (function (app) {
42440             var controllers;
42441             (function (controllers) {
42442                 var Execution = (function () {
42443                     function Execution(MyModal, $scope) {
42444                         this.MyModal = MyModal;
42445                         this.$scope = $scope;
42446                         this.commandInfoList = [];
42447                     }
42448                     ;
42449                     Execution.prototype.add = function () {
42450                         this.$scope.$broadcast('close');
42451                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
42452                     };
42453                     Execution.prototype.open = function () {
42454                         var result = this.MyModal.open('SelectCommand');
42455                         console.log(result);
42456                     };
42457                     Execution.prototype.remove = function (index, list) {
42458                         list.splice(index, 1);
42459                     };
42460                     Execution.prototype.close = function () {
42461                         console.log("close");
42462                     };
42463                     Execution.$inject = ['MyModal', '$scope'];
42464                     return Execution;
42465                 })();
42466                 controllers.Execution = Execution;
42467             })(controllers = app.controllers || (app.controllers = {}));
42468         })(app || (app = {}));
42469         var app;
42470         (function (app) {
42471             var controllers;
42472             (function (controllers) {
42473                 var Files = (function () {
42474                     function Files($scope) {
42475                         this.page = "ManageFiles";
42476                     }
42477                     Files.$inject = ['$scope'];
42478                     return Files;
42479                 })();
42480                 controllers.Files = Files;
42481             })(controllers = app.controllers || (app.controllers = {}));
42482         })(app || (app = {}));
42483         var app;
42484         (function (app) {
42485             var controllers;
42486             (function (controllers) {
42487                 var History = (function () {
42488                     function History($scope) {
42489                         this.page = "History";
42490                     }
42491                     History.$inject = ['$scope'];
42492                     return History;
42493                 })();
42494                 controllers.History = History;
42495             })(controllers = app.controllers || (app.controllers = {}));
42496         })(app || (app = {}));
42497         var app;
42498         (function (app) {
42499             'use strict';
42500             var appName = 'zephyr';
42501             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42502             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42503                 $urlRouterProvider.otherwise('/execution');
42504                 $locationProvider.html5Mode({
42505                     enabled: true,
42506                     requireBase: false
42507                 });
42508                 $stateProvider
42509                     .state('execution', {
42510                     url: '/execution',
42511                     templateUrl: 'templates/execution.html',
42512                     controller: 'executionController',
42513                     controllerAs: 'c'
42514                 })
42515                     .state('files', {
42516                     url: '/files',
42517                     templateUrl: 'templates/files.html',
42518                     controller: 'filesController',
42519                     controllerAs: 'c'
42520                 })
42521                     .state('history', {
42522                     url: '/history',
42523                     templateUrl: 'templates/history.html',
42524                     controller: 'historyController',
42525                     controllerAs: 'c'
42526                 });
42527             });
42528             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42529             app.zephyr.service('MyModal', app.services.MyModal);
42530             app.zephyr.controller('executionController', app.controllers.Execution);
42531             app.zephyr.controller('filesController', app.controllers.Files);
42532             app.zephyr.controller('historyController', app.controllers.History);
42533             app.zephyr.controller('commandController', app.directives.CommandController);
42534             app.zephyr.controller('optionController', app.directives.OptionController);
42535             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42536             app.zephyr.directive('command', app.directives.Command.Factory());
42537             app.zephyr.directive('option', app.directives.Option.Factory());
42538         })(app || (app = {}));
42539
42540
42541 /***/ },
42542 /* 13 */
42543 /***/ function(module, exports) {
42544
42545         var app;
42546         (function (app) {
42547             var declares;
42548             (function (declares) {
42549                 var CommandInfo = (function () {
42550                     function CommandInfo(name) {
42551                         this.name = name;
42552                     }
42553                     return CommandInfo;
42554                 })();
42555                 declares.CommandInfo = CommandInfo;
42556             })(declares = app.declares || (app.declares = {}));
42557         })(app || (app = {}));
42558         var app;
42559         (function (app) {
42560             var services;
42561             (function (services) {
42562                 var APIEndPoint = (function () {
42563                     function APIEndPoint($resource) {
42564                         this.$resource = $resource;
42565                     }
42566                     APIEndPoint.prototype.resource = function () {
42567                         var getOption = {
42568                             method: 'GET',
42569                             isArray: false
42570                         };
42571                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
42572                         return this.$resource(apiUrl, {}, { getOption: getOption });
42573                     };
42574                     APIEndPoint.prototype.getOptionControlFile = function () {
42575                         return this.resource().query();
42576                     };
42577                     return APIEndPoint;
42578                 })();
42579                 services.APIEndPoint = APIEndPoint;
42580             })(services = app.services || (app.services = {}));
42581         })(app || (app = {}));
42582         var app;
42583         (function (app) {
42584             var services;
42585             (function (services) {
42586                 var MyModal = (function () {
42587                     function MyModal($uibModal) {
42588                         this.$uibModal = $uibModal;
42589                         this.modalOption = {
42590                             backdrop: true,
42591                             controller: null,
42592                             templateUrl: null,
42593                             size: null
42594                         };
42595                     }
42596                     MyModal.prototype.open = function (modalName) {
42597                         if (modalName === 'SelectCommand') {
42598                             this.modalOption.templateUrl = 'templates/select-command.html';
42599                             this.modalOption.size = 'lg';
42600                         }
42601                         return this.$uibModal.open(this.modalOption);
42602                     };
42603                     return MyModal;
42604                 })();
42605                 services.MyModal = MyModal;
42606             })(services = app.services || (app.services = {}));
42607         })(app || (app = {}));
42608         var app;
42609         (function (app) {
42610             var directives;
42611             (function (directives) {
42612                 var Command = (function () {
42613                     function Command() {
42614                         this.restrict = 'E';
42615                         this.replace = true;
42616                         this.scope = true;
42617                         this.controller = 'commandController';
42618                         this.controllerAs = 'ctrl';
42619                         this.bindToController = {
42620                             index: '=',
42621                             name: '=',
42622                             remove: '&',
42623                             list: '='
42624                         };
42625                         this.templateUrl = 'templates/command.html';
42626                     }
42627                     Command.Factory = function () {
42628                         var directive = function () {
42629                             return new Command();
42630                         };
42631                         directive.$inject = [];
42632                         return directive;
42633                     };
42634                     return Command;
42635                 })();
42636                 directives.Command = Command;
42637                 var CommandController = (function () {
42638                     function CommandController(APIEndPoint, $scope) {
42639                         this.APIEndPoint = APIEndPoint;
42640                         this.$scope = $scope;
42641                         var controller = this;
42642                         this.APIEndPoint
42643                             .getOptionControlFile()
42644                             .$promise
42645                             .then(function (result) {
42646                             controller.options = result;
42647                         });
42648                         this.files = ['a.file', 'b.file', 'c.file'];
42649                         this.heading = "[" + this.index + "]: dcdFilePring";
42650                         this.isOpen = true;
42651                         this.$scope.$on('close', function () {
42652                             controller.isOpen = false;
42653                         });
42654                     }
42655                     CommandController.prototype.submit = function () {
42656                         var params = {};
42657                         angular.forEach(this.options, function (option) {
42658                             var inputs = [];
42659                             angular.forEach(option.arg, function (arg) {
42660                                 if (arg.input) {
42661                                     inputs.push(arg.input);
42662                                 }
42663                             });
42664                             if (inputs.length > 0) {
42665                                 params[option.option] = inputs;
42666                             }
42667                         });
42668                         console.log(params);
42669                     };
42670                     CommandController.prototype.removeMySelf = function (index) {
42671                         this.remove()(index, this.list);
42672                     };
42673                     CommandController.$inject = ['APIEndPoint', '$scope'];
42674                     return CommandController;
42675                 })();
42676                 directives.CommandController = CommandController;
42677             })(directives = app.directives || (app.directives = {}));
42678         })(app || (app = {}));
42679         var app;
42680         (function (app) {
42681             var directives;
42682             (function (directives) {
42683                 var HeaderMenu = (function () {
42684                     function HeaderMenu() {
42685                         this.restrict = 'E';
42686                         this.replace = true;
42687                         this.templateUrl = 'templates/header-menu.html';
42688                     }
42689                     HeaderMenu.Factory = function () {
42690                         var directive = function () {
42691                             return new HeaderMenu();
42692                         };
42693                         return directive;
42694                     };
42695                     return HeaderMenu;
42696                 })();
42697                 directives.HeaderMenu = HeaderMenu;
42698             })(directives = app.directives || (app.directives = {}));
42699         })(app || (app = {}));
42700         var app;
42701         (function (app) {
42702             var directives;
42703             (function (directives) {
42704                 var Option = (function () {
42705                     function Option() {
42706                         this.restrict = 'E';
42707                         this.replace = true;
42708                         this.controller = 'optionController';
42709                         this.bindToController = {
42710                             info: '=',
42711                             files: '='
42712                         };
42713                         this.scope = true;
42714                         this.templateUrl = 'templates/option.html';
42715                         this.controllerAs = 'ctrl';
42716                     }
42717                     Option.Factory = function () {
42718                         var directive = function () {
42719                             return new Option();
42720                         };
42721                         directive.$inject = [];
42722                         return directive;
42723                     };
42724                     return Option;
42725                 })();
42726                 directives.Option = Option;
42727                 var OptionController = (function () {
42728                     function OptionController() {
42729                         var controller = this;
42730                         angular.forEach(controller.info.arg, function (arg) {
42731                             if (arg.initialValue) {
42732                                 if (arg.formType === 'number') {
42733                                     arg.input = parseInt(arg.initialValue);
42734                                 }
42735                                 else {
42736                                     arg.input = arg.initialValue;
42737                                 }
42738                             }
42739                         });
42740                     }
42741                     OptionController.$inject = [];
42742                     return OptionController;
42743                 })();
42744                 directives.OptionController = OptionController;
42745             })(directives = app.directives || (app.directives = {}));
42746         })(app || (app = {}));
42747         var app;
42748         (function (app) {
42749             var controllers;
42750             (function (controllers) {
42751                 var Execution = (function () {
42752                     function Execution(MyModal, $scope) {
42753                         this.MyModal = MyModal;
42754                         this.$scope = $scope;
42755                         this.commandInfoList = [];
42756                     }
42757                     ;
42758                     Execution.prototype.add = function () {
42759                         this.$scope.$broadcast('close');
42760                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
42761                     };
42762                     Execution.prototype.open = function () {
42763                         var result = this.MyModal.open('SelectCommand');
42764                         console.log(result);
42765                     };
42766                     Execution.prototype.remove = function (index, list) {
42767                         list.splice(index, 1);
42768                     };
42769                     Execution.prototype.close = function () {
42770                         console.log("close");
42771                     };
42772                     Execution.$inject = ['MyModal', '$scope'];
42773                     return Execution;
42774                 })();
42775                 controllers.Execution = Execution;
42776             })(controllers = app.controllers || (app.controllers = {}));
42777         })(app || (app = {}));
42778         var app;
42779         (function (app) {
42780             var controllers;
42781             (function (controllers) {
42782                 var Files = (function () {
42783                     function Files($scope) {
42784                         this.page = "ManageFiles";
42785                     }
42786                     Files.$inject = ['$scope'];
42787                     return Files;
42788                 })();
42789                 controllers.Files = Files;
42790             })(controllers = app.controllers || (app.controllers = {}));
42791         })(app || (app = {}));
42792         var app;
42793         (function (app) {
42794             var controllers;
42795             (function (controllers) {
42796                 var History = (function () {
42797                     function History($scope) {
42798                         this.page = "History";
42799                     }
42800                     History.$inject = ['$scope'];
42801                     return History;
42802                 })();
42803                 controllers.History = History;
42804             })(controllers = app.controllers || (app.controllers = {}));
42805         })(app || (app = {}));
42806         var app;
42807         (function (app) {
42808             'use strict';
42809             var appName = 'zephyr';
42810             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
42811             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
42812                 $urlRouterProvider.otherwise('/execution');
42813                 $locationProvider.html5Mode({
42814                     enabled: true,
42815                     requireBase: false
42816                 });
42817                 $stateProvider
42818                     .state('execution', {
42819                     url: '/execution',
42820                     templateUrl: 'templates/execution.html',
42821                     controller: 'executionController',
42822                     controllerAs: 'c'
42823                 })
42824                     .state('files', {
42825                     url: '/files',
42826                     templateUrl: 'templates/files.html',
42827                     controller: 'filesController',
42828                     controllerAs: 'c'
42829                 })
42830                     .state('history', {
42831                     url: '/history',
42832                     templateUrl: 'templates/history.html',
42833                     controller: 'historyController',
42834                     controllerAs: 'c'
42835                 });
42836             });
42837             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
42838             app.zephyr.service('MyModal', app.services.MyModal);
42839             app.zephyr.controller('executionController', app.controllers.Execution);
42840             app.zephyr.controller('filesController', app.controllers.Files);
42841             app.zephyr.controller('historyController', app.controllers.History);
42842             app.zephyr.controller('commandController', app.directives.CommandController);
42843             app.zephyr.controller('optionController', app.directives.OptionController);
42844             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
42845             app.zephyr.directive('command', app.directives.Command.Factory());
42846             app.zephyr.directive('option', app.directives.Option.Factory());
42847         })(app || (app = {}));
42848
42849
42850 /***/ },
42851 /* 14 */
42852 /***/ function(module, exports) {
42853
42854         var app;
42855         (function (app) {
42856             var declares;
42857             (function (declares) {
42858                 var CommandInfo = (function () {
42859                     function CommandInfo(name) {
42860                         this.name = name;
42861                     }
42862                     return CommandInfo;
42863                 })();
42864                 declares.CommandInfo = CommandInfo;
42865             })(declares = app.declares || (app.declares = {}));
42866         })(app || (app = {}));
42867         var app;
42868         (function (app) {
42869             var services;
42870             (function (services) {
42871                 var APIEndPoint = (function () {
42872                     function APIEndPoint($resource) {
42873                         this.$resource = $resource;
42874                     }
42875                     APIEndPoint.prototype.resource = function () {
42876                         var getOption = {
42877                             method: 'GET',
42878                             isArray: false
42879                         };
42880                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
42881                         return this.$resource(apiUrl, {}, { getOption: getOption });
42882                     };
42883                     APIEndPoint.prototype.getOptionControlFile = function () {
42884                         return this.resource().query();
42885                     };
42886                     return APIEndPoint;
42887                 })();
42888                 services.APIEndPoint = APIEndPoint;
42889             })(services = app.services || (app.services = {}));
42890         })(app || (app = {}));
42891         var app;
42892         (function (app) {
42893             var services;
42894             (function (services) {
42895                 var MyModal = (function () {
42896                     function MyModal($uibModal) {
42897                         this.$uibModal = $uibModal;
42898                         this.modalOption = {
42899                             backdrop: true,
42900                             controller: null,
42901                             templateUrl: null,
42902                             size: null
42903                         };
42904                     }
42905                     MyModal.prototype.open = function (modalName) {
42906                         if (modalName === 'SelectCommand') {
42907                             this.modalOption.templateUrl = 'templates/select-command.html';
42908                             this.modalOption.size = 'lg';
42909                         }
42910                         return this.$uibModal.open(this.modalOption);
42911                     };
42912                     return MyModal;
42913                 })();
42914                 services.MyModal = MyModal;
42915             })(services = app.services || (app.services = {}));
42916         })(app || (app = {}));
42917         var app;
42918         (function (app) {
42919             var directives;
42920             (function (directives) {
42921                 var Command = (function () {
42922                     function Command() {
42923                         this.restrict = 'E';
42924                         this.replace = true;
42925                         this.scope = true;
42926                         this.controller = 'commandController';
42927                         this.controllerAs = 'ctrl';
42928                         this.bindToController = {
42929                             index: '=',
42930                             name: '=',
42931                             remove: '&',
42932                             list: '='
42933                         };
42934                         this.templateUrl = 'templates/command.html';
42935                     }
42936                     Command.Factory = function () {
42937                         var directive = function () {
42938                             return new Command();
42939                         };
42940                         directive.$inject = [];
42941                         return directive;
42942                     };
42943                     return Command;
42944                 })();
42945                 directives.Command = Command;
42946                 var CommandController = (function () {
42947                     function CommandController(APIEndPoint, $scope) {
42948                         this.APIEndPoint = APIEndPoint;
42949                         this.$scope = $scope;
42950                         var controller = this;
42951                         this.APIEndPoint
42952                             .getOptionControlFile()
42953                             .$promise
42954                             .then(function (result) {
42955                             controller.options = result;
42956                         });
42957                         this.files = ['a.file', 'b.file', 'c.file'];
42958                         this.heading = "[" + this.index + "]: dcdFilePring";
42959                         this.isOpen = true;
42960                         this.$scope.$on('close', function () {
42961                             controller.isOpen = false;
42962                         });
42963                     }
42964                     CommandController.prototype.submit = function () {
42965                         var params = {};
42966                         angular.forEach(this.options, function (option) {
42967                             var inputs = [];
42968                             angular.forEach(option.arg, function (arg) {
42969                                 if (arg.input) {
42970                                     inputs.push(arg.input);
42971                                 }
42972                             });
42973                             if (inputs.length > 0) {
42974                                 params[option.option] = inputs;
42975                             }
42976                         });
42977                         console.log(params);
42978                     };
42979                     CommandController.prototype.removeMySelf = function (index) {
42980                         this.remove()(index, this.list);
42981                     };
42982                     CommandController.$inject = ['APIEndPoint', '$scope'];
42983                     return CommandController;
42984                 })();
42985                 directives.CommandController = CommandController;
42986             })(directives = app.directives || (app.directives = {}));
42987         })(app || (app = {}));
42988         var app;
42989         (function (app) {
42990             var directives;
42991             (function (directives) {
42992                 var HeaderMenu = (function () {
42993                     function HeaderMenu() {
42994                         this.restrict = 'E';
42995                         this.replace = true;
42996                         this.templateUrl = 'templates/header-menu.html';
42997                     }
42998                     HeaderMenu.Factory = function () {
42999                         var directive = function () {
43000                             return new HeaderMenu();
43001                         };
43002                         return directive;
43003                     };
43004                     return HeaderMenu;
43005                 })();
43006                 directives.HeaderMenu = HeaderMenu;
43007             })(directives = app.directives || (app.directives = {}));
43008         })(app || (app = {}));
43009         var app;
43010         (function (app) {
43011             var directives;
43012             (function (directives) {
43013                 var Option = (function () {
43014                     function Option() {
43015                         this.restrict = 'E';
43016                         this.replace = true;
43017                         this.controller = 'optionController';
43018                         this.bindToController = {
43019                             info: '=',
43020                             files: '='
43021                         };
43022                         this.scope = true;
43023                         this.templateUrl = 'templates/option.html';
43024                         this.controllerAs = 'ctrl';
43025                     }
43026                     Option.Factory = function () {
43027                         var directive = function () {
43028                             return new Option();
43029                         };
43030                         directive.$inject = [];
43031                         return directive;
43032                     };
43033                     return Option;
43034                 })();
43035                 directives.Option = Option;
43036                 var OptionController = (function () {
43037                     function OptionController() {
43038                         var controller = this;
43039                         angular.forEach(controller.info.arg, function (arg) {
43040                             if (arg.initialValue) {
43041                                 if (arg.formType === 'number') {
43042                                     arg.input = parseInt(arg.initialValue);
43043                                 }
43044                                 else {
43045                                     arg.input = arg.initialValue;
43046                                 }
43047                             }
43048                         });
43049                     }
43050                     OptionController.$inject = [];
43051                     return OptionController;
43052                 })();
43053                 directives.OptionController = OptionController;
43054             })(directives = app.directives || (app.directives = {}));
43055         })(app || (app = {}));
43056         var app;
43057         (function (app) {
43058             var controllers;
43059             (function (controllers) {
43060                 var Execution = (function () {
43061                     function Execution(MyModal, $scope) {
43062                         this.MyModal = MyModal;
43063                         this.$scope = $scope;
43064                         this.commandInfoList = [];
43065                     }
43066                     ;
43067                     Execution.prototype.add = function () {
43068                         this.$scope.$broadcast('close');
43069                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
43070                     };
43071                     Execution.prototype.open = function () {
43072                         var result = this.MyModal.open('SelectCommand');
43073                         console.log(result);
43074                     };
43075                     Execution.prototype.remove = function (index, list) {
43076                         list.splice(index, 1);
43077                     };
43078                     Execution.prototype.close = function () {
43079                         console.log("close");
43080                     };
43081                     Execution.$inject = ['MyModal', '$scope'];
43082                     return Execution;
43083                 })();
43084                 controllers.Execution = Execution;
43085             })(controllers = app.controllers || (app.controllers = {}));
43086         })(app || (app = {}));
43087         var app;
43088         (function (app) {
43089             var controllers;
43090             (function (controllers) {
43091                 var Files = (function () {
43092                     function Files($scope) {
43093                         this.page = "ManageFiles";
43094                     }
43095                     Files.$inject = ['$scope'];
43096                     return Files;
43097                 })();
43098                 controllers.Files = Files;
43099             })(controllers = app.controllers || (app.controllers = {}));
43100         })(app || (app = {}));
43101         var app;
43102         (function (app) {
43103             var controllers;
43104             (function (controllers) {
43105                 var History = (function () {
43106                     function History($scope) {
43107                         this.page = "History";
43108                     }
43109                     History.$inject = ['$scope'];
43110                     return History;
43111                 })();
43112                 controllers.History = History;
43113             })(controllers = app.controllers || (app.controllers = {}));
43114         })(app || (app = {}));
43115         var app;
43116         (function (app) {
43117             'use strict';
43118             var appName = 'zephyr';
43119             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43120             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43121                 $urlRouterProvider.otherwise('/execution');
43122                 $locationProvider.html5Mode({
43123                     enabled: true,
43124                     requireBase: false
43125                 });
43126                 $stateProvider
43127                     .state('execution', {
43128                     url: '/execution',
43129                     templateUrl: 'templates/execution.html',
43130                     controller: 'executionController',
43131                     controllerAs: 'c'
43132                 })
43133                     .state('files', {
43134                     url: '/files',
43135                     templateUrl: 'templates/files.html',
43136                     controller: 'filesController',
43137                     controllerAs: 'c'
43138                 })
43139                     .state('history', {
43140                     url: '/history',
43141                     templateUrl: 'templates/history.html',
43142                     controller: 'historyController',
43143                     controllerAs: 'c'
43144                 });
43145             });
43146             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43147             app.zephyr.service('MyModal', app.services.MyModal);
43148             app.zephyr.controller('executionController', app.controllers.Execution);
43149             app.zephyr.controller('filesController', app.controllers.Files);
43150             app.zephyr.controller('historyController', app.controllers.History);
43151             app.zephyr.controller('commandController', app.directives.CommandController);
43152             app.zephyr.controller('optionController', app.directives.OptionController);
43153             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43154             app.zephyr.directive('command', app.directives.Command.Factory());
43155             app.zephyr.directive('option', app.directives.Option.Factory());
43156         })(app || (app = {}));
43157
43158
43159 /***/ },
43160 /* 15 */
43161 /***/ function(module, exports) {
43162
43163         var app;
43164         (function (app) {
43165             var declares;
43166             (function (declares) {
43167                 var CommandInfo = (function () {
43168                     function CommandInfo(name) {
43169                         this.name = name;
43170                     }
43171                     return CommandInfo;
43172                 })();
43173                 declares.CommandInfo = CommandInfo;
43174             })(declares = app.declares || (app.declares = {}));
43175         })(app || (app = {}));
43176         var app;
43177         (function (app) {
43178             var services;
43179             (function (services) {
43180                 var APIEndPoint = (function () {
43181                     function APIEndPoint($resource) {
43182                         this.$resource = $resource;
43183                     }
43184                     APIEndPoint.prototype.resource = function () {
43185                         var getOption = {
43186                             method: 'GET',
43187                             isArray: false
43188                         };
43189                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
43190                         return this.$resource(apiUrl, {}, { getOption: getOption });
43191                     };
43192                     APIEndPoint.prototype.getOptionControlFile = function () {
43193                         return this.resource().query();
43194                     };
43195                     return APIEndPoint;
43196                 })();
43197                 services.APIEndPoint = APIEndPoint;
43198             })(services = app.services || (app.services = {}));
43199         })(app || (app = {}));
43200         var app;
43201         (function (app) {
43202             var services;
43203             (function (services) {
43204                 var MyModal = (function () {
43205                     function MyModal($uibModal) {
43206                         this.$uibModal = $uibModal;
43207                         this.modalOption = {
43208                             backdrop: true,
43209                             controller: null,
43210                             templateUrl: null,
43211                             size: null
43212                         };
43213                     }
43214                     MyModal.prototype.open = function (modalName) {
43215                         if (modalName === 'SelectCommand') {
43216                             this.modalOption.templateUrl = 'templates/select-command.html';
43217                             this.modalOption.size = 'lg';
43218                         }
43219                         return this.$uibModal.open(this.modalOption);
43220                     };
43221                     return MyModal;
43222                 })();
43223                 services.MyModal = MyModal;
43224             })(services = app.services || (app.services = {}));
43225         })(app || (app = {}));
43226         var app;
43227         (function (app) {
43228             var directives;
43229             (function (directives) {
43230                 var Command = (function () {
43231                     function Command() {
43232                         this.restrict = 'E';
43233                         this.replace = true;
43234                         this.scope = true;
43235                         this.controller = 'commandController';
43236                         this.controllerAs = 'ctrl';
43237                         this.bindToController = {
43238                             index: '=',
43239                             name: '=',
43240                             remove: '&',
43241                             list: '='
43242                         };
43243                         this.templateUrl = 'templates/command.html';
43244                     }
43245                     Command.Factory = function () {
43246                         var directive = function () {
43247                             return new Command();
43248                         };
43249                         directive.$inject = [];
43250                         return directive;
43251                     };
43252                     return Command;
43253                 })();
43254                 directives.Command = Command;
43255                 var CommandController = (function () {
43256                     function CommandController(APIEndPoint, $scope) {
43257                         this.APIEndPoint = APIEndPoint;
43258                         this.$scope = $scope;
43259                         var controller = this;
43260                         this.APIEndPoint
43261                             .getOptionControlFile()
43262                             .$promise
43263                             .then(function (result) {
43264                             controller.options = result;
43265                         });
43266                         this.files = ['a.file', 'b.file', 'c.file'];
43267                         this.heading = "[" + this.index + "]: dcdFilePring";
43268                         this.isOpen = true;
43269                         this.$scope.$on('close', function () {
43270                             controller.isOpen = false;
43271                         });
43272                     }
43273                     CommandController.prototype.submit = function () {
43274                         var params = {};
43275                         angular.forEach(this.options, function (option) {
43276                             var inputs = [];
43277                             angular.forEach(option.arg, function (arg) {
43278                                 if (arg.input) {
43279                                     inputs.push(arg.input);
43280                                 }
43281                             });
43282                             if (inputs.length > 0) {
43283                                 params[option.option] = inputs;
43284                             }
43285                         });
43286                         console.log(params);
43287                     };
43288                     CommandController.prototype.removeMySelf = function (index) {
43289                         this.remove()(index, this.list);
43290                     };
43291                     CommandController.$inject = ['APIEndPoint', '$scope'];
43292                     return CommandController;
43293                 })();
43294                 directives.CommandController = CommandController;
43295             })(directives = app.directives || (app.directives = {}));
43296         })(app || (app = {}));
43297         var app;
43298         (function (app) {
43299             var directives;
43300             (function (directives) {
43301                 var HeaderMenu = (function () {
43302                     function HeaderMenu() {
43303                         this.restrict = 'E';
43304                         this.replace = true;
43305                         this.templateUrl = 'templates/header-menu.html';
43306                     }
43307                     HeaderMenu.Factory = function () {
43308                         var directive = function () {
43309                             return new HeaderMenu();
43310                         };
43311                         return directive;
43312                     };
43313                     return HeaderMenu;
43314                 })();
43315                 directives.HeaderMenu = HeaderMenu;
43316             })(directives = app.directives || (app.directives = {}));
43317         })(app || (app = {}));
43318         var app;
43319         (function (app) {
43320             var directives;
43321             (function (directives) {
43322                 var Option = (function () {
43323                     function Option() {
43324                         this.restrict = 'E';
43325                         this.replace = true;
43326                         this.controller = 'optionController';
43327                         this.bindToController = {
43328                             info: '=',
43329                             files: '='
43330                         };
43331                         this.scope = true;
43332                         this.templateUrl = 'templates/option.html';
43333                         this.controllerAs = 'ctrl';
43334                     }
43335                     Option.Factory = function () {
43336                         var directive = function () {
43337                             return new Option();
43338                         };
43339                         directive.$inject = [];
43340                         return directive;
43341                     };
43342                     return Option;
43343                 })();
43344                 directives.Option = Option;
43345                 var OptionController = (function () {
43346                     function OptionController() {
43347                         var controller = this;
43348                         angular.forEach(controller.info.arg, function (arg) {
43349                             if (arg.initialValue) {
43350                                 if (arg.formType === 'number') {
43351                                     arg.input = parseInt(arg.initialValue);
43352                                 }
43353                                 else {
43354                                     arg.input = arg.initialValue;
43355                                 }
43356                             }
43357                         });
43358                     }
43359                     OptionController.$inject = [];
43360                     return OptionController;
43361                 })();
43362                 directives.OptionController = OptionController;
43363             })(directives = app.directives || (app.directives = {}));
43364         })(app || (app = {}));
43365         var app;
43366         (function (app) {
43367             var controllers;
43368             (function (controllers) {
43369                 var Execution = (function () {
43370                     function Execution(MyModal, $scope) {
43371                         this.MyModal = MyModal;
43372                         this.$scope = $scope;
43373                         this.commandInfoList = [];
43374                     }
43375                     ;
43376                     Execution.prototype.add = function () {
43377                         this.$scope.$broadcast('close');
43378                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
43379                     };
43380                     Execution.prototype.open = function () {
43381                         var result = this.MyModal.open('SelectCommand');
43382                         console.log(result);
43383                     };
43384                     Execution.prototype.remove = function (index, list) {
43385                         list.splice(index, 1);
43386                     };
43387                     Execution.prototype.close = function () {
43388                         console.log("close");
43389                     };
43390                     Execution.$inject = ['MyModal', '$scope'];
43391                     return Execution;
43392                 })();
43393                 controllers.Execution = Execution;
43394             })(controllers = app.controllers || (app.controllers = {}));
43395         })(app || (app = {}));
43396         var app;
43397         (function (app) {
43398             var controllers;
43399             (function (controllers) {
43400                 var Files = (function () {
43401                     function Files($scope) {
43402                         this.page = "ManageFiles";
43403                     }
43404                     Files.$inject = ['$scope'];
43405                     return Files;
43406                 })();
43407                 controllers.Files = Files;
43408             })(controllers = app.controllers || (app.controllers = {}));
43409         })(app || (app = {}));
43410         var app;
43411         (function (app) {
43412             var controllers;
43413             (function (controllers) {
43414                 var History = (function () {
43415                     function History($scope) {
43416                         this.page = "History";
43417                     }
43418                     History.$inject = ['$scope'];
43419                     return History;
43420                 })();
43421                 controllers.History = History;
43422             })(controllers = app.controllers || (app.controllers = {}));
43423         })(app || (app = {}));
43424         var app;
43425         (function (app) {
43426             'use strict';
43427             var appName = 'zephyr';
43428             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43429             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43430                 $urlRouterProvider.otherwise('/execution');
43431                 $locationProvider.html5Mode({
43432                     enabled: true,
43433                     requireBase: false
43434                 });
43435                 $stateProvider
43436                     .state('execution', {
43437                     url: '/execution',
43438                     templateUrl: 'templates/execution.html',
43439                     controller: 'executionController',
43440                     controllerAs: 'c'
43441                 })
43442                     .state('files', {
43443                     url: '/files',
43444                     templateUrl: 'templates/files.html',
43445                     controller: 'filesController',
43446                     controllerAs: 'c'
43447                 })
43448                     .state('history', {
43449                     url: '/history',
43450                     templateUrl: 'templates/history.html',
43451                     controller: 'historyController',
43452                     controllerAs: 'c'
43453                 });
43454             });
43455             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43456             app.zephyr.service('MyModal', app.services.MyModal);
43457             app.zephyr.controller('executionController', app.controllers.Execution);
43458             app.zephyr.controller('filesController', app.controllers.Files);
43459             app.zephyr.controller('historyController', app.controllers.History);
43460             app.zephyr.controller('commandController', app.directives.CommandController);
43461             app.zephyr.controller('optionController', app.directives.OptionController);
43462             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43463             app.zephyr.directive('command', app.directives.Command.Factory());
43464             app.zephyr.directive('option', app.directives.Option.Factory());
43465         })(app || (app = {}));
43466
43467
43468 /***/ },
43469 /* 16 */
43470 /***/ function(module, exports) {
43471
43472         var app;
43473         (function (app) {
43474             var declares;
43475             (function (declares) {
43476                 var CommandInfo = (function () {
43477                     function CommandInfo(name) {
43478                         this.name = name;
43479                     }
43480                     return CommandInfo;
43481                 })();
43482                 declares.CommandInfo = CommandInfo;
43483             })(declares = app.declares || (app.declares = {}));
43484         })(app || (app = {}));
43485         var app;
43486         (function (app) {
43487             var services;
43488             (function (services) {
43489                 var APIEndPoint = (function () {
43490                     function APIEndPoint($resource) {
43491                         this.$resource = $resource;
43492                     }
43493                     APIEndPoint.prototype.resource = function () {
43494                         var getOption = {
43495                             method: 'GET',
43496                             isArray: false
43497                         };
43498                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
43499                         return this.$resource(apiUrl, {}, { getOption: getOption });
43500                     };
43501                     APIEndPoint.prototype.getOptionControlFile = function () {
43502                         return this.resource().query();
43503                     };
43504                     return APIEndPoint;
43505                 })();
43506                 services.APIEndPoint = APIEndPoint;
43507             })(services = app.services || (app.services = {}));
43508         })(app || (app = {}));
43509         var app;
43510         (function (app) {
43511             var services;
43512             (function (services) {
43513                 var MyModal = (function () {
43514                     function MyModal($uibModal) {
43515                         this.$uibModal = $uibModal;
43516                         this.modalOption = {
43517                             backdrop: true,
43518                             controller: null,
43519                             templateUrl: null,
43520                             size: null
43521                         };
43522                     }
43523                     MyModal.prototype.open = function (modalName) {
43524                         if (modalName === 'SelectCommand') {
43525                             this.modalOption.templateUrl = 'templates/select-command.html';
43526                             this.modalOption.size = 'lg';
43527                         }
43528                         return this.$uibModal.open(this.modalOption);
43529                     };
43530                     return MyModal;
43531                 })();
43532                 services.MyModal = MyModal;
43533             })(services = app.services || (app.services = {}));
43534         })(app || (app = {}));
43535         var app;
43536         (function (app) {
43537             var directives;
43538             (function (directives) {
43539                 var Command = (function () {
43540                     function Command() {
43541                         this.restrict = 'E';
43542                         this.replace = true;
43543                         this.scope = true;
43544                         this.controller = 'commandController';
43545                         this.controllerAs = 'ctrl';
43546                         this.bindToController = {
43547                             index: '=',
43548                             name: '=',
43549                             remove: '&',
43550                             list: '='
43551                         };
43552                         this.templateUrl = 'templates/command.html';
43553                     }
43554                     Command.Factory = function () {
43555                         var directive = function () {
43556                             return new Command();
43557                         };
43558                         directive.$inject = [];
43559                         return directive;
43560                     };
43561                     return Command;
43562                 })();
43563                 directives.Command = Command;
43564                 var CommandController = (function () {
43565                     function CommandController(APIEndPoint, $scope) {
43566                         this.APIEndPoint = APIEndPoint;
43567                         this.$scope = $scope;
43568                         var controller = this;
43569                         this.APIEndPoint
43570                             .getOptionControlFile()
43571                             .$promise
43572                             .then(function (result) {
43573                             controller.options = result;
43574                         });
43575                         this.files = ['a.file', 'b.file', 'c.file'];
43576                         this.heading = "[" + this.index + "]: dcdFilePring";
43577                         this.isOpen = true;
43578                         this.$scope.$on('close', function () {
43579                             controller.isOpen = false;
43580                         });
43581                     }
43582                     CommandController.prototype.submit = function () {
43583                         var params = {};
43584                         angular.forEach(this.options, function (option) {
43585                             var inputs = [];
43586                             angular.forEach(option.arg, function (arg) {
43587                                 if (arg.input) {
43588                                     inputs.push(arg.input);
43589                                 }
43590                             });
43591                             if (inputs.length > 0) {
43592                                 params[option.option] = inputs;
43593                             }
43594                         });
43595                         console.log(params);
43596                     };
43597                     CommandController.prototype.removeMySelf = function (index) {
43598                         this.remove()(index, this.list);
43599                     };
43600                     CommandController.$inject = ['APIEndPoint', '$scope'];
43601                     return CommandController;
43602                 })();
43603                 directives.CommandController = CommandController;
43604             })(directives = app.directives || (app.directives = {}));
43605         })(app || (app = {}));
43606         var app;
43607         (function (app) {
43608             var directives;
43609             (function (directives) {
43610                 var HeaderMenu = (function () {
43611                     function HeaderMenu() {
43612                         this.restrict = 'E';
43613                         this.replace = true;
43614                         this.templateUrl = 'templates/header-menu.html';
43615                     }
43616                     HeaderMenu.Factory = function () {
43617                         var directive = function () {
43618                             return new HeaderMenu();
43619                         };
43620                         return directive;
43621                     };
43622                     return HeaderMenu;
43623                 })();
43624                 directives.HeaderMenu = HeaderMenu;
43625             })(directives = app.directives || (app.directives = {}));
43626         })(app || (app = {}));
43627         var app;
43628         (function (app) {
43629             var directives;
43630             (function (directives) {
43631                 var Option = (function () {
43632                     function Option() {
43633                         this.restrict = 'E';
43634                         this.replace = true;
43635                         this.controller = 'optionController';
43636                         this.bindToController = {
43637                             info: '=',
43638                             files: '='
43639                         };
43640                         this.scope = true;
43641                         this.templateUrl = 'templates/option.html';
43642                         this.controllerAs = 'ctrl';
43643                     }
43644                     Option.Factory = function () {
43645                         var directive = function () {
43646                             return new Option();
43647                         };
43648                         directive.$inject = [];
43649                         return directive;
43650                     };
43651                     return Option;
43652                 })();
43653                 directives.Option = Option;
43654                 var OptionController = (function () {
43655                     function OptionController() {
43656                         var controller = this;
43657                         angular.forEach(controller.info.arg, function (arg) {
43658                             if (arg.initialValue) {
43659                                 if (arg.formType === 'number') {
43660                                     arg.input = parseInt(arg.initialValue);
43661                                 }
43662                                 else {
43663                                     arg.input = arg.initialValue;
43664                                 }
43665                             }
43666                         });
43667                     }
43668                     OptionController.$inject = [];
43669                     return OptionController;
43670                 })();
43671                 directives.OptionController = OptionController;
43672             })(directives = app.directives || (app.directives = {}));
43673         })(app || (app = {}));
43674         var app;
43675         (function (app) {
43676             var controllers;
43677             (function (controllers) {
43678                 var Execution = (function () {
43679                     function Execution(MyModal, $scope) {
43680                         this.MyModal = MyModal;
43681                         this.$scope = $scope;
43682                         this.commandInfoList = [];
43683                     }
43684                     ;
43685                     Execution.prototype.add = function () {
43686                         this.$scope.$broadcast('close');
43687                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
43688                     };
43689                     Execution.prototype.open = function () {
43690                         var result = this.MyModal.open('SelectCommand');
43691                         console.log(result);
43692                     };
43693                     Execution.prototype.remove = function (index, list) {
43694                         list.splice(index, 1);
43695                     };
43696                     Execution.prototype.close = function () {
43697                         console.log("close");
43698                     };
43699                     Execution.$inject = ['MyModal', '$scope'];
43700                     return Execution;
43701                 })();
43702                 controllers.Execution = Execution;
43703             })(controllers = app.controllers || (app.controllers = {}));
43704         })(app || (app = {}));
43705         var app;
43706         (function (app) {
43707             var controllers;
43708             (function (controllers) {
43709                 var Files = (function () {
43710                     function Files($scope) {
43711                         this.page = "ManageFiles";
43712                     }
43713                     Files.$inject = ['$scope'];
43714                     return Files;
43715                 })();
43716                 controllers.Files = Files;
43717             })(controllers = app.controllers || (app.controllers = {}));
43718         })(app || (app = {}));
43719         var app;
43720         (function (app) {
43721             var controllers;
43722             (function (controllers) {
43723                 var History = (function () {
43724                     function History($scope) {
43725                         this.page = "History";
43726                     }
43727                     History.$inject = ['$scope'];
43728                     return History;
43729                 })();
43730                 controllers.History = History;
43731             })(controllers = app.controllers || (app.controllers = {}));
43732         })(app || (app = {}));
43733         var app;
43734         (function (app) {
43735             'use strict';
43736             var appName = 'zephyr';
43737             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
43738             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
43739                 $urlRouterProvider.otherwise('/execution');
43740                 $locationProvider.html5Mode({
43741                     enabled: true,
43742                     requireBase: false
43743                 });
43744                 $stateProvider
43745                     .state('execution', {
43746                     url: '/execution',
43747                     templateUrl: 'templates/execution.html',
43748                     controller: 'executionController',
43749                     controllerAs: 'c'
43750                 })
43751                     .state('files', {
43752                     url: '/files',
43753                     templateUrl: 'templates/files.html',
43754                     controller: 'filesController',
43755                     controllerAs: 'c'
43756                 })
43757                     .state('history', {
43758                     url: '/history',
43759                     templateUrl: 'templates/history.html',
43760                     controller: 'historyController',
43761                     controllerAs: 'c'
43762                 });
43763             });
43764             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
43765             app.zephyr.service('MyModal', app.services.MyModal);
43766             app.zephyr.controller('executionController', app.controllers.Execution);
43767             app.zephyr.controller('filesController', app.controllers.Files);
43768             app.zephyr.controller('historyController', app.controllers.History);
43769             app.zephyr.controller('commandController', app.directives.CommandController);
43770             app.zephyr.controller('optionController', app.directives.OptionController);
43771             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
43772             app.zephyr.directive('command', app.directives.Command.Factory());
43773             app.zephyr.directive('option', app.directives.Option.Factory());
43774         })(app || (app = {}));
43775
43776
43777 /***/ },
43778 /* 17 */
43779 /***/ function(module, exports) {
43780
43781         var app;
43782         (function (app) {
43783             var declares;
43784             (function (declares) {
43785                 var CommandInfo = (function () {
43786                     function CommandInfo(name) {
43787                         this.name = name;
43788                     }
43789                     return CommandInfo;
43790                 })();
43791                 declares.CommandInfo = CommandInfo;
43792             })(declares = app.declares || (app.declares = {}));
43793         })(app || (app = {}));
43794         var app;
43795         (function (app) {
43796             var services;
43797             (function (services) {
43798                 var APIEndPoint = (function () {
43799                     function APIEndPoint($resource) {
43800                         this.$resource = $resource;
43801                     }
43802                     APIEndPoint.prototype.resource = function () {
43803                         var getOption = {
43804                             method: 'GET',
43805                             isArray: false
43806                         };
43807                         var apiUrl = '/api/optionControlFile/dcdFilePrint';
43808                         return this.$resource(apiUrl, {}, { getOption: getOption });
43809                     };
43810                     APIEndPoint.prototype.getOptionControlFile = function () {
43811                         return this.resource().query();
43812                     };
43813                     return APIEndPoint;
43814                 })();
43815                 services.APIEndPoint = APIEndPoint;
43816             })(services = app.services || (app.services = {}));
43817         })(app || (app = {}));
43818         var app;
43819         (function (app) {
43820             var services;
43821             (function (services) {
43822                 var MyModal = (function () {
43823                     function MyModal($uibModal) {
43824                         this.$uibModal = $uibModal;
43825                         this.modalOption = {
43826                             backdrop: true,
43827                             controller: null,
43828                             templateUrl: null,
43829                             size: null
43830                         };
43831                     }
43832                     MyModal.prototype.open = function (modalName) {
43833                         if (modalName === 'SelectCommand') {
43834                             this.modalOption.templateUrl = 'templates/select-command.html';
43835                             this.modalOption.size = 'lg';
43836                         }
43837                         return this.$uibModal.open(this.modalOption);
43838                     };
43839                     return MyModal;
43840                 })();
43841                 services.MyModal = MyModal;
43842             })(services = app.services || (app.services = {}));
43843         })(app || (app = {}));
43844         var app;
43845         (function (app) {
43846             var directives;
43847             (function (directives) {
43848                 var Command = (function () {
43849                     function Command() {
43850                         this.restrict = 'E';
43851                         this.replace = true;
43852                         this.scope = true;
43853                         this.controller = 'commandController';
43854                         this.controllerAs = 'ctrl';
43855                         this.bindToController = {
43856                             index: '=',
43857                             name: '=',
43858                             remove: '&',
43859                             list: '='
43860                         };
43861                         this.templateUrl = 'templates/command.html';
43862                     }
43863                     Command.Factory = function () {
43864                         var directive = function () {
43865                             return new Command();
43866                         };
43867                         directive.$inject = [];
43868                         return directive;
43869                     };
43870                     return Command;
43871                 })();
43872                 directives.Command = Command;
43873                 var CommandController = (function () {
43874                     function CommandController(APIEndPoint, $scope) {
43875                         this.APIEndPoint = APIEndPoint;
43876                         this.$scope = $scope;
43877                         var controller = this;
43878                         this.APIEndPoint
43879                             .getOptionControlFile()
43880                             .$promise
43881                             .then(function (result) {
43882                             controller.options = result;
43883                         });
43884                         this.files = ['a.file', 'b.file', 'c.file'];
43885                         this.heading = "[" + this.index + "]: dcdFilePring";
43886                         this.isOpen = true;
43887                         this.$scope.$on('close', function () {
43888                             controller.isOpen = false;
43889                         });
43890                     }
43891                     CommandController.prototype.submit = function () {
43892                         var params = {};
43893                         angular.forEach(this.options, function (option) {
43894                             var inputs = [];
43895                             angular.forEach(option.arg, function (arg) {
43896                                 if (arg.input) {
43897                                     inputs.push(arg.input);
43898                                 }
43899                             });
43900                             if (inputs.length > 0) {
43901                                 params[option.option] = inputs;
43902                             }
43903                         });
43904                         console.log(params);
43905                     };
43906                     CommandController.prototype.removeMySelf = function (index) {
43907                         this.remove()(index, this.list);
43908                     };
43909                     CommandController.$inject = ['APIEndPoint', '$scope'];
43910                     return CommandController;
43911                 })();
43912                 directives.CommandController = CommandController;
43913             })(directives = app.directives || (app.directives = {}));
43914         })(app || (app = {}));
43915         var app;
43916         (function (app) {
43917             var directives;
43918             (function (directives) {
43919                 var HeaderMenu = (function () {
43920                     function HeaderMenu() {
43921                         this.restrict = 'E';
43922                         this.replace = true;
43923                         this.templateUrl = 'templates/header-menu.html';
43924                     }
43925                     HeaderMenu.Factory = function () {
43926                         var directive = function () {
43927                             return new HeaderMenu();
43928                         };
43929                         return directive;
43930                     };
43931                     return HeaderMenu;
43932                 })();
43933                 directives.HeaderMenu = HeaderMenu;
43934             })(directives = app.directives || (app.directives = {}));
43935         })(app || (app = {}));
43936         var app;
43937         (function (app) {
43938             var directives;
43939             (function (directives) {
43940                 var Option = (function () {
43941                     function Option() {
43942                         this.restrict = 'E';
43943                         this.replace = true;
43944                         this.controller = 'optionController';
43945                         this.bindToController = {
43946                             info: '=',
43947                             files: '='
43948                         };
43949                         this.scope = true;
43950                         this.templateUrl = 'templates/option.html';
43951                         this.controllerAs = 'ctrl';
43952                     }
43953                     Option.Factory = function () {
43954                         var directive = function () {
43955                             return new Option();
43956                         };
43957                         directive.$inject = [];
43958                         return directive;
43959                     };
43960                     return Option;
43961                 })();
43962                 directives.Option = Option;
43963                 var OptionController = (function () {
43964                     function OptionController() {
43965                         var controller = this;
43966                         angular.forEach(controller.info.arg, function (arg) {
43967                             if (arg.initialValue) {
43968                                 if (arg.formType === 'number') {
43969                                     arg.input = parseInt(arg.initialValue);
43970                                 }
43971                                 else {
43972                                     arg.input = arg.initialValue;
43973                                 }
43974                             }
43975                         });
43976                     }
43977                     OptionController.$inject = [];
43978                     return OptionController;
43979                 })();
43980                 directives.OptionController = OptionController;
43981             })(directives = app.directives || (app.directives = {}));
43982         })(app || (app = {}));
43983         var app;
43984         (function (app) {
43985             var controllers;
43986             (function (controllers) {
43987                 var Execution = (function () {
43988                     function Execution(MyModal, $scope) {
43989                         this.MyModal = MyModal;
43990                         this.$scope = $scope;
43991                         this.commandInfoList = [];
43992                     }
43993                     ;
43994                     Execution.prototype.add = function () {
43995                         this.$scope.$broadcast('close');
43996                         this.commandInfoList.push(new app.declares.CommandInfo('dcdFilePrint'));
43997                     };
43998                     Execution.prototype.open = function () {
43999                         var result = this.MyModal.open('SelectCommand');
44000                         console.log(result);
44001                     };
44002                     Execution.prototype.remove = function (index, list) {
44003                         list.splice(index, 1);
44004                     };
44005                     Execution.prototype.close = function () {
44006                         console.log("close");
44007                     };
44008                     Execution.$inject = ['MyModal', '$scope'];
44009                     return Execution;
44010                 })();
44011                 controllers.Execution = Execution;
44012             })(controllers = app.controllers || (app.controllers = {}));
44013         })(app || (app = {}));
44014         var app;
44015         (function (app) {
44016             var controllers;
44017             (function (controllers) {
44018                 var Files = (function () {
44019                     function Files($scope) {
44020                         this.page = "ManageFiles";
44021                     }
44022                     Files.$inject = ['$scope'];
44023                     return Files;
44024                 })();
44025                 controllers.Files = Files;
44026             })(controllers = app.controllers || (app.controllers = {}));
44027         })(app || (app = {}));
44028         var app;
44029         (function (app) {
44030             var controllers;
44031             (function (controllers) {
44032                 var History = (function () {
44033                     function History($scope) {
44034                         this.page = "History";
44035                     }
44036                     History.$inject = ['$scope'];
44037                     return History;
44038                 })();
44039                 controllers.History = History;
44040             })(controllers = app.controllers || (app.controllers = {}));
44041         })(app || (app = {}));
44042         var app;
44043         (function (app) {
44044             'use strict';
44045             var appName = 'zephyr';
44046             app.zephyr = angular.module(appName, ['ui.router', 'ngResource', 'ui.bootstrap']);
44047             app.zephyr.config(function ($stateProvider, $urlRouterProvider, $locationProvider) {
44048                 $urlRouterProvider.otherwise('/execution');
44049                 $locationProvider.html5Mode({
44050                     enabled: true,
44051                     requireBase: false
44052                 });
44053                 $stateProvider
44054                     .state('execution', {
44055                     url: '/execution',
44056                     templateUrl: 'templates/execution.html',
44057                     controller: 'executionController',
44058                     controllerAs: 'c'
44059                 })
44060                     .state('files', {
44061                     url: '/files',
44062                     templateUrl: 'templates/files.html',
44063                     controller: 'filesController',
44064                     controllerAs: 'c'
44065                 })
44066                     .state('history', {
44067                     url: '/history',
44068                     templateUrl: 'templates/history.html',
44069                     controller: 'historyController',
44070                     controllerAs: 'c'
44071                 });
44072             });
44073             app.zephyr.service('APIEndPoint', app.services.APIEndPoint);
44074             app.zephyr.service('MyModal', app.services.MyModal);
44075             app.zephyr.controller('executionController', app.controllers.Execution);
44076             app.zephyr.controller('filesController', app.controllers.Files);
44077             app.zephyr.controller('historyController', app.controllers.History);
44078             app.zephyr.controller('commandController', app.directives.CommandController);
44079             app.zephyr.controller('optionController', app.directives.OptionController);
44080             app.zephyr.directive('headerMenu', app.directives.HeaderMenu.Factory());
44081             app.zephyr.directive('command', app.directives.Command.Factory());
44082             app.zephyr.directive('option', app.directives.Option.Factory());
44083         })(app || (app = {}));
44084
44085
44086 /***/ }
44087 /******/ ]);